1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "spdk/stdinc.h" 35 36 #include "spdk/blobfs.h" 37 #include "spdk/env.h" 38 #include "spdk/log.h" 39 #include "spdk/thread.h" 40 #include "spdk/barrier.h" 41 #include "spdk_internal/thread.h" 42 43 #include "spdk_cunit.h" 44 #include "unit/lib/blob/bs_dev_common.c" 45 #include "common/lib/test_env.c" 46 #include "blobfs/blobfs.c" 47 #include "blobfs/tree.c" 48 49 struct spdk_filesystem *g_fs; 50 struct spdk_file *g_file; 51 int g_fserrno; 52 struct spdk_thread *g_dispatch_thread = NULL; 53 struct spdk_trace_histories *g_trace_histories; 54 DEFINE_STUB_V(spdk_trace_add_register_fn, (struct spdk_trace_register_fn *reg_fn)); 55 DEFINE_STUB_V(spdk_trace_register_description, (const char *name, 56 uint16_t tpoint_id, uint8_t owner_type, 57 uint8_t object_type, uint8_t new_object, 58 uint8_t arg1_is_ptr, const char *arg1_name)); 59 DEFINE_STUB_V(_spdk_trace_record, (uint64_t tsc, uint16_t tpoint_id, uint16_t poller_id, 60 uint32_t size, uint64_t object_id, uint64_t arg1)); 61 62 /* Return NULL to test hardcoded defaults. */ 63 struct spdk_conf_section * 64 spdk_conf_find_section(struct spdk_conf *cp, const char *name) 65 { 66 return NULL; 67 } 68 69 /* Return -1 to test hardcoded defaults. */ 70 int 71 spdk_conf_section_get_intval(struct spdk_conf_section *sp, const char *key) 72 { 73 return -1; 74 } 75 76 struct ut_request { 77 fs_request_fn fn; 78 void *arg; 79 volatile int done; 80 }; 81 82 static void 83 send_request(fs_request_fn fn, void *arg) 84 { 85 spdk_thread_send_msg(g_dispatch_thread, (spdk_msg_fn)fn, arg); 86 } 87 88 static void 89 ut_call_fn(void *arg) 90 { 91 struct ut_request *req = arg; 92 93 req->fn(req->arg); 94 req->done = 1; 95 } 96 97 static void 98 ut_send_request(fs_request_fn fn, void *arg) 99 { 100 struct ut_request req; 101 102 req.fn = fn; 103 req.arg = arg; 104 req.done = 0; 105 106 spdk_thread_send_msg(g_dispatch_thread, ut_call_fn, &req); 107 108 /* Wait for this to finish */ 109 while (req.done == 0) { } 110 } 111 112 static void 113 fs_op_complete(void *ctx, int fserrno) 114 { 115 g_fserrno = fserrno; 116 } 117 118 static void 119 fs_op_with_handle_complete(void *ctx, struct spdk_filesystem *fs, int fserrno) 120 { 121 g_fs = fs; 122 g_fserrno = fserrno; 123 } 124 125 static void 126 _fs_init(void *arg) 127 { 128 struct spdk_thread *thread; 129 struct spdk_bs_dev *dev; 130 131 g_fs = NULL; 132 g_fserrno = -1; 133 dev = init_dev(); 134 spdk_fs_init(dev, NULL, send_request, fs_op_with_handle_complete, NULL); 135 thread = spdk_get_thread(); 136 while (spdk_thread_poll(thread, 0, 0) > 0) {} 137 138 SPDK_CU_ASSERT_FATAL(g_fs != NULL); 139 SPDK_CU_ASSERT_FATAL(g_fs->bdev == dev); 140 CU_ASSERT(g_fserrno == 0); 141 } 142 143 static void 144 _fs_load(void *arg) 145 { 146 struct spdk_thread *thread; 147 struct spdk_bs_dev *dev; 148 149 g_fs = NULL; 150 g_fserrno = -1; 151 dev = init_dev(); 152 spdk_fs_load(dev, send_request, fs_op_with_handle_complete, NULL); 153 thread = spdk_get_thread(); 154 while (spdk_thread_poll(thread, 0, 0) > 0) {} 155 156 SPDK_CU_ASSERT_FATAL(g_fs != NULL); 157 SPDK_CU_ASSERT_FATAL(g_fs->bdev == dev); 158 CU_ASSERT(g_fserrno == 0); 159 } 160 161 static void 162 _fs_unload(void *arg) 163 { 164 struct spdk_thread *thread; 165 166 g_fserrno = -1; 167 spdk_fs_unload(g_fs, fs_op_complete, NULL); 168 thread = spdk_get_thread(); 169 while (spdk_thread_poll(thread, 0, 0) > 0) {} 170 CU_ASSERT(g_fserrno == 0); 171 g_fs = NULL; 172 } 173 174 static void 175 _nop(void *arg) 176 { 177 } 178 179 static void 180 cache_read_after_write(void) 181 { 182 uint64_t length; 183 int rc; 184 char w_buf[100], r_buf[100]; 185 struct spdk_fs_thread_ctx *channel; 186 struct spdk_file_stat stat = {0}; 187 188 ut_send_request(_fs_init, NULL); 189 190 channel = spdk_fs_alloc_thread_ctx(g_fs); 191 192 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 193 CU_ASSERT(rc == 0); 194 SPDK_CU_ASSERT_FATAL(g_file != NULL); 195 196 length = (4 * 1024 * 1024); 197 rc = spdk_file_truncate(g_file, channel, length); 198 CU_ASSERT(rc == 0); 199 200 memset(w_buf, 0x5a, sizeof(w_buf)); 201 spdk_file_write(g_file, channel, w_buf, 0, sizeof(w_buf)); 202 203 CU_ASSERT(spdk_file_get_length(g_file) == length); 204 205 rc = spdk_file_truncate(g_file, channel, sizeof(w_buf)); 206 CU_ASSERT(rc == 0); 207 208 spdk_file_close(g_file, channel); 209 210 rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); 211 CU_ASSERT(rc == 0); 212 CU_ASSERT(sizeof(w_buf) == stat.size); 213 214 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &g_file); 215 CU_ASSERT(rc == 0); 216 SPDK_CU_ASSERT_FATAL(g_file != NULL); 217 218 memset(r_buf, 0, sizeof(r_buf)); 219 spdk_file_read(g_file, channel, r_buf, 0, sizeof(r_buf)); 220 CU_ASSERT(memcmp(w_buf, r_buf, sizeof(r_buf)) == 0); 221 222 spdk_file_close(g_file, channel); 223 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 224 CU_ASSERT(rc == 0); 225 226 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 227 CU_ASSERT(rc == -ENOENT); 228 229 spdk_fs_free_thread_ctx(channel); 230 231 ut_send_request(_fs_unload, NULL); 232 } 233 234 static void 235 file_length(void) 236 { 237 int rc; 238 char *buf; 239 uint64_t buf_length; 240 volatile uint64_t *length_flushed; 241 struct spdk_fs_thread_ctx *channel; 242 struct spdk_file_stat stat = {0}; 243 244 ut_send_request(_fs_init, NULL); 245 246 channel = spdk_fs_alloc_thread_ctx(g_fs); 247 248 g_file = NULL; 249 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 250 CU_ASSERT(rc == 0); 251 SPDK_CU_ASSERT_FATAL(g_file != NULL); 252 253 /* Write one CACHE_BUFFER. Filling at least one cache buffer triggers 254 * a flush to disk. 255 */ 256 buf_length = CACHE_BUFFER_SIZE; 257 buf = calloc(1, buf_length); 258 spdk_file_write(g_file, channel, buf, 0, buf_length); 259 free(buf); 260 261 /* Spin until all of the data has been flushed to the SSD. There's been no 262 * sync operation yet, so the xattr on the file is still 0. 263 * 264 * length_flushed: This variable is modified by a different thread in this unit 265 * test. So we need to dereference it as a volatile to ensure the value is always 266 * re-read. 267 */ 268 length_flushed = &g_file->length_flushed; 269 while (*length_flushed != buf_length) {} 270 271 /* Close the file. This causes an implicit sync which should write the 272 * length_flushed value as the "length" xattr on the file. 273 */ 274 spdk_file_close(g_file, channel); 275 276 rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); 277 CU_ASSERT(rc == 0); 278 CU_ASSERT(buf_length == stat.size); 279 280 spdk_fs_free_thread_ctx(channel); 281 282 /* Unload and reload the filesystem. The file length will be 283 * read during load from the length xattr. We want to make sure 284 * it matches what was written when the file was originally 285 * written and closed. 286 */ 287 ut_send_request(_fs_unload, NULL); 288 289 ut_send_request(_fs_load, NULL); 290 291 channel = spdk_fs_alloc_thread_ctx(g_fs); 292 293 rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); 294 CU_ASSERT(rc == 0); 295 CU_ASSERT(buf_length == stat.size); 296 297 g_file = NULL; 298 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &g_file); 299 CU_ASSERT(rc == 0); 300 SPDK_CU_ASSERT_FATAL(g_file != NULL); 301 302 spdk_file_close(g_file, channel); 303 304 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 305 CU_ASSERT(rc == 0); 306 307 spdk_fs_free_thread_ctx(channel); 308 309 ut_send_request(_fs_unload, NULL); 310 } 311 312 static void 313 append_write_to_extend_blob(void) 314 { 315 uint64_t blob_size, buf_length; 316 char *buf, append_buf[64]; 317 int rc; 318 struct spdk_fs_thread_ctx *channel; 319 320 ut_send_request(_fs_init, NULL); 321 322 channel = spdk_fs_alloc_thread_ctx(g_fs); 323 324 /* create a file and write the file with blob_size - 1 data length */ 325 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 326 CU_ASSERT(rc == 0); 327 SPDK_CU_ASSERT_FATAL(g_file != NULL); 328 329 blob_size = __file_get_blob_size(g_file); 330 331 buf_length = blob_size - 1; 332 buf = calloc(1, buf_length); 333 rc = spdk_file_write(g_file, channel, buf, 0, buf_length); 334 CU_ASSERT(rc == 0); 335 free(buf); 336 337 spdk_file_close(g_file, channel); 338 spdk_fs_free_thread_ctx(channel); 339 ut_send_request(_fs_unload, NULL); 340 341 /* load existing file and write extra 2 bytes to cross blob boundary */ 342 ut_send_request(_fs_load, NULL); 343 344 channel = spdk_fs_alloc_thread_ctx(g_fs); 345 g_file = NULL; 346 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &g_file); 347 CU_ASSERT(rc == 0); 348 SPDK_CU_ASSERT_FATAL(g_file != NULL); 349 350 CU_ASSERT(g_file->length == buf_length); 351 CU_ASSERT(g_file->last == NULL); 352 CU_ASSERT(g_file->append_pos == buf_length); 353 354 rc = spdk_file_write(g_file, channel, append_buf, buf_length, 2); 355 CU_ASSERT(rc == 0); 356 CU_ASSERT(2 * blob_size == __file_get_blob_size(g_file)); 357 spdk_file_close(g_file, channel); 358 CU_ASSERT(g_file->length == buf_length + 2); 359 360 spdk_fs_free_thread_ctx(channel); 361 ut_send_request(_fs_unload, NULL); 362 } 363 364 static void 365 partial_buffer(void) 366 { 367 int rc; 368 char *buf; 369 uint64_t buf_length; 370 struct spdk_fs_thread_ctx *channel; 371 struct spdk_file_stat stat = {0}; 372 373 ut_send_request(_fs_init, NULL); 374 375 channel = spdk_fs_alloc_thread_ctx(g_fs); 376 377 g_file = NULL; 378 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 379 CU_ASSERT(rc == 0); 380 SPDK_CU_ASSERT_FATAL(g_file != NULL); 381 382 /* Write one CACHE_BUFFER plus one byte. Filling at least one cache buffer triggers 383 * a flush to disk. We want to make sure the extra byte is not implicitly flushed. 384 * It should only get flushed once we sync or close the file. 385 */ 386 buf_length = CACHE_BUFFER_SIZE + 1; 387 buf = calloc(1, buf_length); 388 spdk_file_write(g_file, channel, buf, 0, buf_length); 389 free(buf); 390 391 /* Send some nop messages to the dispatch thread. This will ensure any of the 392 * pending write operations are completed. A well-functioning blobfs should only 393 * issue one write for the filled CACHE_BUFFER - a buggy one might try to write 394 * the extra byte. So do a bunch of _nops to make sure all of them (even the buggy 395 * ones) get a chance to run. Note that we can't just send a message to the 396 * dispatch thread to call spdk_thread_poll() because the messages are themselves 397 * run in the context of spdk_thread_poll(). 398 */ 399 ut_send_request(_nop, NULL); 400 ut_send_request(_nop, NULL); 401 ut_send_request(_nop, NULL); 402 ut_send_request(_nop, NULL); 403 ut_send_request(_nop, NULL); 404 ut_send_request(_nop, NULL); 405 406 CU_ASSERT(g_file->length_flushed == CACHE_BUFFER_SIZE); 407 408 /* Close the file. This causes an implicit sync which should write the 409 * length_flushed value as the "length" xattr on the file. 410 */ 411 spdk_file_close(g_file, channel); 412 413 rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); 414 CU_ASSERT(rc == 0); 415 CU_ASSERT(buf_length == stat.size); 416 417 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 418 CU_ASSERT(rc == 0); 419 420 spdk_fs_free_thread_ctx(channel); 421 422 ut_send_request(_fs_unload, NULL); 423 } 424 425 static void 426 cache_write_null_buffer(void) 427 { 428 uint64_t length; 429 int rc; 430 struct spdk_fs_thread_ctx *channel; 431 struct spdk_thread *thread; 432 433 ut_send_request(_fs_init, NULL); 434 435 channel = spdk_fs_alloc_thread_ctx(g_fs); 436 437 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 438 CU_ASSERT(rc == 0); 439 SPDK_CU_ASSERT_FATAL(g_file != NULL); 440 441 length = 0; 442 rc = spdk_file_truncate(g_file, channel, length); 443 CU_ASSERT(rc == 0); 444 445 rc = spdk_file_write(g_file, channel, NULL, 0, 0); 446 CU_ASSERT(rc == 0); 447 448 spdk_file_close(g_file, channel); 449 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 450 CU_ASSERT(rc == 0); 451 452 spdk_fs_free_thread_ctx(channel); 453 454 thread = spdk_get_thread(); 455 while (spdk_thread_poll(thread, 0, 0) > 0) {} 456 457 ut_send_request(_fs_unload, NULL); 458 } 459 460 static void 461 fs_create_sync(void) 462 { 463 int rc; 464 struct spdk_fs_thread_ctx *channel; 465 struct spdk_thread *thread; 466 467 ut_send_request(_fs_init, NULL); 468 469 channel = spdk_fs_alloc_thread_ctx(g_fs); 470 CU_ASSERT(channel != NULL); 471 472 rc = spdk_fs_create_file(g_fs, channel, "testfile"); 473 CU_ASSERT(rc == 0); 474 475 /* Create should fail, because the file already exists. */ 476 rc = spdk_fs_create_file(g_fs, channel, "testfile"); 477 CU_ASSERT(rc != 0); 478 479 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 480 CU_ASSERT(rc == 0); 481 482 spdk_fs_free_thread_ctx(channel); 483 484 thread = spdk_get_thread(); 485 while (spdk_thread_poll(thread, 0, 0) > 0) {} 486 487 ut_send_request(_fs_unload, NULL); 488 } 489 490 static void 491 fs_rename_sync(void) 492 { 493 int rc; 494 struct spdk_fs_thread_ctx *channel; 495 struct spdk_thread *thread; 496 497 ut_send_request(_fs_init, NULL); 498 499 channel = spdk_fs_alloc_thread_ctx(g_fs); 500 CU_ASSERT(channel != NULL); 501 502 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 503 CU_ASSERT(rc == 0); 504 SPDK_CU_ASSERT_FATAL(g_file != NULL); 505 506 CU_ASSERT(strcmp(spdk_file_get_name(g_file), "testfile") == 0); 507 508 rc = spdk_fs_rename_file(g_fs, channel, "testfile", "newtestfile"); 509 CU_ASSERT(rc == 0); 510 CU_ASSERT(strcmp(spdk_file_get_name(g_file), "newtestfile") == 0); 511 512 spdk_file_close(g_file, channel); 513 spdk_fs_free_thread_ctx(channel); 514 515 thread = spdk_get_thread(); 516 while (spdk_thread_poll(thread, 0, 0) > 0) {} 517 518 ut_send_request(_fs_unload, NULL); 519 } 520 521 static void 522 cache_append_no_cache(void) 523 { 524 int rc; 525 char buf[100]; 526 struct spdk_fs_thread_ctx *channel; 527 struct spdk_thread *thread; 528 529 ut_send_request(_fs_init, NULL); 530 531 channel = spdk_fs_alloc_thread_ctx(g_fs); 532 533 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 534 CU_ASSERT(rc == 0); 535 SPDK_CU_ASSERT_FATAL(g_file != NULL); 536 537 spdk_file_write(g_file, channel, buf, 0 * sizeof(buf), sizeof(buf)); 538 CU_ASSERT(spdk_file_get_length(g_file) == 1 * sizeof(buf)); 539 spdk_file_write(g_file, channel, buf, 1 * sizeof(buf), sizeof(buf)); 540 CU_ASSERT(spdk_file_get_length(g_file) == 2 * sizeof(buf)); 541 spdk_file_sync(g_file, channel); 542 cache_free_buffers(g_file); 543 spdk_file_write(g_file, channel, buf, 2 * sizeof(buf), sizeof(buf)); 544 CU_ASSERT(spdk_file_get_length(g_file) == 3 * sizeof(buf)); 545 spdk_file_write(g_file, channel, buf, 3 * sizeof(buf), sizeof(buf)); 546 CU_ASSERT(spdk_file_get_length(g_file) == 4 * sizeof(buf)); 547 spdk_file_write(g_file, channel, buf, 4 * sizeof(buf), sizeof(buf)); 548 CU_ASSERT(spdk_file_get_length(g_file) == 5 * sizeof(buf)); 549 550 spdk_file_close(g_file, channel); 551 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 552 CU_ASSERT(rc == 0); 553 554 spdk_fs_free_thread_ctx(channel); 555 556 thread = spdk_get_thread(); 557 while (spdk_thread_poll(thread, 0, 0) > 0) {} 558 559 ut_send_request(_fs_unload, NULL); 560 } 561 562 static void 563 fs_delete_file_without_close(void) 564 { 565 int rc; 566 struct spdk_fs_thread_ctx *channel; 567 struct spdk_file *file; 568 struct spdk_thread *thread; 569 570 ut_send_request(_fs_init, NULL); 571 channel = spdk_fs_alloc_thread_ctx(g_fs); 572 CU_ASSERT(channel != NULL); 573 574 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 575 CU_ASSERT(rc == 0); 576 SPDK_CU_ASSERT_FATAL(g_file != NULL); 577 578 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 579 CU_ASSERT(rc == 0); 580 CU_ASSERT(g_file->ref_count != 0); 581 CU_ASSERT(g_file->is_deleted == true); 582 583 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &file); 584 CU_ASSERT(rc != 0); 585 586 spdk_file_close(g_file, channel); 587 588 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &file); 589 CU_ASSERT(rc != 0); 590 591 spdk_fs_free_thread_ctx(channel); 592 593 thread = spdk_get_thread(); 594 while (spdk_thread_poll(thread, 0, 0) > 0) {} 595 596 ut_send_request(_fs_unload, NULL); 597 598 } 599 600 static bool g_thread_exit = false; 601 602 static void 603 terminate_spdk_thread(void *arg) 604 { 605 g_thread_exit = true; 606 } 607 608 static void * 609 spdk_thread(void *arg) 610 { 611 struct spdk_thread *thread = arg; 612 613 spdk_set_thread(thread); 614 615 while (!g_thread_exit) { 616 spdk_thread_poll(thread, 0, 0); 617 } 618 619 return NULL; 620 } 621 622 int main(int argc, char **argv) 623 { 624 struct spdk_thread *thread; 625 CU_pSuite suite = NULL; 626 pthread_t spdk_tid; 627 unsigned int num_failures; 628 629 if (CU_initialize_registry() != CUE_SUCCESS) { 630 return CU_get_error(); 631 } 632 633 suite = CU_add_suite("blobfs_sync_ut", NULL, NULL); 634 if (suite == NULL) { 635 CU_cleanup_registry(); 636 return CU_get_error(); 637 } 638 639 if ( 640 CU_add_test(suite, "cache read after write", cache_read_after_write) == NULL || 641 CU_add_test(suite, "file length", file_length) == NULL || 642 CU_add_test(suite, "append write to extend blob", append_write_to_extend_blob) == NULL || 643 CU_add_test(suite, "partial buffer", partial_buffer) == NULL || 644 CU_add_test(suite, "write_null_buffer", cache_write_null_buffer) == NULL || 645 CU_add_test(suite, "create_sync", fs_create_sync) == NULL || 646 CU_add_test(suite, "rename_sync", fs_rename_sync) == NULL || 647 CU_add_test(suite, "append_no_cache", cache_append_no_cache) == NULL || 648 CU_add_test(suite, "delete_file_without_close", fs_delete_file_without_close) == NULL 649 ) { 650 CU_cleanup_registry(); 651 return CU_get_error(); 652 } 653 654 spdk_thread_lib_init(NULL, 0); 655 656 thread = spdk_thread_create("test_thread", NULL); 657 spdk_set_thread(thread); 658 659 g_dispatch_thread = spdk_thread_create("dispatch_thread", NULL); 660 pthread_create(&spdk_tid, NULL, spdk_thread, g_dispatch_thread); 661 662 g_dev_buffer = calloc(1, DEV_BUFFER_SIZE); 663 664 CU_basic_set_mode(CU_BRM_VERBOSE); 665 CU_basic_run_tests(); 666 num_failures = CU_get_number_of_failures(); 667 CU_cleanup_registry(); 668 669 free(g_dev_buffer); 670 671 ut_send_request(terminate_spdk_thread, NULL); 672 pthread_join(spdk_tid, NULL); 673 674 while (spdk_thread_poll(g_dispatch_thread, 0, 0) > 0) {} 675 while (spdk_thread_poll(thread, 0, 0) > 0) {} 676 677 spdk_set_thread(thread); 678 spdk_thread_exit(thread); 679 spdk_thread_destroy(thread); 680 681 spdk_set_thread(g_dispatch_thread); 682 spdk_thread_exit(g_dispatch_thread); 683 spdk_thread_destroy(g_dispatch_thread); 684 685 spdk_thread_lib_fini(); 686 687 return num_failures; 688 } 689