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