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