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