xref: /spdk/test/unit/lib/scsi/lun.c/lun_ut.c (revision 8a0a98d35e21f282088edf28b9e8da66ec390e3a)
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_cunit.h"
37 
38 #include "scsi/task.c"
39 #include "scsi/lun.c"
40 
41 #include "spdk_internal/mock.h"
42 
43 /* Unit test bdev mockup */
44 struct spdk_bdev {
45 	int x;
46 };
47 
48 SPDK_LOG_REGISTER_COMPONENT("scsi", SPDK_LOG_SCSI)
49 
50 struct spdk_scsi_globals g_spdk_scsi;
51 
52 static bool g_lun_execute_fail = false;
53 static int g_lun_execute_status = SPDK_SCSI_TASK_PENDING;
54 static uint32_t g_task_count = 0;
55 
56 struct spdk_poller *
57 spdk_poller_register(spdk_poller_fn fn,
58 		     void *arg,
59 		     uint64_t period_microseconds)
60 {
61 	return NULL;
62 }
63 
64 void
65 spdk_poller_unregister(struct spdk_poller **ppoller)
66 {
67 }
68 
69 void
70 spdk_thread_send_msg(const struct spdk_thread *thread, spdk_thread_fn fn, void *ctx)
71 {
72 }
73 
74 void spdk_trace_record(uint16_t tpoint_id, uint16_t poller_id, uint32_t size,
75 		       uint64_t object_id, uint64_t arg1)
76 {
77 }
78 
79 static void
80 spdk_lun_ut_cpl_task(struct spdk_scsi_task *task)
81 {
82 	SPDK_CU_ASSERT_FATAL(g_task_count > 0);
83 	g_task_count--;
84 }
85 
86 static void
87 spdk_lun_ut_free_task(struct spdk_scsi_task *task)
88 {
89 }
90 
91 static void
92 ut_init_task(struct spdk_scsi_task *task)
93 {
94 	memset(task, 0, sizeof(*task));
95 	spdk_scsi_task_construct(task, spdk_lun_ut_cpl_task,
96 				 spdk_lun_ut_free_task);
97 	g_task_count++;
98 }
99 
100 void *
101 spdk_dma_malloc(size_t size, size_t align, uint64_t *phys_addr)
102 {
103 	void *buf = malloc(size);
104 	if (phys_addr) {
105 		*phys_addr = (uint64_t)buf;
106 	}
107 	return buf;
108 }
109 
110 void *
111 spdk_dma_zmalloc(size_t size, size_t align, uint64_t *phys_addr)
112 {
113 	void *buf = calloc(size, 1);
114 	if (phys_addr) {
115 		*phys_addr = (uint64_t)buf;
116 	}
117 	return buf;
118 }
119 
120 void
121 spdk_dma_free(void *buf)
122 {
123 	free(buf);
124 }
125 
126 void
127 spdk_bdev_free_io(struct spdk_bdev_io *bdev_io)
128 {
129 	CU_ASSERT(0);
130 }
131 
132 int
133 spdk_bdev_open(struct spdk_bdev *bdev, bool write, spdk_bdev_remove_cb_t remove_cb,
134 	       void *remove_ctx, struct spdk_bdev_desc **desc)
135 {
136 	return 0;
137 }
138 
139 void
140 spdk_bdev_close(struct spdk_bdev_desc *desc)
141 {
142 }
143 
144 const char *
145 spdk_bdev_get_name(const struct spdk_bdev *bdev)
146 {
147 	return "test";
148 }
149 
150 void spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev,
151 				   struct spdk_scsi_task *task,
152 				   enum spdk_scsi_task_func func)
153 {
154 }
155 
156 void spdk_scsi_dev_delete_lun(struct spdk_scsi_dev *dev,
157 			      struct spdk_scsi_lun *lun)
158 {
159 	return;
160 }
161 
162 int
163 spdk_bdev_scsi_reset(struct spdk_scsi_task *task)
164 {
165 	return 0;
166 }
167 
168 int
169 spdk_bdev_scsi_execute(struct spdk_scsi_task *task)
170 {
171 	if (g_lun_execute_fail) {
172 		return -EINVAL;
173 	} else {
174 		task->status = SPDK_SCSI_STATUS_GOOD;
175 
176 		if (g_lun_execute_status == SPDK_SCSI_TASK_PENDING) {
177 			return g_lun_execute_status;
178 		} else if (g_lun_execute_status == SPDK_SCSI_TASK_COMPLETE) {
179 			return g_lun_execute_status;
180 		} else {
181 			return 0;
182 		}
183 	}
184 }
185 
186 struct spdk_io_channel *
187 spdk_bdev_get_io_channel(struct spdk_bdev_desc *desc)
188 {
189 	return NULL;
190 }
191 
192 void
193 spdk_put_io_channel(struct spdk_io_channel *ch)
194 {
195 }
196 
197 DEFINE_STUB(spdk_io_channel_get_thread, struct spdk_thread *, (struct spdk_io_channel *ch), NULL)
198 DEFINE_STUB(spdk_get_thread, struct spdk_thread *, (void), NULL)
199 
200 static _spdk_scsi_lun *
201 lun_construct(void)
202 {
203 	struct spdk_scsi_lun		*lun;
204 	struct spdk_bdev		bdev;
205 
206 	lun = spdk_scsi_lun_construct(&bdev, NULL, NULL);
207 
208 	SPDK_CU_ASSERT_FATAL(lun != NULL);
209 	return lun;
210 }
211 
212 static void
213 lun_destruct(struct spdk_scsi_lun *lun)
214 {
215 	/* LUN will defer its removal if there are any unfinished tasks */
216 	SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&lun->tasks));
217 
218 	spdk_scsi_lun_destruct(lun);
219 }
220 
221 static void
222 lun_task_mgmt_execute_null_task(void)
223 {
224 	int rc;
225 
226 	rc = spdk_scsi_lun_task_mgmt_execute(NULL, SPDK_SCSI_TASK_FUNC_ABORT_TASK);
227 
228 	/* returns -1 since we passed NULL for the task */
229 	CU_ASSERT_TRUE(rc < 0);
230 	CU_ASSERT_EQUAL(g_task_count, 0);
231 }
232 
233 static void
234 lun_task_mgmt_execute_abort_task_null_lun_failure(void)
235 {
236 	struct spdk_scsi_task mgmt_task = { 0 };
237 	struct spdk_scsi_port initiator_port = { 0 };
238 	int rc;
239 
240 	ut_init_task(&mgmt_task);
241 	mgmt_task.lun = NULL;
242 	mgmt_task.initiator_port = &initiator_port;
243 
244 	rc = spdk_scsi_lun_task_mgmt_execute(&mgmt_task, SPDK_SCSI_TASK_FUNC_ABORT_TASK);
245 
246 	/* returns -1 since we passed NULL for LUN */
247 	CU_ASSERT_TRUE(rc < 0);
248 	CU_ASSERT_EQUAL(g_task_count, 0);
249 }
250 
251 static void
252 lun_task_mgmt_execute_abort_task_not_supported(void)
253 {
254 	struct spdk_scsi_lun *lun;
255 	struct spdk_scsi_task task = { 0 };
256 	struct spdk_scsi_task mgmt_task = { 0 };
257 	struct spdk_scsi_port initiator_port = { 0 };
258 	struct spdk_scsi_dev dev = { 0 };
259 	uint8_t cdb[6] = { 0 };
260 	int rc;
261 
262 	lun = lun_construct();
263 	lun->dev = &dev;
264 
265 	ut_init_task(&mgmt_task);
266 	mgmt_task.lun = lun;
267 	mgmt_task.initiator_port = &initiator_port;
268 
269 	/* Params to add regular task to the lun->tasks */
270 	ut_init_task(&task);
271 	task.lun = lun;
272 	task.cdb = cdb;
273 
274 	spdk_scsi_lun_execute_task(lun, &task);
275 
276 	/* task should now be on the tasks list */
277 	CU_ASSERT(!TAILQ_EMPTY(&lun->tasks));
278 
279 	rc = spdk_scsi_lun_task_mgmt_execute(&mgmt_task, SPDK_SCSI_TASK_FUNC_ABORT_TASK);
280 
281 	/* returns -1 since task abort is not supported */
282 	CU_ASSERT_TRUE(rc < 0);
283 	CU_ASSERT(mgmt_task.response == SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED);
284 
285 	/* task is still on the tasks list */
286 	CU_ASSERT_EQUAL(g_task_count, 1);
287 
288 	spdk_scsi_lun_complete_task(lun, &task);
289 	CU_ASSERT_EQUAL(g_task_count, 0);
290 
291 	lun_destruct(lun);
292 }
293 
294 static void
295 lun_task_mgmt_execute_abort_task_all_null_lun_failure(void)
296 {
297 	struct spdk_scsi_task mgmt_task = { 0 };
298 	struct spdk_scsi_port initiator_port = { 0 };
299 	int rc;
300 
301 	ut_init_task(&mgmt_task);
302 	mgmt_task.lun = NULL;
303 	mgmt_task.initiator_port = &initiator_port;
304 
305 	rc = spdk_scsi_lun_task_mgmt_execute(&mgmt_task, SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET);
306 
307 	/* Returns -1 since we passed NULL for lun */
308 	CU_ASSERT_TRUE(rc < 0);
309 
310 	CU_ASSERT_EQUAL(g_task_count, 0);
311 }
312 
313 static void
314 lun_task_mgmt_execute_abort_task_all_not_supported(void)
315 {
316 	struct spdk_scsi_lun *lun;
317 	struct spdk_scsi_task task = { 0 };
318 	struct spdk_scsi_task mgmt_task = { 0 };
319 	struct spdk_scsi_port initiator_port = { 0 };
320 	struct spdk_scsi_dev dev = { 0 };
321 	int rc;
322 	uint8_t cdb[6] = { 0 };
323 
324 	lun = lun_construct();
325 	lun->dev = &dev;
326 
327 	ut_init_task(&mgmt_task);
328 	mgmt_task.lun = lun;
329 	mgmt_task.initiator_port = &initiator_port;
330 
331 	/* Params to add regular task to the lun->tasks */
332 	ut_init_task(&task);
333 	task.initiator_port = &initiator_port;
334 	task.lun = lun;
335 	task.cdb = cdb;
336 
337 	spdk_scsi_lun_execute_task(lun, &task);
338 
339 	/* task should now be on the tasks list */
340 	CU_ASSERT(!TAILQ_EMPTY(&lun->tasks));
341 
342 	rc = spdk_scsi_lun_task_mgmt_execute(&mgmt_task, SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET);
343 
344 	/* returns -1 since task abort is not supported */
345 	CU_ASSERT_TRUE(rc < 0);
346 	CU_ASSERT(mgmt_task.response == SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED);
347 
348 	/* task is still on the tasks list */
349 	CU_ASSERT_EQUAL(g_task_count, 1);
350 
351 	spdk_scsi_lun_complete_task(lun, &task);
352 
353 	CU_ASSERT_EQUAL(g_task_count, 0);
354 
355 	lun_destruct(lun);
356 }
357 
358 static void
359 lun_task_mgmt_execute_lun_reset_failure(void)
360 {
361 	struct spdk_scsi_task mgmt_task = { 0 };
362 	int rc;
363 
364 	ut_init_task(&mgmt_task);
365 	mgmt_task.lun = NULL;
366 
367 	rc = spdk_scsi_lun_task_mgmt_execute(&mgmt_task, SPDK_SCSI_TASK_FUNC_LUN_RESET);
368 
369 	/* Returns -1 since we passed NULL for lun */
370 	CU_ASSERT_TRUE(rc < 0);
371 
372 	CU_ASSERT_EQUAL(g_task_count, 0);
373 }
374 
375 static void
376 lun_task_mgmt_execute_lun_reset(void)
377 {
378 	struct spdk_scsi_lun *lun;
379 	struct spdk_scsi_task mgmt_task = { 0 };
380 	struct spdk_scsi_dev dev = { 0 };
381 	int rc;
382 
383 	lun = lun_construct();
384 	lun->dev = &dev;
385 
386 	ut_init_task(&mgmt_task);
387 	mgmt_task.lun = lun;
388 
389 	rc = spdk_scsi_lun_task_mgmt_execute(&mgmt_task, SPDK_SCSI_TASK_FUNC_LUN_RESET);
390 
391 	/* Returns success */
392 	CU_ASSERT_EQUAL(rc, 0);
393 
394 	lun_destruct(lun);
395 
396 	/* task is still on the tasks list */
397 	CU_ASSERT_EQUAL(g_task_count, 1);
398 	g_task_count = 0;
399 }
400 
401 static void
402 lun_task_mgmt_execute_invalid_case(void)
403 {
404 	struct spdk_scsi_lun *lun;
405 	struct spdk_scsi_task mgmt_task = { 0 };
406 	struct spdk_scsi_dev dev = { 0 };
407 	int rc;
408 
409 	lun = lun_construct();
410 	lun->dev = &dev;
411 
412 	ut_init_task(&mgmt_task);
413 	/* Pass an invalid value to the switch statement */
414 	rc = spdk_scsi_lun_task_mgmt_execute(&mgmt_task, 5);
415 
416 	/* Returns -1 on passing an invalid value to the switch case */
417 	CU_ASSERT_TRUE(rc < 0);
418 
419 	lun_destruct(lun);
420 
421 	CU_ASSERT_EQUAL(g_task_count, 0);
422 }
423 
424 static void
425 lun_append_task_null_lun_task_cdb_spc_inquiry(void)
426 {
427 	struct spdk_scsi_task task = { 0 };
428 	uint8_t cdb[6] = { 0 };
429 
430 	ut_init_task(&task);
431 	task.cdb = cdb;
432 	task.cdb[0] = SPDK_SPC_INQUIRY;
433 	/* alloc_len >= 4096 */
434 	task.cdb[3] = 0xFF;
435 	task.cdb[4] = 0xFF;
436 	task.lun = NULL;
437 
438 	spdk_scsi_task_process_null_lun(&task);
439 
440 	CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_GOOD);
441 
442 	spdk_scsi_task_put(&task);
443 
444 	/* spdk_scsi_task_process_null_lun() does not call cpl_fn */
445 	CU_ASSERT_EQUAL(g_task_count, 1);
446 	g_task_count = 0;
447 }
448 
449 static void
450 lun_append_task_null_lun_alloc_len_lt_4096(void)
451 {
452 	struct spdk_scsi_task task = { 0 };
453 	uint8_t cdb[6] = { 0 };
454 
455 	ut_init_task(&task);
456 	task.cdb = cdb;
457 	task.cdb[0] = SPDK_SPC_INQUIRY;
458 	/* alloc_len < 4096 */
459 	task.cdb[3] = 0;
460 	task.cdb[4] = 0;
461 	/* alloc_len is set to a minimal value of 4096
462 	 * Hence, buf of size 4096 is allocated */
463 	spdk_scsi_task_process_null_lun(&task);
464 
465 	CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_GOOD);
466 
467 	spdk_scsi_task_put(&task);
468 
469 	/* spdk_scsi_task_process_null_lun() does not call cpl_fn */
470 	CU_ASSERT_EQUAL(g_task_count, 1);
471 	g_task_count = 0;
472 }
473 
474 static void
475 lun_append_task_null_lun_not_supported(void)
476 {
477 	struct spdk_scsi_task task = { 0 };
478 	uint8_t cdb[6] = { 0 };
479 
480 	ut_init_task(&task);
481 	task.cdb = cdb;
482 	task.lun = NULL;
483 
484 	spdk_scsi_task_process_null_lun(&task);
485 
486 	CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_CHECK_CONDITION);
487 	/* LUN not supported; task's data transferred should be 0 */
488 	CU_ASSERT_EQUAL(task.data_transferred, 0);
489 
490 	/* spdk_scsi_task_process_null_lun() does not call cpl_fn */
491 	CU_ASSERT_EQUAL(g_task_count, 1);
492 	g_task_count = 0;
493 }
494 
495 static void
496 lun_execute_scsi_task_pending(void)
497 {
498 	struct spdk_scsi_lun *lun;
499 	struct spdk_scsi_task task = { 0 };
500 	struct spdk_scsi_dev dev = { 0 };
501 
502 	lun = lun_construct();
503 
504 	ut_init_task(&task);
505 	task.lun = lun;
506 	lun->dev = &dev;
507 
508 	g_lun_execute_fail = false;
509 	g_lun_execute_status = SPDK_SCSI_TASK_PENDING;
510 
511 	/* the tasks list should still be empty since it has not been
512 	   executed yet
513 	 */
514 	CU_ASSERT(TAILQ_EMPTY(&lun->tasks));
515 
516 	spdk_scsi_lun_execute_task(lun, &task);
517 
518 	/* Assert the task has been successfully added to the tasks queue */
519 	CU_ASSERT(!TAILQ_EMPTY(&lun->tasks));
520 
521 	/* task is still on the tasks list */
522 	CU_ASSERT_EQUAL(g_task_count, 1);
523 
524 	/* Need to complete task so LUN might be removed right now */
525 	spdk_scsi_lun_complete_task(lun, &task);
526 
527 	CU_ASSERT_EQUAL(g_task_count, 0);
528 
529 	lun_destruct(lun);
530 }
531 
532 static void
533 lun_execute_scsi_task_complete(void)
534 {
535 	struct spdk_scsi_lun *lun;
536 	struct spdk_scsi_task task = { 0 };
537 	struct spdk_scsi_dev dev = { 0 };
538 
539 	lun = lun_construct();
540 
541 	ut_init_task(&task);
542 	task.lun = lun;
543 	lun->dev = &dev;
544 
545 	g_lun_execute_fail = false;
546 	g_lun_execute_status = SPDK_SCSI_TASK_COMPLETE;
547 
548 	/* the tasks list should still be empty since it has not been
549 	   executed yet
550 	 */
551 	CU_ASSERT(TAILQ_EMPTY(&lun->tasks));
552 
553 	spdk_scsi_lun_execute_task(lun, &task);
554 
555 	/* Assert the task has not been added to the tasks queue */
556 	CU_ASSERT(TAILQ_EMPTY(&lun->tasks));
557 
558 	lun_destruct(lun);
559 
560 	CU_ASSERT_EQUAL(g_task_count, 0);
561 }
562 
563 static void
564 lun_destruct_success(void)
565 {
566 	struct spdk_scsi_lun *lun;
567 
568 	lun = lun_construct();
569 
570 	spdk_scsi_lun_destruct(lun);
571 
572 	CU_ASSERT_EQUAL(g_task_count, 0);
573 }
574 
575 static void
576 lun_construct_null_ctx(void)
577 {
578 	struct spdk_scsi_lun		*lun;
579 
580 	lun = spdk_scsi_lun_construct(NULL, NULL, NULL);
581 
582 	/* lun should be NULL since we passed NULL for the ctx pointer. */
583 	CU_ASSERT(lun == NULL);
584 	CU_ASSERT_EQUAL(g_task_count, 0);
585 }
586 
587 static void
588 lun_construct_success(void)
589 {
590 	struct spdk_scsi_lun *lun = lun_construct();
591 
592 	lun_destruct(lun);
593 
594 	CU_ASSERT_EQUAL(g_task_count, 0);
595 }
596 
597 int
598 main(int argc, char **argv)
599 {
600 	CU_pSuite	suite = NULL;
601 	unsigned int	num_failures;
602 	int		rc;
603 
604 	if (CU_initialize_registry() != CUE_SUCCESS) {
605 		return CU_get_error();
606 	}
607 
608 	suite = CU_add_suite("lun_suite", NULL, NULL);
609 	if (suite == NULL) {
610 		CU_cleanup_registry();
611 		return CU_get_error();
612 	}
613 
614 	if (
615 		CU_add_test(suite, "task management - null task failure",
616 			    lun_task_mgmt_execute_null_task) == NULL
617 		|| CU_add_test(suite, "task management abort task - null lun failure",
618 			       lun_task_mgmt_execute_abort_task_null_lun_failure) == NULL
619 		|| CU_add_test(suite, "task management abort task - not supported",
620 			       lun_task_mgmt_execute_abort_task_not_supported) == NULL
621 		|| CU_add_test(suite, "task management abort task set - null lun failure",
622 			       lun_task_mgmt_execute_abort_task_all_null_lun_failure) == NULL
623 		|| CU_add_test(suite, "task management abort task set - success",
624 			       lun_task_mgmt_execute_abort_task_all_not_supported) == NULL
625 		|| CU_add_test(suite, "task management - lun reset failure",
626 			       lun_task_mgmt_execute_lun_reset_failure) == NULL
627 		|| CU_add_test(suite, "task management - lun reset success",
628 			       lun_task_mgmt_execute_lun_reset) == NULL
629 		|| CU_add_test(suite, "task management - invalid option",
630 			       lun_task_mgmt_execute_invalid_case) == NULL
631 		|| CU_add_test(suite, "append task - null lun SPDK_SPC_INQUIRY",
632 			       lun_append_task_null_lun_task_cdb_spc_inquiry) == NULL
633 		|| CU_add_test(suite, "append task - allocated length less than 4096",
634 			       lun_append_task_null_lun_alloc_len_lt_4096) == NULL
635 		|| CU_add_test(suite, "append task - unsupported lun",
636 			       lun_append_task_null_lun_not_supported) == NULL
637 		|| CU_add_test(suite, "execute task - scsi task pending",
638 			       lun_execute_scsi_task_pending) == NULL
639 		|| CU_add_test(suite, "execute task - scsi task complete",
640 			       lun_execute_scsi_task_complete) == NULL
641 		|| CU_add_test(suite, "destruct task - success", lun_destruct_success) == NULL
642 		|| CU_add_test(suite, "construct - null ctx", lun_construct_null_ctx) == NULL
643 		|| CU_add_test(suite, "construct - success", lun_construct_success) == NULL
644 	) {
645 		CU_cleanup_registry();
646 		return CU_get_error();
647 	}
648 
649 	CU_basic_set_mode(CU_BRM_VERBOSE);
650 	CU_basic_run_tests();
651 	num_failures = CU_get_number_of_failures();
652 
653 	if (argc > 1) {
654 		rc = spdk_cunit_print_results(argv[1]);
655 		if (rc != 0) {
656 			CU_cleanup_registry();
657 			return rc;
658 		}
659 	}
660 
661 	CU_cleanup_registry();
662 	return num_failures;
663 }
664