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_unload(void *arg) 145 { 146 struct spdk_thread *thread; 147 148 g_fserrno = -1; 149 spdk_fs_unload(g_fs, fs_op_complete, NULL); 150 thread = spdk_get_thread(); 151 while (spdk_thread_poll(thread, 0, 0) > 0) {} 152 CU_ASSERT(g_fserrno == 0); 153 g_fs = NULL; 154 } 155 156 static void 157 cache_read_after_write(void) 158 { 159 uint64_t length; 160 int rc; 161 char w_buf[100], r_buf[100]; 162 struct spdk_fs_thread_ctx *channel; 163 struct spdk_file_stat stat = {0}; 164 165 ut_send_request(_fs_init, NULL); 166 167 channel = spdk_fs_alloc_thread_ctx(g_fs); 168 169 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 170 CU_ASSERT(rc == 0); 171 SPDK_CU_ASSERT_FATAL(g_file != NULL); 172 173 length = (4 * 1024 * 1024); 174 rc = spdk_file_truncate(g_file, channel, length); 175 CU_ASSERT(rc == 0); 176 177 memset(w_buf, 0x5a, sizeof(w_buf)); 178 spdk_file_write(g_file, channel, w_buf, 0, sizeof(w_buf)); 179 180 CU_ASSERT(spdk_file_get_length(g_file) == length); 181 182 rc = spdk_file_truncate(g_file, channel, sizeof(w_buf)); 183 CU_ASSERT(rc == 0); 184 185 spdk_file_close(g_file, channel); 186 187 rc = spdk_fs_file_stat(g_fs, channel, "testfile", &stat); 188 CU_ASSERT(rc == 0); 189 CU_ASSERT(sizeof(w_buf) == stat.size); 190 191 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &g_file); 192 CU_ASSERT(rc == 0); 193 SPDK_CU_ASSERT_FATAL(g_file != NULL); 194 195 memset(r_buf, 0, sizeof(r_buf)); 196 spdk_file_read(g_file, channel, r_buf, 0, sizeof(r_buf)); 197 CU_ASSERT(memcmp(w_buf, r_buf, sizeof(r_buf)) == 0); 198 199 spdk_file_close(g_file, channel); 200 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 201 CU_ASSERT(rc == 0); 202 203 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 204 CU_ASSERT(rc == -ENOENT); 205 206 spdk_fs_free_thread_ctx(channel); 207 208 ut_send_request(_fs_unload, NULL); 209 } 210 211 static void 212 cache_write_null_buffer(void) 213 { 214 uint64_t length; 215 int rc; 216 struct spdk_fs_thread_ctx *channel; 217 struct spdk_thread *thread; 218 219 ut_send_request(_fs_init, NULL); 220 221 channel = spdk_fs_alloc_thread_ctx(g_fs); 222 223 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 224 CU_ASSERT(rc == 0); 225 SPDK_CU_ASSERT_FATAL(g_file != NULL); 226 227 length = 0; 228 rc = spdk_file_truncate(g_file, channel, length); 229 CU_ASSERT(rc == 0); 230 231 rc = spdk_file_write(g_file, channel, NULL, 0, 0); 232 CU_ASSERT(rc == 0); 233 234 spdk_file_close(g_file, channel); 235 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 236 CU_ASSERT(rc == 0); 237 238 spdk_fs_free_thread_ctx(channel); 239 240 thread = spdk_get_thread(); 241 while (spdk_thread_poll(thread, 0, 0) > 0) {} 242 243 ut_send_request(_fs_unload, NULL); 244 } 245 246 static void 247 fs_create_sync(void) 248 { 249 int rc; 250 struct spdk_fs_thread_ctx *channel; 251 struct spdk_thread *thread; 252 253 ut_send_request(_fs_init, NULL); 254 255 channel = spdk_fs_alloc_thread_ctx(g_fs); 256 CU_ASSERT(channel != NULL); 257 258 rc = spdk_fs_create_file(g_fs, channel, "testfile"); 259 CU_ASSERT(rc == 0); 260 261 /* Create should fail, because the file already exists. */ 262 rc = spdk_fs_create_file(g_fs, channel, "testfile"); 263 CU_ASSERT(rc != 0); 264 265 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 266 CU_ASSERT(rc == 0); 267 268 spdk_fs_free_thread_ctx(channel); 269 270 thread = spdk_get_thread(); 271 while (spdk_thread_poll(thread, 0, 0) > 0) {} 272 273 ut_send_request(_fs_unload, NULL); 274 } 275 276 static void 277 fs_rename_sync(void) 278 { 279 int rc; 280 struct spdk_fs_thread_ctx *channel; 281 struct spdk_thread *thread; 282 283 ut_send_request(_fs_init, NULL); 284 285 channel = spdk_fs_alloc_thread_ctx(g_fs); 286 CU_ASSERT(channel != NULL); 287 288 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 289 CU_ASSERT(rc == 0); 290 SPDK_CU_ASSERT_FATAL(g_file != NULL); 291 292 CU_ASSERT(strcmp(spdk_file_get_name(g_file), "testfile") == 0); 293 294 rc = spdk_fs_rename_file(g_fs, channel, "testfile", "newtestfile"); 295 CU_ASSERT(rc == 0); 296 CU_ASSERT(strcmp(spdk_file_get_name(g_file), "newtestfile") == 0); 297 298 spdk_file_close(g_file, channel); 299 spdk_fs_free_thread_ctx(channel); 300 301 thread = spdk_get_thread(); 302 while (spdk_thread_poll(thread, 0, 0) > 0) {} 303 304 ut_send_request(_fs_unload, NULL); 305 } 306 307 static void 308 cache_append_no_cache(void) 309 { 310 int rc; 311 char buf[100]; 312 struct spdk_fs_thread_ctx *channel; 313 struct spdk_thread *thread; 314 315 ut_send_request(_fs_init, NULL); 316 317 channel = spdk_fs_alloc_thread_ctx(g_fs); 318 319 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 320 CU_ASSERT(rc == 0); 321 SPDK_CU_ASSERT_FATAL(g_file != NULL); 322 323 spdk_file_write(g_file, channel, buf, 0 * sizeof(buf), sizeof(buf)); 324 CU_ASSERT(spdk_file_get_length(g_file) == 1 * sizeof(buf)); 325 spdk_file_write(g_file, channel, buf, 1 * sizeof(buf), sizeof(buf)); 326 CU_ASSERT(spdk_file_get_length(g_file) == 2 * sizeof(buf)); 327 spdk_file_sync(g_file, channel); 328 cache_free_buffers(g_file); 329 spdk_file_write(g_file, channel, buf, 2 * sizeof(buf), sizeof(buf)); 330 CU_ASSERT(spdk_file_get_length(g_file) == 3 * sizeof(buf)); 331 spdk_file_write(g_file, channel, buf, 3 * sizeof(buf), sizeof(buf)); 332 CU_ASSERT(spdk_file_get_length(g_file) == 4 * sizeof(buf)); 333 spdk_file_write(g_file, channel, buf, 4 * sizeof(buf), sizeof(buf)); 334 CU_ASSERT(spdk_file_get_length(g_file) == 5 * sizeof(buf)); 335 336 spdk_file_close(g_file, channel); 337 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 338 CU_ASSERT(rc == 0); 339 340 spdk_fs_free_thread_ctx(channel); 341 342 thread = spdk_get_thread(); 343 while (spdk_thread_poll(thread, 0, 0) > 0) {} 344 345 ut_send_request(_fs_unload, NULL); 346 } 347 348 static void 349 fs_delete_file_without_close(void) 350 { 351 int rc; 352 struct spdk_fs_thread_ctx *channel; 353 struct spdk_file *file; 354 struct spdk_thread *thread; 355 356 ut_send_request(_fs_init, NULL); 357 channel = spdk_fs_alloc_thread_ctx(g_fs); 358 CU_ASSERT(channel != NULL); 359 360 rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file); 361 CU_ASSERT(rc == 0); 362 SPDK_CU_ASSERT_FATAL(g_file != NULL); 363 364 rc = spdk_fs_delete_file(g_fs, channel, "testfile"); 365 CU_ASSERT(rc == 0); 366 CU_ASSERT(g_file->ref_count != 0); 367 CU_ASSERT(g_file->is_deleted == true); 368 369 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &file); 370 CU_ASSERT(rc != 0); 371 372 spdk_file_close(g_file, channel); 373 374 rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &file); 375 CU_ASSERT(rc != 0); 376 377 spdk_fs_free_thread_ctx(channel); 378 379 thread = spdk_get_thread(); 380 while (spdk_thread_poll(thread, 0, 0) > 0) {} 381 382 ut_send_request(_fs_unload, NULL); 383 384 } 385 386 static bool g_thread_exit = false; 387 388 static void 389 terminate_spdk_thread(void *arg) 390 { 391 g_thread_exit = true; 392 } 393 394 static void * 395 spdk_thread(void *arg) 396 { 397 struct spdk_thread *thread = arg; 398 399 spdk_set_thread(thread); 400 401 while (!g_thread_exit) { 402 spdk_thread_poll(thread, 0, 0); 403 } 404 405 return NULL; 406 } 407 408 int main(int argc, char **argv) 409 { 410 struct spdk_thread *thread; 411 CU_pSuite suite = NULL; 412 pthread_t spdk_tid; 413 unsigned int num_failures; 414 415 if (CU_initialize_registry() != CUE_SUCCESS) { 416 return CU_get_error(); 417 } 418 419 suite = CU_add_suite("blobfs_sync_ut", NULL, NULL); 420 if (suite == NULL) { 421 CU_cleanup_registry(); 422 return CU_get_error(); 423 } 424 425 if ( 426 CU_add_test(suite, "cache read after write", cache_read_after_write) == NULL || 427 CU_add_test(suite, "write_null_buffer", cache_write_null_buffer) == NULL || 428 CU_add_test(suite, "create_sync", fs_create_sync) == NULL || 429 CU_add_test(suite, "rename_sync", fs_rename_sync) == NULL || 430 CU_add_test(suite, "append_no_cache", cache_append_no_cache) == NULL || 431 CU_add_test(suite, "delete_file_without_close", fs_delete_file_without_close) == NULL 432 ) { 433 CU_cleanup_registry(); 434 return CU_get_error(); 435 } 436 437 spdk_thread_lib_init(NULL, 0); 438 439 thread = spdk_thread_create("test_thread", NULL); 440 spdk_set_thread(thread); 441 442 g_dispatch_thread = spdk_thread_create("dispatch_thread", NULL); 443 pthread_create(&spdk_tid, NULL, spdk_thread, g_dispatch_thread); 444 445 g_dev_buffer = calloc(1, DEV_BUFFER_SIZE); 446 447 CU_basic_set_mode(CU_BRM_VERBOSE); 448 CU_basic_run_tests(); 449 num_failures = CU_get_number_of_failures(); 450 CU_cleanup_registry(); 451 452 free(g_dev_buffer); 453 454 ut_send_request(terminate_spdk_thread, NULL); 455 pthread_join(spdk_tid, NULL); 456 457 while (spdk_thread_poll(g_dispatch_thread, 0, 0) > 0) {} 458 while (spdk_thread_poll(thread, 0, 0) > 0) {} 459 460 spdk_set_thread(thread); 461 spdk_thread_exit(thread); 462 spdk_thread_destroy(thread); 463 464 spdk_set_thread(g_dispatch_thread); 465 spdk_thread_exit(g_dispatch_thread); 466 spdk_thread_destroy(g_dispatch_thread); 467 468 spdk_thread_lib_fini(); 469 470 return num_failures; 471 } 472