1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
12 #include "global/global_init.h"
13 #include "common/ceph_argparse.h"
14 #include "include/stringify.h"
15 #include "include/scope_guard.h"
16 #include "common/errno.h"
17 #include <gtest/gtest.h>
19 #include "os/bluestore/BlueFS.h"
21 string
get_temp_bdev(uint64_t size
)
24 string fn
= "ceph_test_bluefs.tmp.block." + stringify(getpid())
25 + "." + stringify(++n
);
26 int fd
= ::open(fn
.c_str(), O_CREAT
|O_RDWR
|O_TRUNC
, 0644);
28 int r
= ::ftruncate(fd
, size
);
34 std::unique_ptr
<char[]> gen_buffer(uint64_t size
)
36 std::unique_ptr
<char[]> buffer
= std::make_unique
<char[]>(size
);
37 std::independent_bits_engine
<std::default_random_engine
, CHAR_BIT
, unsigned char> e
;
38 std::generate(buffer
.get(), buffer
.get()+size
, std::ref(e
));
43 void rm_temp_bdev(string f
)
49 uint64_t size
= 1048576 * 128;
50 string fn
= get_temp_bdev(size
);
52 BlueFS
fs(g_ceph_context
);
53 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
54 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
55 ASSERT_EQ(0, fs
.mkfs(fsid
));
59 TEST(BlueFS
, mkfs_mount
) {
60 uint64_t size
= 1048576 * 128;
61 string fn
= get_temp_bdev(size
);
62 BlueFS
fs(g_ceph_context
);
63 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
64 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
66 ASSERT_EQ(0, fs
.mkfs(fsid
));
67 ASSERT_EQ(0, fs
.mount());
68 ASSERT_EQ(fs
.get_total(BlueFS::BDEV_DB
), size
- 1048576);
69 ASSERT_LT(fs
.get_free(BlueFS::BDEV_DB
), size
- 1048576);
74 TEST(BlueFS
, write_read
) {
75 uint64_t size
= 1048576 * 128;
76 string fn
= get_temp_bdev(size
);
77 BlueFS
fs(g_ceph_context
);
78 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
79 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
81 ASSERT_EQ(0, fs
.mkfs(fsid
));
82 ASSERT_EQ(0, fs
.mount());
84 BlueFS::FileWriter
*h
;
85 ASSERT_EQ(0, fs
.mkdir("dir"));
86 ASSERT_EQ(0, fs
.open_for_write("dir", "file", &h
, false));
94 BlueFS::FileReader
*h
;
95 ASSERT_EQ(0, fs
.open_for_read("dir", "file", &h
));
97 BlueFS::FileReaderBuffer
buf(4096);
98 ASSERT_EQ(9, fs
.read(h
, &buf
, 0, 1024, &bl
, NULL
));
99 ASSERT_EQ(0, strncmp("foobarbaz", bl
.c_str(), 9));
106 TEST(BlueFS
, small_appends
) {
107 uint64_t size
= 1048576 * 128;
108 string fn
= get_temp_bdev(size
);
109 BlueFS
fs(g_ceph_context
);
110 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
111 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
113 ASSERT_EQ(0, fs
.mkfs(fsid
));
114 ASSERT_EQ(0, fs
.mount());
116 BlueFS::FileWriter
*h
;
117 ASSERT_EQ(0, fs
.mkdir("dir"));
118 ASSERT_EQ(0, fs
.open_for_write("dir", "file", &h
, false));
119 for (unsigned i
= 0; i
< 10000; ++i
) {
120 h
->append("abcdeabcdeabcdeabcdeabcdeabc", 23);
126 BlueFS::FileWriter
*h
;
127 ASSERT_EQ(0, fs
.open_for_write("dir", "file_sync", &h
, false));
128 for (unsigned i
= 0; i
< 1000; ++i
) {
129 h
->append("abcdeabcdeabcdeabcdeabcdeabc", 23);
130 ASSERT_EQ(0, fs
.fsync(h
));
138 TEST(BlueFS
, very_large_write
) {
139 // we'll write a ~3G file, so allocate more than that for the whole fs
140 uint64_t size
= 1048576 * 1024 * 8ull;
141 string fn
= get_temp_bdev(size
);
142 BlueFS
fs(g_ceph_context
);
144 bool old
= g_ceph_context
->_conf
.get_val
<bool>("bluefs_buffered_io");
145 g_ceph_context
->_conf
.set_val("bluefs_buffered_io", "false");
147 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
148 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
150 ASSERT_EQ(0, fs
.mkfs(fsid
));
151 ASSERT_EQ(0, fs
.mount());
152 char buf
[1048571]; // this is biggish, but intentionally not evenly aligned
153 for (unsigned i
= 0; i
< sizeof(buf
); ++i
) {
157 BlueFS::FileWriter
*h
;
158 ASSERT_EQ(0, fs
.mkdir("dir"));
159 ASSERT_EQ(0, fs
.open_for_write("dir", "bigfile", &h
, false));
160 for (unsigned i
= 0; i
< 3*1024*1048576ull / sizeof(buf
); ++i
) {
161 h
->append(buf
, sizeof(buf
));
167 BlueFS::FileReader
*h
;
168 ASSERT_EQ(0, fs
.open_for_read("dir", "bigfile", &h
));
170 BlueFS::FileReaderBuffer
readbuf(10485760);
171 for (unsigned i
= 0; i
< 3*1024*1048576ull / sizeof(buf
); ++i
) {
173 fs
.read(h
, &readbuf
, i
* sizeof(buf
), sizeof(buf
), &bl
, NULL
);
174 int r
= memcmp(buf
, bl
.c_str(), sizeof(buf
));
176 cerr
<< "read got mismatch at offset " << i
*sizeof(buf
) << " r " << r
185 g_ceph_context
->_conf
.set_val("bluefs_buffered_io", stringify((int)old
));
190 #define ALLOC_SIZE 4096
192 void write_data(BlueFS
&fs
, uint64_t rationed_bytes
)
195 uint64_t written_bytes
= 0;
196 rationed_bytes
-= ALLOC_SIZE
;
199 ss
<< std::this_thread::get_id();
200 dir
.append(ss
.str());
202 dir
.append(to_string(j
));
203 ASSERT_EQ(0, fs
.mkdir(dir
));
205 string file
= "file.";
206 file
.append(to_string(j
));
207 BlueFS::FileWriter
*h
;
208 ASSERT_EQ(0, fs
.open_for_write(dir
, file
, &h
, false));
209 ASSERT_NE(nullptr, h
);
210 auto sg
= make_scope_guard([&fs
, h
] { fs
.close_writer(h
); });
212 std::unique_ptr
<char[]> buf
= gen_buffer(ALLOC_SIZE
);
213 bufferptr bp
= buffer::claim_char(ALLOC_SIZE
, buf
.get());
215 h
->append(bl
.c_str(), bl
.length());
220 written_bytes
+= g_conf()->bluefs_alloc_size
;
222 if ((rationed_bytes
- written_bytes
) <= g_conf()->bluefs_alloc_size
) {
228 void create_single_file(BlueFS
&fs
)
230 BlueFS::FileWriter
*h
;
232 string dir
= "dir.test";
233 ASSERT_EQ(0, fs
.mkdir(dir
));
234 string file
= "testfile";
235 ASSERT_EQ(0, fs
.open_for_write(dir
, file
, &h
, false));
237 std::unique_ptr
<char[]> buf
= gen_buffer(ALLOC_SIZE
);
238 bufferptr bp
= buffer::claim_char(ALLOC_SIZE
, buf
.get());
240 h
->append(bl
.c_str(), bl
.length());
245 void write_single_file(BlueFS
&fs
, uint64_t rationed_bytes
)
248 const string dir
= "dir.test";
249 const string file
= "testfile";
250 uint64_t written_bytes
= 0;
251 rationed_bytes
-= ALLOC_SIZE
;
253 BlueFS::FileWriter
*h
;
254 ASSERT_EQ(0, fs
.open_for_write(dir
, file
, &h
, false));
255 ASSERT_NE(nullptr, h
);
256 auto sg
= make_scope_guard([&fs
, h
] { fs
.close_writer(h
); });
258 std::unique_ptr
<char[]> buf
= gen_buffer(ALLOC_SIZE
);
259 bufferptr bp
= buffer::claim_char(ALLOC_SIZE
, buf
.get());
261 h
->append(bl
.c_str(), bl
.length());
266 written_bytes
+= g_conf()->bluefs_alloc_size
;
267 if ((rationed_bytes
- written_bytes
) <= g_conf()->bluefs_alloc_size
) {
273 bool writes_done
= false;
275 void sync_fs(BlueFS
&fs
)
278 if (writes_done
== true)
286 void do_join(std::thread
& t
)
291 void join_all(std::vector
<std::thread
>& v
)
293 std::for_each(v
.begin(),v
.end(),do_join
);
296 #define NUM_WRITERS 3
297 #define NUM_SYNC_THREADS 1
299 #define NUM_SINGLE_FILE_WRITERS 1
300 #define NUM_MULTIPLE_FILE_WRITERS 2
302 TEST(BlueFS
, test_flush_1
) {
303 uint64_t size
= 1048576 * 128;
304 string fn
= get_temp_bdev(size
);
305 g_ceph_context
->_conf
.set_val(
308 g_ceph_context
->_conf
.apply_changes(nullptr);
310 BlueFS
fs(g_ceph_context
);
311 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
312 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
314 ASSERT_EQ(0, fs
.mkfs(fsid
));
315 ASSERT_EQ(0, fs
.mount());
317 std::vector
<std::thread
> write_thread_multiple
;
318 uint64_t effective_size
= size
- (32 * 1048576); // leaving the last 32 MB for log compaction
319 uint64_t per_thread_bytes
= (effective_size
/(NUM_MULTIPLE_FILE_WRITERS
+ NUM_SINGLE_FILE_WRITERS
));
320 for (int i
=0; i
<NUM_MULTIPLE_FILE_WRITERS
; i
++) {
321 write_thread_multiple
.push_back(std::thread(write_data
, std::ref(fs
), per_thread_bytes
));
324 create_single_file(fs
);
325 std::vector
<std::thread
> write_thread_single
;
326 for (int i
=0; i
<NUM_SINGLE_FILE_WRITERS
; i
++) {
327 write_thread_single
.push_back(std::thread(write_single_file
, std::ref(fs
), per_thread_bytes
));
330 join_all(write_thread_single
);
331 join_all(write_thread_multiple
);
337 TEST(BlueFS
, test_flush_2
) {
338 uint64_t size
= 1048576 * 256;
339 string fn
= get_temp_bdev(size
);
340 g_ceph_context
->_conf
.set_val(
343 g_ceph_context
->_conf
.apply_changes(nullptr);
345 BlueFS
fs(g_ceph_context
);
346 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
347 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
349 ASSERT_EQ(0, fs
.mkfs(fsid
));
350 ASSERT_EQ(0, fs
.mount());
352 uint64_t effective_size
= size
- (128 * 1048576); // leaving the last 32 MB for log compaction
353 uint64_t per_thread_bytes
= (effective_size
/(NUM_WRITERS
));
354 std::vector
<std::thread
> write_thread_multiple
;
355 for (int i
=0; i
<NUM_WRITERS
; i
++) {
356 write_thread_multiple
.push_back(std::thread(write_data
, std::ref(fs
), per_thread_bytes
));
359 join_all(write_thread_multiple
);
365 TEST(BlueFS
, test_flush_3
) {
366 uint64_t size
= 1048576 * 256;
367 string fn
= get_temp_bdev(size
);
368 g_ceph_context
->_conf
.set_val(
371 g_ceph_context
->_conf
.apply_changes(nullptr);
373 BlueFS
fs(g_ceph_context
);
374 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
375 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
377 ASSERT_EQ(0, fs
.mkfs(fsid
));
378 ASSERT_EQ(0, fs
.mount());
380 std::vector
<std::thread
> write_threads
;
381 uint64_t effective_size
= size
- (64 * 1048576); // leaving the last 11 MB for log compaction
382 uint64_t per_thread_bytes
= (effective_size
/(NUM_WRITERS
));
383 for (int i
=0; i
<NUM_WRITERS
; i
++) {
384 write_threads
.push_back(std::thread(write_data
, std::ref(fs
), per_thread_bytes
));
387 std::vector
<std::thread
> sync_threads
;
388 for (int i
=0; i
<NUM_SYNC_THREADS
; i
++) {
389 sync_threads
.push_back(std::thread(sync_fs
, std::ref(fs
)));
392 join_all(write_threads
);
394 join_all(sync_threads
);
400 TEST(BlueFS
, test_simple_compaction_sync
) {
401 g_ceph_context
->_conf
.set_val(
402 "bluefs_compact_log_sync",
404 uint64_t size
= 1048576 * 128;
405 string fn
= get_temp_bdev(size
);
407 BlueFS
fs(g_ceph_context
);
408 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
409 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
411 ASSERT_EQ(0, fs
.mkfs(fsid
));
412 ASSERT_EQ(0, fs
.mount());
414 for (int i
=0; i
<10; i
++) {
416 dir
.append(to_string(i
));
417 ASSERT_EQ(0, fs
.mkdir(dir
));
418 for (int j
=0; j
<10; j
++) {
419 string file
= "file.";
420 file
.append(to_string(j
));
421 BlueFS::FileWriter
*h
;
422 ASSERT_EQ(0, fs
.open_for_write(dir
, file
, &h
, false));
423 ASSERT_NE(nullptr, h
);
424 auto sg
= make_scope_guard([&fs
, h
] { fs
.close_writer(h
); });
426 std::unique_ptr
<char[]> buf
= gen_buffer(4096);
427 bufferptr bp
= buffer::claim_char(4096, buf
.get());
429 h
->append(bl
.c_str(), bl
.length());
435 for (int i
=0; i
<10; i
+=2) {
437 dir
.append(to_string(i
));
438 for (int j
=0; j
<10; j
++) {
439 string file
= "file.";
440 file
.append(to_string(j
));
441 fs
.unlink(dir
, file
);
444 ASSERT_EQ(0, fs
.rmdir(dir
));
453 TEST(BlueFS
, test_simple_compaction_async
) {
454 g_ceph_context
->_conf
.set_val(
455 "bluefs_compact_log_sync",
457 uint64_t size
= 1048576 * 128;
458 string fn
= get_temp_bdev(size
);
460 BlueFS
fs(g_ceph_context
);
461 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
462 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
464 ASSERT_EQ(0, fs
.mkfs(fsid
));
465 ASSERT_EQ(0, fs
.mount());
467 for (int i
=0; i
<10; i
++) {
469 dir
.append(to_string(i
));
470 ASSERT_EQ(0, fs
.mkdir(dir
));
471 for (int j
=0; j
<10; j
++) {
472 string file
= "file.";
473 file
.append(to_string(j
));
474 BlueFS::FileWriter
*h
;
475 ASSERT_EQ(0, fs
.open_for_write(dir
, file
, &h
, false));
476 ASSERT_NE(nullptr, h
);
477 auto sg
= make_scope_guard([&fs
, h
] { fs
.close_writer(h
); });
479 std::unique_ptr
<char[]> buf
= gen_buffer(4096);
480 bufferptr bp
= buffer::claim_char(4096, buf
.get());
482 h
->append(bl
.c_str(), bl
.length());
488 for (int i
=0; i
<10; i
+=2) {
490 dir
.append(to_string(i
));
491 for (int j
=0; j
<10; j
++) {
492 string file
= "file.";
493 file
.append(to_string(j
));
494 fs
.unlink(dir
, file
);
497 ASSERT_EQ(0, fs
.rmdir(dir
));
506 TEST(BlueFS
, test_compaction_sync
) {
507 uint64_t size
= 1048576 * 128;
508 string fn
= get_temp_bdev(size
);
509 g_ceph_context
->_conf
.set_val(
512 g_ceph_context
->_conf
.set_val(
513 "bluefs_compact_log_sync",
516 BlueFS
fs(g_ceph_context
);
517 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
518 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
520 ASSERT_EQ(0, fs
.mkfs(fsid
));
521 ASSERT_EQ(0, fs
.mount());
523 std::vector
<std::thread
> write_threads
;
524 uint64_t effective_size
= size
- (32 * 1048576); // leaving the last 32 MB for log compaction
525 uint64_t per_thread_bytes
= (effective_size
/(NUM_WRITERS
));
526 for (int i
=0; i
<NUM_WRITERS
; i
++) {
527 write_threads
.push_back(std::thread(write_data
, std::ref(fs
), per_thread_bytes
));
530 std::vector
<std::thread
> sync_threads
;
531 for (int i
=0; i
<NUM_SYNC_THREADS
; i
++) {
532 sync_threads
.push_back(std::thread(sync_fs
, std::ref(fs
)));
535 join_all(write_threads
);
537 join_all(sync_threads
);
544 TEST(BlueFS
, test_compaction_async
) {
545 uint64_t size
= 1048576 * 128;
546 string fn
= get_temp_bdev(size
);
547 g_ceph_context
->_conf
.set_val(
550 g_ceph_context
->_conf
.set_val(
551 "bluefs_compact_log_sync",
554 BlueFS
fs(g_ceph_context
);
555 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
556 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
558 ASSERT_EQ(0, fs
.mkfs(fsid
));
559 ASSERT_EQ(0, fs
.mount());
561 std::vector
<std::thread
> write_threads
;
562 uint64_t effective_size
= size
- (32 * 1048576); // leaving the last 32 MB for log compaction
563 uint64_t per_thread_bytes
= (effective_size
/(NUM_WRITERS
));
564 for (int i
=0; i
<NUM_WRITERS
; i
++) {
565 write_threads
.push_back(std::thread(write_data
, std::ref(fs
), per_thread_bytes
));
568 std::vector
<std::thread
> sync_threads
;
569 for (int i
=0; i
<NUM_SYNC_THREADS
; i
++) {
570 sync_threads
.push_back(std::thread(sync_fs
, std::ref(fs
)));
573 join_all(write_threads
);
575 join_all(sync_threads
);
582 TEST(BlueFS
, test_replay
) {
583 uint64_t size
= 1048576 * 128;
584 string fn
= get_temp_bdev(size
);
585 g_ceph_context
->_conf
.set_val(
588 g_ceph_context
->_conf
.set_val(
589 "bluefs_compact_log_sync",
592 BlueFS
fs(g_ceph_context
);
593 ASSERT_EQ(0, fs
.add_block_device(BlueFS::BDEV_DB
, fn
, false));
594 fs
.add_block_extent(BlueFS::BDEV_DB
, 1048576, size
- 1048576);
596 ASSERT_EQ(0, fs
.mkfs(fsid
));
597 ASSERT_EQ(0, fs
.mount());
599 std::vector
<std::thread
> write_threads
;
600 uint64_t effective_size
= size
- (32 * 1048576); // leaving the last 32 MB for log compaction
601 uint64_t per_thread_bytes
= (effective_size
/(NUM_WRITERS
));
602 for (int i
=0; i
<NUM_WRITERS
; i
++) {
603 write_threads
.push_back(std::thread(write_data
, std::ref(fs
), per_thread_bytes
));
606 std::vector
<std::thread
> sync_threads
;
607 for (int i
=0; i
<NUM_SYNC_THREADS
; i
++) {
608 sync_threads
.push_back(std::thread(sync_fs
, std::ref(fs
)));
611 join_all(write_threads
);
613 join_all(sync_threads
);
617 // remount and check log can replay safe?
618 ASSERT_EQ(0, fs
.mount());
623 int main(int argc
, char **argv
) {
624 vector
<const char*> args
;
625 argv_to_vec(argc
, (const char **)argv
, args
);
627 map
<string
,string
> defaults
= {
628 { "debug_bluefs", "1/20" },
629 { "debug_bdev", "1/20" }
632 auto cct
= global_init(&defaults
, args
, CEPH_ENTITY_TYPE_CLIENT
,
633 CODE_ENVIRONMENT_UTILITY
,
634 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
);
635 common_init_finish(g_ceph_context
);
636 g_ceph_context
->_conf
.set_val(
637 "enable_experimental_unrecoverable_data_corrupting_features",
639 g_ceph_context
->_conf
.apply_changes(nullptr);
641 ::testing::InitGoogleTest(&argc
, argv
);
642 return RUN_ALL_TESTS();