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