]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* |
2 | * This file is open source software, licensed to you under the terms | |
3 | * of the Apache License, Version 2.0 (the "License"). See the NOTICE file | |
4 | * distributed with this work for additional information regarding copyright | |
5 | * ownership. You may not use this file except in compliance with the License. | |
6 | * | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, | |
12 | * software distributed under the License is distributed on an | |
13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
14 | * KIND, either express or implied. See the License for the | |
15 | * specific language governing permissions and limitations | |
16 | * under the License. | |
17 | */ | |
18 | /* | |
19 | * Copyright (C) 2014-2015 Cloudius Systems, Ltd. | |
20 | */ | |
21 | ||
22 | #include <seastar/testing/test_case.hh> | |
9f95a23c | 23 | #include <seastar/testing/thread_test_case.hh> |
f67539c2 | 24 | #include <seastar/testing/test_runner.hh> |
11fdf7f2 | 25 | |
f67539c2 | 26 | #include <seastar/core/seastar.hh> |
11fdf7f2 TL |
27 | #include <seastar/core/semaphore.hh> |
28 | #include <seastar/core/condition-variable.hh> | |
29 | #include <seastar/core/file.hh> | |
f67539c2 | 30 | #include <seastar/core/layered_file.hh> |
11fdf7f2 TL |
31 | #include <seastar/core/thread.hh> |
32 | #include <seastar/core/stall_sampler.hh> | |
f67539c2 | 33 | #include <seastar/core/aligned_buffer.hh> |
20effc67 | 34 | #include <seastar/core/io_intent.hh> |
f67539c2 TL |
35 | #include <seastar/util/tmp_file.hh> |
36 | #include <seastar/util/alloc_failure_injector.hh> | |
20effc67 | 37 | #include <seastar/util/closeable.hh> |
1e59de90 TL |
38 | #include <seastar/util/internal/magic.hh> |
39 | #include <seastar/util/internal/iovec_utils.hh> | |
f67539c2 | 40 | |
9f95a23c | 41 | #include <boost/range/adaptor/transformed.hpp> |
11fdf7f2 | 42 | #include <iostream> |
f67539c2 | 43 | #include <sys/statfs.h> |
20effc67 | 44 | #include <fcntl.h> |
11fdf7f2 | 45 | |
9f95a23c TL |
46 | #include "core/file-impl.hh" |
47 | ||
11fdf7f2 | 48 | using namespace seastar; |
f67539c2 | 49 | namespace fs = std::filesystem; |
11fdf7f2 TL |
50 | |
51 | SEASTAR_TEST_CASE(open_flags_test) { | |
52 | open_flags flags = open_flags::rw | open_flags::create | open_flags::exclusive; | |
53 | BOOST_REQUIRE(std::underlying_type_t<open_flags>(flags) == | |
54 | (std::underlying_type_t<open_flags>(open_flags::rw) | | |
55 | std::underlying_type_t<open_flags>(open_flags::create) | | |
56 | std::underlying_type_t<open_flags>(open_flags::exclusive))); | |
57 | ||
58 | open_flags mask = open_flags::create | open_flags::exclusive; | |
59 | BOOST_REQUIRE((flags & mask) == mask); | |
60 | return make_ready_future<>(); | |
61 | } | |
62 | ||
9f95a23c TL |
63 | SEASTAR_TEST_CASE(access_flags_test) { |
64 | access_flags flags = access_flags::read | access_flags::write | access_flags::execute; | |
65 | BOOST_REQUIRE(std::underlying_type_t<open_flags>(flags) == | |
66 | (std::underlying_type_t<open_flags>(access_flags::read) | | |
67 | std::underlying_type_t<open_flags>(access_flags::write) | | |
68 | std::underlying_type_t<open_flags>(access_flags::execute))); | |
69 | return make_ready_future<>(); | |
70 | } | |
71 | ||
72 | SEASTAR_TEST_CASE(file_exists_test) { | |
f67539c2 TL |
73 | return tmp_dir::do_with_thread([] (tmp_dir& t) { |
74 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
9f95a23c TL |
75 | auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0(); |
76 | f.close().get(); | |
77 | auto exists = file_exists(filename).get0(); | |
78 | BOOST_REQUIRE(exists); | |
79 | remove_file(filename).get(); | |
80 | exists = file_exists(filename).get0(); | |
81 | BOOST_REQUIRE(!exists); | |
82 | }); | |
83 | } | |
84 | ||
f67539c2 TL |
85 | SEASTAR_TEST_CASE(handle_bad_alloc_test) { |
86 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
87 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
88 | auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0(); | |
89 | f.close().get(); | |
90 | bool exists = false; | |
91 | memory::with_allocation_failures([&] { | |
92 | exists = file_exists(filename).get0(); | |
93 | }); | |
94 | BOOST_REQUIRE(exists); | |
95 | }); | |
96 | } | |
97 | ||
9f95a23c | 98 | SEASTAR_TEST_CASE(file_access_test) { |
f67539c2 TL |
99 | return tmp_dir::do_with_thread([] (tmp_dir& t) { |
100 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
9f95a23c TL |
101 | auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0(); |
102 | f.close().get(); | |
103 | auto is_accessible = file_accessible(filename, access_flags::read | access_flags::write).get0(); | |
104 | BOOST_REQUIRE(is_accessible); | |
9f95a23c TL |
105 | }); |
106 | } | |
107 | ||
11fdf7f2 TL |
108 | struct file_test { |
109 | file_test(file&& f) : f(std::move(f)) {} | |
110 | file f; | |
111 | semaphore sem = { 0 }; | |
112 | semaphore par = { 1000 }; | |
113 | }; | |
114 | ||
115 | SEASTAR_TEST_CASE(test1) { | |
116 | // Note: this tests generates a file "testfile.tmp" with size 4096 * max (= 40 MB). | |
f67539c2 | 117 | return tmp_dir::do_with([] (tmp_dir& t) { |
11fdf7f2 | 118 | static constexpr auto max = 10000; |
f67539c2 TL |
119 | sstring filename = (t.get_path() / "testfile.tmp").native(); |
120 | return open_file_dma(filename, open_flags::rw | open_flags::create).then([filename] (file f) { | |
11fdf7f2 TL |
121 | auto ft = new file_test{std::move(f)}; |
122 | for (size_t i = 0; i < max; ++i) { | |
9f95a23c TL |
123 | // Don't wait for future, use semaphore to signal when done instead. |
124 | (void)ft->par.wait().then([ft, i] { | |
11fdf7f2 TL |
125 | auto wbuf = allocate_aligned_buffer<unsigned char>(4096, 4096); |
126 | std::fill(wbuf.get(), wbuf.get() + 4096, i); | |
127 | auto wb = wbuf.get(); | |
9f95a23c | 128 | (void)ft->f.dma_write(i * 4096, wb, 4096).then( |
11fdf7f2 TL |
129 | [ft, i, wbuf = std::move(wbuf)] (size_t ret) mutable { |
130 | BOOST_REQUIRE(ret == 4096); | |
131 | auto rbuf = allocate_aligned_buffer<unsigned char>(4096, 4096); | |
132 | auto rb = rbuf.get(); | |
9f95a23c | 133 | (void)ft->f.dma_read(i * 4096, rb, 4096).then( |
11fdf7f2 TL |
134 | [ft, rbuf = std::move(rbuf), wbuf = std::move(wbuf)] (size_t ret) mutable { |
135 | BOOST_REQUIRE(ret == 4096); | |
136 | BOOST_REQUIRE(std::equal(rbuf.get(), rbuf.get() + 4096, wbuf.get())); | |
137 | ft->sem.signal(1); | |
138 | ft->par.signal(); | |
139 | }); | |
140 | }); | |
141 | }); | |
142 | } | |
143 | return ft->sem.wait(max).then([ft] () mutable { | |
144 | return ft->f.flush(); | |
145 | }).then([ft] { | |
146 | return ft->f.close(); | |
147 | }).then([ft] () mutable { | |
11fdf7f2 TL |
148 | delete ft; |
149 | }); | |
150 | }); | |
f67539c2 | 151 | }); |
11fdf7f2 TL |
152 | } |
153 | ||
154 | SEASTAR_TEST_CASE(parallel_write_fsync) { | |
155 | return internal::report_reactor_stalls([] { | |
f67539c2 | 156 | return tmp_dir::do_with_thread([] (tmp_dir& t) { |
11fdf7f2 | 157 | // Plan: open a file and write to it like crazy. In parallel fsync() it all the time. |
f67539c2 | 158 | auto fname = (t.get_path() / "testfile.tmp").native(); |
11fdf7f2 TL |
159 | auto sz = uint64_t(32*1024*1024); |
160 | auto buffer_size = 32768; | |
161 | auto write_concurrency = 16; | |
162 | auto fsync_every = 1024*1024; | |
163 | auto max_write_ahead_of_fsync = 4*1024*1024; // ensures writes don't complete too quickly | |
164 | auto written = uint64_t(0); | |
165 | auto fsynced_at = uint64_t(0); | |
166 | ||
167 | file f = open_file_dma(fname, open_flags::rw | open_flags::create | open_flags::truncate).get0(); | |
20effc67 | 168 | auto close_f = deferred_close(f); |
11fdf7f2 TL |
169 | // Avoid filesystem problems with size-extending operations |
170 | f.truncate(sz).get(); | |
171 | ||
172 | auto fsync_semaphore = semaphore(0); | |
173 | auto may_write_condvar = condition_variable(); | |
174 | auto fsync_thread = thread([&] { | |
175 | auto fsynced = uint64_t(0); | |
176 | while (fsynced < sz) { | |
177 | fsync_semaphore.wait(fsync_every).get(); | |
178 | fsynced_at = written; | |
179 | // Signal the condition variable now so that writes proceed | |
180 | // in parallel with the fsync | |
181 | may_write_condvar.broadcast(); | |
182 | f.flush().get(); | |
183 | fsynced += fsync_every; | |
184 | } | |
185 | }); | |
186 | ||
187 | auto write_semaphore = semaphore(write_concurrency); | |
188 | while (written < sz) { | |
189 | write_semaphore.wait().get(); | |
190 | may_write_condvar.wait([&] { | |
191 | return written <= fsynced_at + max_write_ahead_of_fsync; | |
192 | }).get(); | |
193 | auto buf = temporary_buffer<char>::aligned(f.memory_dma_alignment(), buffer_size); | |
f67539c2 | 194 | memset(buf.get_write(), 0, buf.size()); |
9f95a23c TL |
195 | // Write asynchronously, signal when done. |
196 | (void)f.dma_write(written, buf.get(), buf.size()).then([&fsync_semaphore, &write_semaphore, buf = std::move(buf)] (size_t w) { | |
11fdf7f2 TL |
197 | fsync_semaphore.signal(buf.size()); |
198 | write_semaphore.signal(); | |
199 | }); | |
200 | written += buffer_size; | |
201 | } | |
202 | write_semaphore.wait(write_concurrency).get(); | |
203 | ||
204 | fsync_thread.join().get(); | |
20effc67 | 205 | close_f.close_now(); |
11fdf7f2 TL |
206 | remove_file(fname).get(); |
207 | }); | |
208 | }).then([] (internal::stall_report sr) { | |
209 | std::cout << "parallel_write_fsync: " << sr << "\n"; | |
210 | }); | |
211 | } | |
9f95a23c | 212 | |
f67539c2 TL |
213 | SEASTAR_TEST_CASE(test_iov_max) { |
214 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
9f95a23c TL |
215 | static constexpr size_t buffer_size = 4096; |
216 | static constexpr size_t buffer_count = IOV_MAX * 2 + 1; | |
217 | ||
218 | std::vector<temporary_buffer<char>> original_buffers; | |
219 | std::vector<iovec> iovecs; | |
220 | for (auto i = 0u; i < buffer_count; i++) { | |
221 | original_buffers.emplace_back(temporary_buffer<char>::aligned(buffer_size, buffer_size)); | |
222 | std::fill_n(original_buffers.back().get_write(), buffer_size, char(i)); | |
223 | iovecs.emplace_back(iovec { original_buffers.back().get_write(), buffer_size }); | |
224 | } | |
225 | ||
f67539c2 TL |
226 | auto filename = (t.get_path() / "testfile.tmp").native(); |
227 | auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0(); | |
20effc67 | 228 | auto close_f = deferred_close(f); |
9f95a23c TL |
229 | size_t left = buffer_size * buffer_count; |
230 | size_t position = 0; | |
231 | while (left) { | |
232 | auto written = f.dma_write(position, iovecs).get0(); | |
233 | iovecs.erase(iovecs.begin(), iovecs.begin() + written / buffer_size); | |
234 | assert(written % buffer_size == 0); | |
235 | position += written; | |
236 | left -= written; | |
237 | } | |
238 | ||
239 | BOOST_CHECK(iovecs.empty()); | |
240 | ||
241 | std::vector<temporary_buffer<char>> read_buffers; | |
242 | for (auto i = 0u; i < buffer_count; i++) { | |
243 | read_buffers.emplace_back(temporary_buffer<char>::aligned(buffer_size, buffer_size)); | |
244 | std::fill_n(read_buffers.back().get_write(), buffer_size, char(0)); | |
245 | iovecs.emplace_back(iovec { read_buffers.back().get_write(), buffer_size }); | |
246 | } | |
247 | ||
248 | left = buffer_size * buffer_count; | |
249 | position = 0; | |
250 | while (left) { | |
251 | auto read = f.dma_read(position, iovecs).get0(); | |
252 | iovecs.erase(iovecs.begin(), iovecs.begin() + read / buffer_size); | |
253 | assert(read % buffer_size == 0); | |
254 | position += read; | |
255 | left -= read; | |
256 | } | |
257 | ||
258 | for (auto i = 0u; i < buffer_count; i++) { | |
259 | BOOST_CHECK(std::equal(original_buffers[i].get(), original_buffers[i].get() + original_buffers[i].size(), | |
260 | read_buffers[i].get(), read_buffers[i].get() + read_buffers[i].size())); | |
261 | } | |
f67539c2 | 262 | }); |
9f95a23c TL |
263 | } |
264 | ||
265 | SEASTAR_THREAD_TEST_CASE(test_sanitize_iovecs) { | |
266 | auto buf = temporary_buffer<char>::aligned(4096, 4096); | |
267 | ||
268 | auto iovec_equal = [] (const iovec& a, const iovec& b) { | |
269 | return a.iov_base == b.iov_base && a.iov_len == b.iov_len; | |
270 | }; | |
271 | ||
272 | { // Single fragment, sanitize is noop | |
273 | auto original_iovecs = std::vector<iovec> { { buf.get_write(), buf.size() } }; | |
274 | auto actual_iovecs = original_iovecs; | |
275 | auto actual_length = internal::sanitize_iovecs(actual_iovecs, 4096); | |
276 | BOOST_CHECK_EQUAL(actual_length, 4096); | |
277 | BOOST_CHECK_EQUAL(actual_iovecs.size(), 1); | |
278 | BOOST_CHECK(iovec_equal(original_iovecs.back(), actual_iovecs.back())); | |
279 | } | |
280 | ||
281 | { // one 1024 buffer and IOV_MAX+6 buffers of 512; 4096 byte disk alignment, sanitize needs to drop buffers | |
282 | auto original_iovecs = std::vector<iovec>{}; | |
283 | for (auto i = 0u; i < IOV_MAX + 7; i++) { | |
284 | original_iovecs.emplace_back(iovec { buf.get_write(), i == 0 ? 1024u : 512u }); | |
285 | } | |
286 | auto actual_iovecs = original_iovecs; | |
287 | auto actual_length = internal::sanitize_iovecs(actual_iovecs, 4096); | |
288 | BOOST_CHECK_EQUAL(actual_length, 512 * IOV_MAX); | |
289 | BOOST_CHECK_EQUAL(actual_iovecs.size(), IOV_MAX - 1); | |
290 | ||
291 | original_iovecs.resize(IOV_MAX - 1); | |
292 | BOOST_CHECK(std::equal(original_iovecs.begin(), original_iovecs.end(), | |
293 | actual_iovecs.begin(), actual_iovecs.end(), iovec_equal)); | |
294 | } | |
295 | ||
296 | { // IOV_MAX-1 buffers of 512, one 1024 buffer, and 6 512 buffers; 4096 byte disk alignment, sanitize needs to drop and trim buffers | |
297 | auto original_iovecs = std::vector<iovec>{}; | |
298 | for (auto i = 0u; i < IOV_MAX + 7; i++) { | |
299 | original_iovecs.emplace_back(iovec { buf.get_write(), i == (IOV_MAX - 1) ? 1024u : 512u }); | |
300 | } | |
301 | auto actual_iovecs = original_iovecs; | |
302 | auto actual_length = internal::sanitize_iovecs(actual_iovecs, 4096); | |
303 | BOOST_CHECK_EQUAL(actual_length, 512 * IOV_MAX); | |
304 | BOOST_CHECK_EQUAL(actual_iovecs.size(), IOV_MAX); | |
305 | ||
306 | original_iovecs.resize(IOV_MAX); | |
307 | original_iovecs.back().iov_len = 512; | |
308 | BOOST_CHECK(std::equal(original_iovecs.begin(), original_iovecs.end(), | |
309 | actual_iovecs.begin(), actual_iovecs.end(), iovec_equal)); | |
310 | } | |
311 | ||
312 | { // IOV_MAX+8 buffers of 512; 4096 byte disk alignment, sanitize needs to drop buffers | |
313 | auto original_iovecs = std::vector<iovec>{}; | |
314 | for (auto i = 0u; i < IOV_MAX + 8; i++) { | |
315 | original_iovecs.emplace_back(iovec { buf.get_write(), 512 }); | |
316 | } | |
317 | auto actual_iovecs = original_iovecs; | |
318 | auto actual_length = internal::sanitize_iovecs(actual_iovecs, 4096); | |
319 | BOOST_CHECK_EQUAL(actual_length, 512 * IOV_MAX); | |
320 | BOOST_CHECK_EQUAL(actual_iovecs.size(), IOV_MAX); | |
321 | ||
322 | original_iovecs.resize(IOV_MAX); | |
323 | BOOST_CHECK(std::equal(original_iovecs.begin(), original_iovecs.end(), | |
324 | actual_iovecs.begin(), actual_iovecs.end(), iovec_equal)); | |
325 | } | |
326 | } | |
327 | ||
f67539c2 TL |
328 | SEASTAR_TEST_CASE(test_chmod) { |
329 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
9f95a23c | 330 | auto oflags = open_flags::rw | open_flags::create; |
f67539c2 | 331 | sstring filename = (t.get_path() / "testfile.tmp").native(); |
9f95a23c TL |
332 | if (file_exists(filename).get0()) { |
333 | remove_file(filename).get(); | |
334 | } | |
335 | ||
336 | auto orig_umask = umask(0); | |
337 | ||
338 | // test default_file_permissions | |
339 | auto f = open_file_dma(filename, oflags).get0(); | |
340 | f.close().get(); | |
341 | auto sd = file_stat(filename).get0(); | |
342 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_file_permissions)); | |
343 | ||
344 | // test chmod with new_permissions | |
345 | auto new_permissions = file_permissions::user_read | file_permissions::group_read | file_permissions::others_read; | |
346 | BOOST_REQUIRE(new_permissions != file_permissions::default_file_permissions); | |
347 | BOOST_REQUIRE(file_exists(filename).get0()); | |
348 | chmod(filename, new_permissions).get(); | |
349 | sd = file_stat(filename).get0(); | |
350 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(new_permissions)); | |
351 | remove_file(filename).get(); | |
352 | ||
353 | umask(orig_umask); | |
f67539c2 | 354 | }); |
9f95a23c TL |
355 | } |
356 | ||
f67539c2 TL |
357 | SEASTAR_TEST_CASE(test_open_file_dma_permissions) { |
358 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
9f95a23c | 359 | auto oflags = open_flags::rw | open_flags::create; |
f67539c2 | 360 | sstring filename = (t.get_path() / "testfile.tmp").native(); |
9f95a23c TL |
361 | if (file_exists(filename).get0()) { |
362 | remove_file(filename).get(); | |
363 | } | |
364 | ||
365 | auto orig_umask = umask(0); | |
366 | ||
367 | // test default_file_permissions | |
368 | auto f = open_file_dma(filename, oflags).get0(); | |
369 | f.close().get(); | |
370 | auto sd = file_stat(filename).get0(); | |
371 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_file_permissions)); | |
372 | remove_file(filename).get(); | |
373 | ||
374 | // test options.create_permissions | |
375 | auto options = file_open_options(); | |
376 | options.create_permissions = file_permissions::user_read | file_permissions::group_read | file_permissions::others_read; | |
377 | BOOST_REQUIRE(options.create_permissions != file_permissions::default_file_permissions); | |
378 | f = open_file_dma(filename, oflags, options).get0(); | |
379 | f.close().get(); | |
380 | sd = file_stat(filename).get0(); | |
381 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(options.create_permissions)); | |
382 | remove_file(filename).get(); | |
383 | ||
384 | umask(orig_umask); | |
f67539c2 | 385 | }); |
9f95a23c TL |
386 | } |
387 | ||
f67539c2 TL |
388 | SEASTAR_TEST_CASE(test_make_directory_permissions) { |
389 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
390 | sstring dirname = (t.get_path() / "testdir.tmp").native(); | |
9f95a23c TL |
391 | auto orig_umask = umask(0); |
392 | ||
393 | // test default_dir_permissions with make_directory | |
394 | make_directory(dirname).get(); | |
395 | auto sd = file_stat(dirname).get0(); | |
396 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions)); | |
397 | remove_file(dirname).get(); | |
398 | ||
399 | // test make_directory | |
400 | auto create_permissions = file_permissions::user_read | file_permissions::group_read | file_permissions::others_read; | |
401 | BOOST_REQUIRE(create_permissions != file_permissions::default_dir_permissions); | |
402 | make_directory(dirname, create_permissions).get(); | |
403 | sd = file_stat(dirname).get0(); | |
404 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(create_permissions)); | |
405 | remove_file(dirname).get(); | |
406 | ||
407 | umask(orig_umask); | |
f67539c2 | 408 | }); |
9f95a23c TL |
409 | } |
410 | ||
f67539c2 TL |
411 | SEASTAR_TEST_CASE(test_touch_directory_permissions) { |
412 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
413 | sstring dirname = (t.get_path() / "testdir.tmp").native(); | |
9f95a23c TL |
414 | auto orig_umask = umask(0); |
415 | ||
416 | // test default_dir_permissions with touch_directory | |
417 | touch_directory(dirname).get(); | |
418 | auto sd = file_stat(dirname).get0(); | |
419 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions)); | |
420 | remove_file(dirname).get(); | |
421 | ||
422 | // test touch_directory, dir creation | |
423 | auto create_permissions = file_permissions::user_read | file_permissions::group_read | file_permissions::others_read; | |
424 | BOOST_REQUIRE(create_permissions != file_permissions::default_dir_permissions); | |
425 | BOOST_REQUIRE(!file_exists(dirname).get0()); | |
426 | touch_directory(dirname, create_permissions).get(); | |
427 | sd = file_stat(dirname).get0(); | |
428 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(create_permissions)); | |
429 | ||
430 | // test touch_directory of existing dir, dir mode need not change | |
431 | BOOST_REQUIRE(file_exists(dirname).get0()); | |
432 | touch_directory(dirname, file_permissions::default_dir_permissions).get(); | |
433 | sd = file_stat(dirname).get0(); | |
434 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(create_permissions)); | |
435 | remove_file(dirname).get(); | |
436 | ||
437 | umask(orig_umask); | |
f67539c2 | 438 | }); |
9f95a23c TL |
439 | } |
440 | ||
f67539c2 TL |
441 | SEASTAR_TEST_CASE(test_recursive_touch_directory_permissions) { |
442 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
443 | sstring base_dirname = (t.get_path() / "testbasedir.tmp").native(); | |
444 | sstring dirpath = base_dirname + "/" + "testsubdir.tmp"; | |
9f95a23c TL |
445 | if (file_exists(dirpath).get0()) { |
446 | remove_file(dirpath).get(); | |
447 | } | |
448 | if (file_exists(base_dirname).get0()) { | |
449 | remove_file(base_dirname).get(); | |
450 | } | |
451 | ||
452 | auto orig_umask = umask(0); | |
453 | ||
454 | // test default_dir_permissions with recursive_touch_directory | |
455 | recursive_touch_directory(dirpath).get(); | |
456 | auto sd = file_stat(base_dirname).get0(); | |
457 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions)); | |
458 | sd = file_stat(dirpath).get0(); | |
459 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions)); | |
460 | remove_file(dirpath).get(); | |
461 | ||
462 | // test recursive_touch_directory, dir creation | |
463 | auto create_permissions = file_permissions::user_read | file_permissions::group_read | file_permissions::others_read; | |
464 | BOOST_REQUIRE(create_permissions != file_permissions::default_dir_permissions); | |
465 | BOOST_REQUIRE(file_exists(base_dirname).get0()); | |
466 | BOOST_REQUIRE(!file_exists(dirpath).get0()); | |
467 | recursive_touch_directory(dirpath, create_permissions).get(); | |
468 | sd = file_stat(base_dirname).get0(); | |
469 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions)); | |
470 | sd = file_stat(dirpath).get0(); | |
471 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(create_permissions)); | |
472 | ||
473 | // test recursive_touch_directory of existing dir, dir mode need not change | |
474 | BOOST_REQUIRE(file_exists(dirpath).get0()); | |
475 | recursive_touch_directory(dirpath, file_permissions::default_dir_permissions).get(); | |
476 | sd = file_stat(base_dirname).get0(); | |
477 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions)); | |
478 | sd = file_stat(dirpath).get0(); | |
479 | BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(create_permissions)); | |
480 | remove_file(dirpath).get(); | |
481 | remove_file(base_dirname).get(); | |
482 | ||
483 | umask(orig_umask); | |
f67539c2 TL |
484 | }); |
485 | } | |
486 | ||
487 | SEASTAR_TEST_CASE(test_file_stat_method) { | |
488 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
489 | auto oflags = open_flags::rw | open_flags::create; | |
490 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
491 | ||
492 | auto orig_umask = umask(0); | |
493 | ||
494 | auto f = open_file_dma(filename, oflags).get0(); | |
20effc67 | 495 | auto close_f = deferred_close(f); |
f67539c2 | 496 | auto st = f.stat().get0(); |
f67539c2 TL |
497 | BOOST_CHECK_EQUAL(st.st_mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_file_permissions)); |
498 | ||
499 | umask(orig_umask); | |
500 | }); | |
501 | } | |
502 | ||
20effc67 TL |
503 | SEASTAR_TEST_CASE(test_file_write_lifetime_method) { |
504 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
505 | auto oflags = open_flags::rw | open_flags::create; | |
506 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
507 | ||
508 | auto f1 = open_file_dma(filename, oflags).get0(); | |
509 | auto close_f1 = deferred_close(f1); | |
510 | auto f2 = open_file_dma(filename, oflags).get0(); | |
511 | auto close_f2 = deferred_close(f2); | |
512 | ||
513 | // Write life time hint values | |
514 | std::vector<uint64_t> hint_set = {RWF_WRITE_LIFE_NOT_SET, | |
515 | RWH_WRITE_LIFE_NONE, | |
516 | RWH_WRITE_LIFE_SHORT, | |
517 | RWH_WRITE_LIFE_MEDIUM, | |
518 | RWH_WRITE_LIFE_LONG, | |
519 | RWH_WRITE_LIFE_EXTREME}; | |
520 | ||
521 | for (auto i = 0ul; i < hint_set.size(); ++i) { | |
522 | auto hint = hint_set[i]; | |
523 | ||
524 | // Set and verify the lifetime hint of the inode | |
525 | f1.set_inode_lifetime_hint(hint).get(); | |
526 | auto o_hint1 = f1.get_inode_lifetime_hint().get0(); | |
527 | BOOST_CHECK_EQUAL(hint, o_hint1); | |
20effc67 TL |
528 | } |
529 | ||
530 | // Perform invalid ops | |
531 | uint64_t hint = RWH_WRITE_LIFE_EXTREME + 1; | |
532 | BOOST_REQUIRE_THROW(f1.set_inode_lifetime_hint(hint).get(), std::system_error); | |
20effc67 TL |
533 | }); |
534 | } | |
535 | ||
536 | SEASTAR_TEST_CASE(test_file_fcntl) { | |
537 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
538 | auto oflags = open_flags::rw | open_flags::create; | |
539 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
540 | ||
541 | auto f = open_file_dma(filename, oflags).get0(); | |
542 | auto close_f = deferred_close(f); | |
543 | ||
544 | // Set and verify a lease value | |
545 | auto lease = F_WRLCK; | |
546 | BOOST_REQUIRE(!f.fcntl(F_SETLEASE, lease).get0()); | |
547 | auto o_lease = f.fcntl(F_GETLEASE).get0(); | |
548 | BOOST_CHECK_EQUAL(lease, o_lease); | |
549 | ||
550 | // Use _short version and test the same | |
551 | o_lease = f.fcntl_short(F_GETLEASE).get0(); | |
552 | BOOST_CHECK_EQUAL(lease, o_lease); | |
553 | ||
554 | // Perform invalid ops | |
555 | BOOST_REQUIRE_THROW(f.fcntl(F_SETLEASE, (uintptr_t)~0ul).get(), std::system_error); | |
556 | BOOST_REQUIRE_THROW(f.fcntl_short(F_SETLEASE, (uintptr_t)~0ul).get(), std::system_error); | |
20effc67 TL |
557 | }); |
558 | } | |
559 | ||
560 | SEASTAR_TEST_CASE(test_file_ioctl) { | |
561 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
562 | auto oflags = open_flags::rw | open_flags::create; | |
563 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
564 | uint64_t block_size = 0; | |
565 | ||
566 | auto f = open_file_dma(filename, oflags).get0(); | |
567 | auto close_f = deferred_close(f); | |
568 | ||
569 | // Issueing an FS ioctl which is applicable on regular files | |
570 | // and can be executed as normal user | |
571 | try { | |
572 | BOOST_REQUIRE(!f.ioctl(FIGETBSZ, &block_size).get0()); | |
573 | BOOST_REQUIRE(block_size != 0); | |
574 | ||
575 | // Use _short version and test the same | |
576 | BOOST_REQUIRE(!f.ioctl_short(FIGETBSZ, &block_size).get0()); | |
577 | BOOST_REQUIRE(block_size != 0); | |
578 | } catch (std::system_error& e) { | |
579 | // anon_bdev filesystems do not support FIGETBSZ, and return EINVAL | |
580 | BOOST_REQUIRE_EQUAL(e.code().value(), EINVAL); | |
581 | } | |
582 | ||
583 | // Perform invalid ops | |
584 | BOOST_REQUIRE_THROW(f.ioctl(FIGETBSZ, 0ul).get(), std::system_error); | |
585 | BOOST_REQUIRE_THROW(f.ioctl_short(FIGETBSZ, 0ul).get(), std::system_error); | |
586 | }); | |
587 | } | |
f67539c2 TL |
588 | |
589 | class test_layered_file : public layered_file_impl { | |
590 | public: | |
591 | explicit test_layered_file(file f) : layered_file_impl(std::move(f)) {} | |
592 | virtual future<size_t> write_dma(uint64_t pos, const void* buffer, size_t len, const io_priority_class& pc) override { | |
593 | abort(); | |
594 | } | |
595 | virtual future<size_t> write_dma(uint64_t pos, std::vector<iovec> iov, const io_priority_class& pc) override { | |
596 | abort(); | |
597 | } | |
598 | virtual future<size_t> read_dma(uint64_t pos, void* buffer, size_t len, const io_priority_class& pc) override { | |
599 | abort(); | |
600 | } | |
601 | virtual future<size_t> read_dma(uint64_t pos, std::vector<iovec> iov, const io_priority_class& pc) override { | |
602 | abort(); | |
603 | } | |
604 | virtual future<> flush(void) override { | |
605 | abort(); | |
606 | } | |
607 | virtual future<struct stat> stat(void) override { | |
608 | abort(); | |
609 | } | |
610 | virtual future<> truncate(uint64_t length) override { | |
611 | abort(); | |
612 | } | |
613 | virtual future<> discard(uint64_t offset, uint64_t length) override { | |
614 | abort(); | |
615 | } | |
616 | virtual future<> allocate(uint64_t position, uint64_t length) override { | |
617 | abort(); | |
618 | } | |
619 | virtual future<uint64_t> size(void) override { | |
620 | abort(); | |
621 | } | |
622 | virtual future<> close() override { | |
623 | abort(); | |
624 | } | |
625 | virtual std::unique_ptr<file_handle_impl> dup() override { | |
626 | abort(); | |
627 | } | |
628 | virtual subscription<directory_entry> list_directory(std::function<future<> (directory_entry de)> next) override { | |
629 | abort(); | |
630 | } | |
631 | virtual future<temporary_buffer<uint8_t>> dma_read_bulk(uint64_t offset, size_t range_size, const io_priority_class& pc) override { | |
632 | abort(); | |
633 | } | |
634 | }; | |
635 | ||
636 | SEASTAR_TEST_CASE(test_underlying_file) { | |
637 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
638 | auto oflags = open_flags::rw | open_flags::create; | |
639 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
640 | auto f = open_file_dma(filename, oflags).get0(); | |
20effc67 | 641 | auto close_f = deferred_close(f); |
f67539c2 TL |
642 | auto lf = file(make_shared<test_layered_file>(f)); |
643 | BOOST_CHECK_EQUAL(f.memory_dma_alignment(), lf.memory_dma_alignment()); | |
644 | BOOST_CHECK_EQUAL(f.disk_read_dma_alignment(), lf.disk_read_dma_alignment()); | |
645 | BOOST_CHECK_EQUAL(f.disk_write_dma_alignment(), lf.disk_write_dma_alignment()); | |
f67539c2 TL |
646 | }); |
647 | } | |
648 | ||
649 | SEASTAR_TEST_CASE(test_file_stat_method_with_file) { | |
650 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
651 | auto oflags = open_flags::rw | open_flags::create | open_flags::truncate; | |
652 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
653 | file ref; | |
654 | ||
655 | auto orig_umask = umask(0); | |
656 | ||
657 | auto st = with_file(open_file_dma(filename, oflags), [&ref] (file& f) { | |
658 | // make a copy of f to verify f is auto-closed when `with_file` returns. | |
659 | ref = f; | |
660 | return f.stat(); | |
661 | }).get0(); | |
662 | BOOST_CHECK_EQUAL(st.st_mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_file_permissions)); | |
663 | ||
664 | // verify that the file was auto-closed | |
665 | BOOST_REQUIRE_THROW(ref.stat().get(), std::system_error); | |
666 | ||
667 | umask(orig_umask); | |
668 | }); | |
669 | } | |
670 | ||
671 | SEASTAR_TEST_CASE(test_open_error_with_file) { | |
672 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
673 | auto open_file = [&t] (bool do_open) { | |
674 | auto oflags = open_flags::ro; | |
675 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
676 | if (do_open) { | |
677 | return open_file_dma(filename, oflags); | |
678 | } else { | |
679 | throw std::runtime_error("expected exception"); | |
680 | } | |
681 | }; | |
682 | bool got_exception = false; | |
683 | ||
684 | BOOST_REQUIRE_NO_THROW(with_file(open_file(true), [] (file& f) { | |
685 | BOOST_REQUIRE(false); | |
686 | }).handle_exception_type([&got_exception] (const std::system_error& e) { | |
687 | got_exception = true; | |
688 | BOOST_REQUIRE(e.code().value() == ENOENT); | |
689 | }).get()); | |
690 | BOOST_REQUIRE(got_exception); | |
691 | ||
692 | got_exception = false; | |
693 | BOOST_REQUIRE_THROW(with_file(open_file(false), [] (file& f) { | |
694 | BOOST_REQUIRE(false); | |
695 | }).handle_exception_type([&got_exception] (const std::runtime_error& e) { | |
696 | got_exception = true; | |
697 | }).get(), std::runtime_error); | |
698 | BOOST_REQUIRE(!got_exception); | |
699 | }); | |
700 | } | |
701 | ||
702 | SEASTAR_TEST_CASE(test_with_file_close_on_failure) { | |
703 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
704 | auto oflags = open_flags::rw | open_flags::create | open_flags::truncate; | |
705 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
706 | ||
707 | auto orig_umask = umask(0); | |
708 | ||
709 | // error-free case | |
710 | auto ref = with_file_close_on_failure(open_file_dma(filename, oflags), [] (file& f) { | |
711 | return f; | |
712 | }).get0(); | |
713 | auto st = ref.stat().get0(); | |
714 | ref.close().get(); | |
715 | BOOST_CHECK_EQUAL(st.st_mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_file_permissions)); | |
716 | ||
717 | // close-on-error case | |
718 | BOOST_REQUIRE_THROW(with_file_close_on_failure(open_file_dma(filename, oflags), [&ref] (file& f) { | |
719 | ref = f; | |
720 | throw std::runtime_error("expected exception"); | |
721 | }).get(), std::runtime_error); | |
722 | ||
723 | // verify that file was auto-closed on error | |
724 | BOOST_REQUIRE_THROW(ref.stat().get(), std::system_error); | |
725 | ||
726 | umask(orig_umask); | |
727 | }); | |
728 | } | |
729 | ||
730 | namespace seastar { | |
731 | extern bool aio_nowait_supported; | |
732 | } | |
733 | ||
734 | SEASTAR_TEST_CASE(test_nowait_flag_correctness) { | |
735 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
736 | auto oflags = open_flags::rw | open_flags::create; | |
737 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
738 | auto is_tmpfs = [&] (sstring filename) { | |
739 | struct ::statfs buf; | |
740 | int fd = ::open(filename.c_str(), static_cast<int>(open_flags::ro)); | |
741 | assert(fd != -1); | |
742 | auto r = ::fstatfs(fd, &buf); | |
743 | if (r == -1) { | |
744 | return false; | |
745 | } | |
1e59de90 | 746 | return buf.f_type == internal::fs_magic::tmpfs; |
f67539c2 TL |
747 | }; |
748 | ||
749 | if (!seastar::aio_nowait_supported) { | |
750 | BOOST_TEST_WARN(0, "Skipping this test because RWF_NOWAIT is not supported by the system"); | |
751 | return; | |
752 | } | |
753 | ||
754 | auto f = open_file_dma(filename, oflags).get0(); | |
20effc67 | 755 | auto close_f = deferred_close(f); |
f67539c2 TL |
756 | |
757 | if (is_tmpfs(filename)) { | |
758 | BOOST_TEST_WARN(0, "Skipping this test because TMPFS was detected, and RWF_NOWAIT is only supported by disk-based FSes"); | |
759 | return; | |
760 | } | |
761 | ||
762 | for (auto i = 0; i < 10; i++) { | |
763 | auto wbuf = allocate_aligned_buffer<unsigned char>(4096, 4096); | |
764 | std::fill(wbuf.get(), wbuf.get() + 4096, i); | |
765 | auto wb = wbuf.get(); | |
766 | f.dma_write(i * 4096, wb, 4096).get(); | |
767 | f.flush().get0(); | |
768 | } | |
769 | }); | |
770 | } | |
771 | ||
772 | SEASTAR_TEST_CASE(test_destruct_just_constructed_append_challenged_file) { | |
773 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
774 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
775 | auto oflags = open_flags::rw | open_flags::create; | |
776 | auto f = open_file_dma(filename, oflags).get0(); | |
777 | }); | |
778 | } | |
779 | ||
20effc67 TL |
780 | SEASTAR_TEST_CASE(test_destruct_just_constructed_append_challenged_file_with_sloppy_size) { |
781 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
782 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
783 | auto oflags = open_flags::rw | open_flags::create; | |
784 | file_open_options opt; | |
785 | opt.sloppy_size = true; | |
786 | auto f = open_file_dma(filename, oflags, opt).get0(); | |
787 | }); | |
788 | } | |
789 | ||
f67539c2 TL |
790 | SEASTAR_TEST_CASE(test_destruct_append_challenged_file_after_write) { |
791 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
792 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
793 | auto buf = allocate_aligned_buffer<unsigned char>(4096, 4096); | |
794 | std::fill(buf.get(), buf.get() + 4096, 0); | |
795 | ||
796 | auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0(); | |
797 | f.dma_write(0, buf.get(), 4096).get(); | |
798 | }); | |
799 | } | |
800 | ||
801 | SEASTAR_TEST_CASE(test_destruct_append_challenged_file_after_read) { | |
802 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
803 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
804 | auto buf = allocate_aligned_buffer<unsigned char>(4096, 4096); | |
805 | std::fill(buf.get(), buf.get() + 4096, 0); | |
806 | ||
807 | auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0(); | |
808 | f.dma_write(0, buf.get(), 4096).get(); | |
809 | f.flush().get0(); | |
810 | f.close().get(); | |
811 | ||
812 | f = open_file_dma(filename, open_flags::rw).get0(); | |
813 | f.dma_read(0, buf.get(), 4096).get(); | |
814 | }); | |
815 | } | |
816 | ||
817 | SEASTAR_TEST_CASE(test_dma_iovec) { | |
818 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
819 | static constexpr size_t alignment = 4096; | |
820 | auto wbuf = allocate_aligned_buffer<char>(alignment, alignment); | |
821 | size_t size = 1234; | |
822 | std::fill_n(wbuf.get(), alignment, char(0)); | |
823 | std::fill_n(wbuf.get(), size, char(42)); | |
824 | std::vector<iovec> iovecs; | |
825 | ||
826 | auto filename = (t.get_path() / "testfile.tmp").native(); | |
827 | auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0(); | |
828 | iovecs.push_back(iovec{ wbuf.get(), alignment }); | |
829 | auto count = f.dma_write(0, iovecs).get0(); | |
830 | BOOST_REQUIRE_EQUAL(count, alignment); | |
831 | f.truncate(size).get(); | |
832 | f.close().get(); | |
833 | ||
834 | auto rbuf = allocate_aligned_buffer<char>(alignment, alignment); | |
835 | ||
836 | // this tests the posix_file_impl | |
837 | f = open_file_dma(filename, open_flags::ro).get0(); | |
838 | std::fill_n(rbuf.get(), alignment, char(0)); | |
839 | iovecs.clear(); | |
840 | iovecs.push_back(iovec{ rbuf.get(), alignment }); | |
841 | count = f.dma_read(0, iovecs).get0(); | |
842 | BOOST_REQUIRE_EQUAL(count, size); | |
843 | ||
844 | BOOST_REQUIRE(std::equal(wbuf.get(), wbuf.get() + alignment, rbuf.get(), rbuf.get() + alignment)); | |
845 | ||
846 | // this tests the append_challenged_posix_file_impl | |
847 | f = open_file_dma(filename, open_flags::rw).get0(); | |
848 | std::fill_n(rbuf.get(), alignment, char(0)); | |
849 | iovecs.clear(); | |
850 | iovecs.push_back(iovec{ rbuf.get(), alignment }); | |
851 | count = f.dma_read(0, iovecs).get0(); | |
852 | BOOST_REQUIRE_EQUAL(count, size); | |
853 | ||
854 | BOOST_REQUIRE(std::equal(wbuf.get(), wbuf.get() + alignment, rbuf.get(), rbuf.get() + alignment)); | |
855 | }); | |
9f95a23c | 856 | } |
20effc67 TL |
857 | |
858 | SEASTAR_TEST_CASE(test_intent) { | |
859 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
860 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
861 | auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0(); | |
862 | auto buf = allocate_aligned_buffer<unsigned char>(1024, 1024); | |
863 | std::fill(buf.get(), buf.get() + 1024, 'a'); | |
864 | f.dma_write(0, buf.get(), 1024).get(); | |
865 | std::fill(buf.get(), buf.get() + 1024, 'b'); | |
866 | io_intent intent; | |
867 | auto f1 = f.dma_write(0, buf.get(), 512); | |
868 | auto f2 = f.dma_write(512, buf.get(), 512, default_priority_class(), &intent); | |
869 | intent.cancel(); | |
870 | ||
871 | bool cancelled = false; | |
872 | f1.get(); | |
873 | try { | |
874 | f2.get(); | |
875 | } catch (cancelled_error& ex) { | |
876 | cancelled = true; | |
877 | } | |
878 | auto rbuf = allocate_aligned_buffer<unsigned char>(1024, 1024); | |
879 | f.dma_read(0, rbuf.get(), 1024).get(); | |
880 | BOOST_REQUIRE(rbuf.get()[0] == 'b'); | |
881 | if (cancelled) { | |
882 | BOOST_REQUIRE(rbuf.get()[512] == 'a'); | |
883 | } else { | |
884 | // The file::dma_write doesn't preemt, but if it | |
885 | // suddenly will, the 2nd write will pass before | |
886 | // the intent would be cancelled | |
887 | BOOST_TEST_WARN(0, "Write won the race with cancellation"); | |
888 | BOOST_REQUIRE(rbuf.get()[512] == 'b'); | |
889 | } | |
890 | }); | |
891 | } | |
892 | ||
893 | SEASTAR_TEST_CASE(parallel_overwrite) { | |
894 | // Avoid /tmp for tmp_dir, since it can be tmpfs | |
895 | return tmp_dir::do_with("XXXXXXXX.tmp", [] (tmp_dir& t) { | |
896 | return async([&] { | |
897 | // Check that overwrites at disk_overwrite_dma_alignment() do not cause stalls. First, | |
898 | // create a file. | |
899 | auto fname = (t.get_path() / "testfile.tmp").native(); | |
900 | auto sz = uint64_t(1*1024*1024); | |
901 | auto buffer_size = 128*1024; | |
902 | ||
903 | file f = open_file_dma(fname, open_flags::rw | open_flags::create | open_flags::truncate).get0(); | |
904 | // Avoid filesystem problems with size-extending operations | |
905 | f.truncate(sz).get(); | |
906 | auto buf = allocate_aligned_buffer<unsigned char>(buffer_size, f.memory_dma_alignment()); | |
907 | for (uint64_t offset = 0; offset < sz; offset += buffer_size) { | |
908 | f.dma_write(offset, buf.get(), buffer_size).get(); | |
909 | } | |
910 | ||
911 | auto random_engine = testing::local_random_engine; | |
912 | auto dist = std::uniform_int_distribution(uint64_t(0), sz-1); | |
913 | auto offsets = std::vector<uint64_t>(); | |
914 | std::generate_n(std::back_insert_iterator(offsets), 5000, [&] { return align_down(dist(random_engine), f.disk_overwrite_dma_alignment()); }); | |
915 | auto stall_report = internal::report_reactor_stalls([&] { | |
916 | return max_concurrent_for_each(offsets, 10, [&] (uint64_t offset) { | |
917 | return f.dma_write(offset, buf.get(), f.disk_overwrite_dma_alignment()).discard_result(); | |
918 | }); | |
919 | }).get0(); | |
920 | std::cout << "parallel_overwrite: " << stall_report << " (overwrite dma alignment " << f.disk_overwrite_dma_alignment() << ")\n"; | |
921 | ||
922 | f.close().get(); | |
923 | remove_file(fname).get(); | |
924 | }); | |
925 | }); | |
926 | } | |
927 | ||
928 | SEASTAR_TEST_CASE(test_oversized_io_works) { | |
929 | return tmp_dir::do_with_thread([] (tmp_dir& t) { | |
930 | sstring filename = (t.get_path() / "testfile.tmp").native(); | |
931 | auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0(); | |
932 | ||
933 | size_t max_write = f.disk_write_max_length(); | |
934 | size_t max_read = f.disk_read_max_length(); | |
935 | size_t buf_size = std::max(max_write, max_read) + 4096; | |
936 | ||
937 | auto buf = allocate_aligned_buffer<unsigned char>(buf_size, 4096); | |
938 | std::fill(buf.get(), buf.get() + buf_size, 'a'); | |
939 | ||
940 | f.dma_write(0, buf.get(), buf_size).get(); | |
941 | f.flush().get0(); | |
942 | f.close().get(); | |
943 | ||
944 | std::fill(buf.get(), buf.get() + buf_size, 'b'); | |
945 | f = open_file_dma(filename, open_flags::rw).get0(); | |
946 | f.dma_read(0, buf.get(), buf_size).get(); | |
947 | ||
948 | BOOST_REQUIRE((size_t)std::count_if(buf.get(), buf.get() + buf_size, [](auto x) { return x == 'a'; }) == buf_size); | |
949 | }); | |
950 | } |