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