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