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_thread_poll(void) 127 { 128 struct spdk_thread *thread; 129 130 thread = spdk_get_thread(); 131 while (spdk_thread_poll(thread, 0, 0) > 0) {} 132 while (spdk_thread_poll(g_cache_pool_thread, 0, 0) > 0) {} 133 } 134 135 static void 136 _fs_init(void *arg) 137 { 138 struct spdk_bs_dev *dev; 139 140 g_fs = NULL; 141 g_fserrno = -1; 142 dev = init_dev(); 143 spdk_fs_init(dev, NULL, send_request, fs_op_with_handle_complete, NULL); 144 145 fs_thread_poll(); 146 147 SPDK_CU_ASSERT_FATAL(g_fs != NULL); 148 SPDK_CU_ASSERT_FATAL(g_fs->bdev == dev); 149 CU_ASSERT(g_fserrno == 0); 150 } 151 152 static void 153 _fs_load(void *arg) 154 { 155 struct spdk_bs_dev *dev; 156 157 g_fs = NULL; 158 g_fserrno = -1; 159 dev = init_dev(); 160 spdk_fs_load(dev, send_request, fs_op_with_handle_complete, NULL); 161 162 fs_thread_poll(); 163 164 SPDK_CU_ASSERT_FATAL(g_fs != NULL); 165 SPDK_CU_ASSERT_FATAL(g_fs->bdev == dev); 166 CU_ASSERT(g_fserrno == 0); 167 } 168 169 static void 170 _fs_unload(void *arg) 171 { 172 g_fserrno = -1; 173 spdk_fs_unload(g_fs, fs_op_complete, NULL); 174 175 fs_thread_poll(); 176 177 CU_ASSERT(g_fserrno == 0); 178 g_fs = NULL; 179 } 180 181 static void 182 _nop(void *arg) 183 { 184 } 185 186 static void 187 cache_read_after_write(void) 188 { 189 uint64_t length; 190 int rc; 191 char w_buf[100], r_buf[100]; 192 struct spdk_fs_thread_ctx *channel; 193 struct spdk_file_stat stat = {0}; 194 195 ut_send_request(_fs_init, NULL); 196 197 channel = spdk_fs_alloc_thread_ctx(g_fs); 198 199 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 200 CU_ASSERT(rc == 0); 201 SPDK_CU_ASSERT_FATAL(g_file != NULL); 202 203 length = (4 * 1024 * 1024); 204 rc = spdk_file_truncate(g_file, channel, length); 205 CU_ASSERT(rc == 0); 206 207 memset(w_buf, 0x5a, sizeof(w_buf)); 208 spdk_file_write(g_file, channel, w_buf, 0, sizeof(w_buf)); 209 210 CU_ASSERT(spdk_file_get_length(g_file) == length); 211 212 rc = spdk_file_truncate(g_file, channel, sizeof(w_buf)); 213 CU_ASSERT(rc == 0); 214 215 spdk_file_close(g_file, channel); 216 217 fs_thread_poll(); 218 219 rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); 220 CU_ASSERT(rc == 0); 221 CU_ASSERT(sizeof(w_buf) == stat.size); 222 223 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &g_file); 224 CU_ASSERT(rc == 0); 225 SPDK_CU_ASSERT_FATAL(g_file != NULL); 226 227 memset(r_buf, 0, sizeof(r_buf)); 228 spdk_file_read(g_file, channel, r_buf, 0, sizeof(r_buf)); 229 CU_ASSERT(memcmp(w_buf, r_buf, sizeof(r_buf)) == 0); 230 231 spdk_file_close(g_file, channel); 232 233 fs_thread_poll(); 234 235 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 236 CU_ASSERT(rc == 0); 237 238 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 239 CU_ASSERT(rc == -ENOENT); 240 241 spdk_fs_free_thread_ctx(channel); 242 243 ut_send_request(_fs_unload, NULL); 244 } 245 246 static void 247 file_length(void) 248 { 249 int rc; 250 char *buf; 251 uint64_t buf_length; 252 volatile uint64_t *length_flushed; 253 struct spdk_fs_thread_ctx *channel; 254 struct spdk_file_stat stat = {0}; 255 256 ut_send_request(_fs_init, NULL); 257 258 channel = spdk_fs_alloc_thread_ctx(g_fs); 259 260 g_file = NULL; 261 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 262 CU_ASSERT(rc == 0); 263 SPDK_CU_ASSERT_FATAL(g_file != NULL); 264 265 /* Write one CACHE_BUFFER. Filling at least one cache buffer triggers 266 * a flush to disk. 267 */ 268 buf_length = CACHE_BUFFER_SIZE; 269 buf = calloc(1, buf_length); 270 spdk_file_write(g_file, channel, buf, 0, buf_length); 271 free(buf); 272 273 /* Spin until all of the data has been flushed to the SSD. There's been no 274 * sync operation yet, so the xattr on the file is still 0. 275 * 276 * length_flushed: This variable is modified by a different thread in this unit 277 * test. So we need to dereference it as a volatile to ensure the value is always 278 * re-read. 279 */ 280 length_flushed = &g_file->length_flushed; 281 while (*length_flushed != buf_length) {} 282 283 /* Close the file. This causes an implicit sync which should write the 284 * length_flushed value as the "length" xattr on the file. 285 */ 286 spdk_file_close(g_file, channel); 287 288 fs_thread_poll(); 289 290 rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); 291 CU_ASSERT(rc == 0); 292 CU_ASSERT(buf_length == stat.size); 293 294 spdk_fs_free_thread_ctx(channel); 295 296 /* Unload and reload the filesystem. The file length will be 297 * read during load from the length xattr. We want to make sure 298 * it matches what was written when the file was originally 299 * written and closed. 300 */ 301 ut_send_request(_fs_unload, NULL); 302 303 ut_send_request(_fs_load, NULL); 304 305 channel = spdk_fs_alloc_thread_ctx(g_fs); 306 307 rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); 308 CU_ASSERT(rc == 0); 309 CU_ASSERT(buf_length == stat.size); 310 311 g_file = NULL; 312 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &g_file); 313 CU_ASSERT(rc == 0); 314 SPDK_CU_ASSERT_FATAL(g_file != NULL); 315 316 spdk_file_close(g_file, channel); 317 318 fs_thread_poll(); 319 320 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 321 CU_ASSERT(rc == 0); 322 323 spdk_fs_free_thread_ctx(channel); 324 325 ut_send_request(_fs_unload, NULL); 326 } 327 328 static void 329 append_write_to_extend_blob(void) 330 { 331 uint64_t blob_size, buf_length; 332 char *buf, append_buf[64]; 333 int rc; 334 struct spdk_fs_thread_ctx *channel; 335 336 ut_send_request(_fs_init, NULL); 337 338 channel = spdk_fs_alloc_thread_ctx(g_fs); 339 340 /* create a file and write the file with blob_size - 1 data length */ 341 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 342 CU_ASSERT(rc == 0); 343 SPDK_CU_ASSERT_FATAL(g_file != NULL); 344 345 blob_size = __file_get_blob_size(g_file); 346 347 buf_length = blob_size - 1; 348 buf = calloc(1, buf_length); 349 rc = spdk_file_write(g_file, channel, buf, 0, buf_length); 350 CU_ASSERT(rc == 0); 351 free(buf); 352 353 spdk_file_close(g_file, channel); 354 fs_thread_poll(); 355 spdk_fs_free_thread_ctx(channel); 356 ut_send_request(_fs_unload, NULL); 357 358 /* load existing file and write extra 2 bytes to cross blob boundary */ 359 ut_send_request(_fs_load, NULL); 360 361 channel = spdk_fs_alloc_thread_ctx(g_fs); 362 g_file = NULL; 363 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &g_file); 364 CU_ASSERT(rc == 0); 365 SPDK_CU_ASSERT_FATAL(g_file != NULL); 366 367 CU_ASSERT(g_file->length == buf_length); 368 CU_ASSERT(g_file->last == NULL); 369 CU_ASSERT(g_file->append_pos == buf_length); 370 371 rc = spdk_file_write(g_file, channel, append_buf, buf_length, 2); 372 CU_ASSERT(rc == 0); 373 CU_ASSERT(2 * blob_size == __file_get_blob_size(g_file)); 374 spdk_file_close(g_file, channel); 375 fs_thread_poll(); 376 CU_ASSERT(g_file->length == buf_length + 2); 377 378 spdk_fs_free_thread_ctx(channel); 379 ut_send_request(_fs_unload, NULL); 380 } 381 382 static void 383 partial_buffer(void) 384 { 385 int rc; 386 char *buf; 387 uint64_t buf_length; 388 struct spdk_fs_thread_ctx *channel; 389 struct spdk_file_stat stat = {0}; 390 391 ut_send_request(_fs_init, NULL); 392 393 channel = spdk_fs_alloc_thread_ctx(g_fs); 394 395 g_file = NULL; 396 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 397 CU_ASSERT(rc == 0); 398 SPDK_CU_ASSERT_FATAL(g_file != NULL); 399 400 /* Write one CACHE_BUFFER plus one byte. Filling at least one cache buffer triggers 401 * a flush to disk. We want to make sure the extra byte is not implicitly flushed. 402 * It should only get flushed once we sync or close the file. 403 */ 404 buf_length = CACHE_BUFFER_SIZE + 1; 405 buf = calloc(1, buf_length); 406 spdk_file_write(g_file, channel, buf, 0, buf_length); 407 free(buf); 408 409 /* Send some nop messages to the dispatch thread. This will ensure any of the 410 * pending write operations are completed. A well-functioning blobfs should only 411 * issue one write for the filled CACHE_BUFFER - a buggy one might try to write 412 * the extra byte. So do a bunch of _nops to make sure all of them (even the buggy 413 * ones) get a chance to run. Note that we can't just send a message to the 414 * dispatch thread to call spdk_thread_poll() because the messages are themselves 415 * run in the context of spdk_thread_poll(). 416 */ 417 ut_send_request(_nop, NULL); 418 ut_send_request(_nop, NULL); 419 ut_send_request(_nop, NULL); 420 ut_send_request(_nop, NULL); 421 ut_send_request(_nop, NULL); 422 ut_send_request(_nop, NULL); 423 424 CU_ASSERT(g_file->length_flushed == CACHE_BUFFER_SIZE); 425 426 /* Close the file. This causes an implicit sync which should write the 427 * length_flushed value as the "length" xattr on the file. 428 */ 429 spdk_file_close(g_file, channel); 430 431 fs_thread_poll(); 432 433 rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); 434 CU_ASSERT(rc == 0); 435 CU_ASSERT(buf_length == stat.size); 436 437 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 438 CU_ASSERT(rc == 0); 439 440 spdk_fs_free_thread_ctx(channel); 441 442 ut_send_request(_fs_unload, NULL); 443 } 444 445 static void 446 cache_write_null_buffer(void) 447 { 448 uint64_t length; 449 int rc; 450 struct spdk_fs_thread_ctx *channel; 451 struct spdk_thread *thread; 452 453 ut_send_request(_fs_init, NULL); 454 455 channel = spdk_fs_alloc_thread_ctx(g_fs); 456 457 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 458 CU_ASSERT(rc == 0); 459 SPDK_CU_ASSERT_FATAL(g_file != NULL); 460 461 length = 0; 462 rc = spdk_file_truncate(g_file, channel, length); 463 CU_ASSERT(rc == 0); 464 465 rc = spdk_file_write(g_file, channel, NULL, 0, 0); 466 CU_ASSERT(rc == 0); 467 468 spdk_file_close(g_file, channel); 469 470 fs_thread_poll(); 471 472 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 473 CU_ASSERT(rc == 0); 474 475 spdk_fs_free_thread_ctx(channel); 476 477 thread = spdk_get_thread(); 478 while (spdk_thread_poll(thread, 0, 0) > 0) {} 479 480 ut_send_request(_fs_unload, NULL); 481 } 482 483 static void 484 fs_create_sync(void) 485 { 486 int rc; 487 struct spdk_fs_thread_ctx *channel; 488 489 ut_send_request(_fs_init, NULL); 490 491 channel = spdk_fs_alloc_thread_ctx(g_fs); 492 CU_ASSERT(channel != NULL); 493 494 rc = spdk_fs_create_file(g_fs, channel, "testfile"); 495 CU_ASSERT(rc == 0); 496 497 /* Create should fail, because the file already exists. */ 498 rc = spdk_fs_create_file(g_fs, channel, "testfile"); 499 CU_ASSERT(rc != 0); 500 501 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 502 CU_ASSERT(rc == 0); 503 504 spdk_fs_free_thread_ctx(channel); 505 506 fs_thread_poll(); 507 508 ut_send_request(_fs_unload, NULL); 509 } 510 511 static void 512 fs_rename_sync(void) 513 { 514 int rc; 515 struct spdk_fs_thread_ctx *channel; 516 517 ut_send_request(_fs_init, NULL); 518 519 channel = spdk_fs_alloc_thread_ctx(g_fs); 520 CU_ASSERT(channel != NULL); 521 522 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 523 CU_ASSERT(rc == 0); 524 SPDK_CU_ASSERT_FATAL(g_file != NULL); 525 526 CU_ASSERT(strcmp(spdk_file_get_name(g_file), "testfile") == 0); 527 528 rc = spdk_fs_rename_file(g_fs, channel, "testfile", "newtestfile"); 529 CU_ASSERT(rc == 0); 530 CU_ASSERT(strcmp(spdk_file_get_name(g_file), "newtestfile") == 0); 531 532 spdk_file_close(g_file, channel); 533 534 fs_thread_poll(); 535 536 spdk_fs_free_thread_ctx(channel); 537 538 ut_send_request(_fs_unload, NULL); 539 } 540 541 static void 542 cache_append_no_cache(void) 543 { 544 int rc; 545 char buf[100]; 546 struct spdk_fs_thread_ctx *channel; 547 548 ut_send_request(_fs_init, NULL); 549 550 channel = spdk_fs_alloc_thread_ctx(g_fs); 551 552 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 553 CU_ASSERT(rc == 0); 554 SPDK_CU_ASSERT_FATAL(g_file != NULL); 555 556 spdk_file_write(g_file, channel, buf, 0 * sizeof(buf), sizeof(buf)); 557 CU_ASSERT(spdk_file_get_length(g_file) == 1 * sizeof(buf)); 558 spdk_file_write(g_file, channel, buf, 1 * sizeof(buf), sizeof(buf)); 559 CU_ASSERT(spdk_file_get_length(g_file) == 2 * sizeof(buf)); 560 spdk_file_sync(g_file, channel); 561 562 fs_thread_poll(); 563 564 spdk_file_write(g_file, channel, buf, 2 * sizeof(buf), sizeof(buf)); 565 CU_ASSERT(spdk_file_get_length(g_file) == 3 * sizeof(buf)); 566 spdk_file_write(g_file, channel, buf, 3 * sizeof(buf), sizeof(buf)); 567 CU_ASSERT(spdk_file_get_length(g_file) == 4 * sizeof(buf)); 568 spdk_file_write(g_file, channel, buf, 4 * sizeof(buf), sizeof(buf)); 569 CU_ASSERT(spdk_file_get_length(g_file) == 5 * sizeof(buf)); 570 571 spdk_file_close(g_file, channel); 572 573 fs_thread_poll(); 574 575 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 576 CU_ASSERT(rc == 0); 577 578 spdk_fs_free_thread_ctx(channel); 579 580 ut_send_request(_fs_unload, NULL); 581 } 582 583 static void 584 fs_delete_file_without_close(void) 585 { 586 int rc; 587 struct spdk_fs_thread_ctx *channel; 588 struct spdk_file *file; 589 590 ut_send_request(_fs_init, NULL); 591 channel = spdk_fs_alloc_thread_ctx(g_fs); 592 CU_ASSERT(channel != NULL); 593 594 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 595 CU_ASSERT(rc == 0); 596 SPDK_CU_ASSERT_FATAL(g_file != NULL); 597 598 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 599 CU_ASSERT(rc == 0); 600 CU_ASSERT(g_file->ref_count != 0); 601 CU_ASSERT(g_file->is_deleted == true); 602 603 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &file); 604 CU_ASSERT(rc != 0); 605 606 spdk_file_close(g_file, channel); 607 608 fs_thread_poll(); 609 610 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &file); 611 CU_ASSERT(rc != 0); 612 613 spdk_fs_free_thread_ctx(channel); 614 615 ut_send_request(_fs_unload, NULL); 616 617 } 618 619 static bool g_thread_exit = false; 620 621 static void 622 terminate_spdk_thread(void *arg) 623 { 624 g_thread_exit = true; 625 } 626 627 static void * 628 spdk_thread(void *arg) 629 { 630 struct spdk_thread *thread = arg; 631 632 spdk_set_thread(thread); 633 634 while (!g_thread_exit) { 635 spdk_thread_poll(thread, 0, 0); 636 } 637 638 return NULL; 639 } 640 641 int main(int argc, char **argv) 642 { 643 struct spdk_thread *thread; 644 CU_pSuite suite = NULL; 645 pthread_t spdk_tid; 646 unsigned int num_failures; 647 648 CU_set_error_action(CUEA_ABORT); 649 CU_initialize_registry(); 650 651 suite = CU_add_suite("blobfs_sync_ut", NULL, NULL); 652 653 CU_ADD_TEST(suite, cache_read_after_write); 654 CU_ADD_TEST(suite, file_length); 655 CU_ADD_TEST(suite, append_write_to_extend_blob); 656 CU_ADD_TEST(suite, partial_buffer); 657 CU_ADD_TEST(suite, cache_write_null_buffer); 658 CU_ADD_TEST(suite, fs_create_sync); 659 CU_ADD_TEST(suite, fs_rename_sync); 660 CU_ADD_TEST(suite, cache_append_no_cache); 661 CU_ADD_TEST(suite, fs_delete_file_without_close); 662 663 spdk_thread_lib_init(NULL, 0); 664 665 thread = spdk_thread_create("test_thread", NULL); 666 spdk_set_thread(thread); 667 668 g_dispatch_thread = spdk_thread_create("dispatch_thread", NULL); 669 pthread_create(&spdk_tid, NULL, spdk_thread, g_dispatch_thread); 670 671 g_dev_buffer = calloc(1, DEV_BUFFER_SIZE); 672 673 CU_basic_set_mode(CU_BRM_VERBOSE); 674 CU_basic_run_tests(); 675 num_failures = CU_get_number_of_failures(); 676 CU_cleanup_registry(); 677 678 free(g_dev_buffer); 679 680 ut_send_request(terminate_spdk_thread, NULL); 681 pthread_join(spdk_tid, NULL); 682 683 while (spdk_thread_poll(g_dispatch_thread, 0, 0) > 0) {} 684 while (spdk_thread_poll(thread, 0, 0) > 0) {} 685 686 spdk_set_thread(thread); 687 spdk_thread_exit(thread); 688 while (!spdk_thread_is_exited(thread)) { 689 spdk_thread_poll(thread, 0, 0); 690 } 691 spdk_thread_destroy(thread); 692 693 spdk_set_thread(g_dispatch_thread); 694 spdk_thread_exit(g_dispatch_thread); 695 while (!spdk_thread_is_exited(g_dispatch_thread)) { 696 spdk_thread_poll(g_dispatch_thread, 0, 0); 697 } 698 spdk_thread_destroy(g_dispatch_thread); 699 700 spdk_thread_lib_fini(); 701 702 return num_failures; 703 } 704