xref: /spdk/test/unit/lib/ftl/ftl_mngt/ftl_mngt_ut.c (revision 60982c759db49b4f4579f16e3b24df0725ba4b94)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2022 Intel Corporation.
3  *   All rights reserved.
4  */
5 
6 #include <sys/queue.h>
7 
8 #include "spdk/stdinc.h"
9 
10 #include "spdk_internal/cunit.h"
11 #include "common/lib/test_env.c"
12 
13 #include "ftl/mngt/ftl_mngt.c"
14 
15 #define CALLER_CB_RET_VALUE 999
16 
17 /* list for structure with results of tests from callbacks */
18 struct entry {
19 	int data;
20 	TAILQ_ENTRY(entry) entries;
21 };
22 
23 TAILQ_HEAD(listhead, entry) g_head;
24 
25 struct thread_send_msg_container {
26 	spdk_msg_fn fn;
27 	void *ctx;
28 } g_thread_send_msg_container;
29 
30 struct spdk_ftl_dev g_dev;
31 
32 int
33 spdk_thread_send_msg(const struct spdk_thread *thread, spdk_msg_fn fn, void *ctx)
34 {
35 	g_thread_send_msg_container.fn = fn;
36 	g_thread_send_msg_container.ctx = ctx;
37 	return 0;
38 }
39 
40 struct spdk_thread *
41 spdk_get_thread(void)
42 {
43 	struct spdk_thread *thd = (struct spdk_thread *)0x1;
44 	return thd;
45 }
46 
47 static int
48 setup_test_list(void)
49 {
50 	TAILQ_INIT(&g_head);
51 	return 0;
52 }
53 
54 static void
55 check_list_empty(void)
56 {
57 	CU_ASSERT_TRUE(TAILQ_EMPTY(&g_head));
58 }
59 
60 static void
61 add_elem_to_test_list(int data)
62 {
63 	struct entry *en = calloc(1, sizeof(*en));
64 	SPDK_CU_ASSERT_FATAL(en != NULL);
65 	en->data = data;
66 	TAILQ_INSERT_TAIL(&g_head, en, entries);
67 }
68 
69 static void
70 check_elem_on_list_and_remove(int compared_elem)
71 {
72 	struct entry *en = TAILQ_FIRST(&g_head);
73 	if (en != NULL) {
74 		CU_ASSERT_EQUAL(en->data, compared_elem);
75 		TAILQ_REMOVE(&g_head, en, entries);
76 		free(en);
77 	} else {
78 		CU_FAIL("not null value was expected");
79 	}
80 }
81 
82 static void
83 fn_finish(struct spdk_ftl_dev *dev, void *ctx, int status)
84 {
85 	add_elem_to_test_list(CALLER_CB_RET_VALUE);
86 	g_thread_send_msg_container.fn = NULL;
87 	g_thread_send_msg_container.ctx = NULL;
88 }
89 
90 typedef int (*ftl_execute_fn)(struct spdk_ftl_dev *dev,
91 			      const struct ftl_mngt_process_desc *process,
92 			      ftl_mngt_completion cb, void *cb_cntx);
93 
94 static void
95 run_ftl_mngt_with_cb_cntx(ftl_execute_fn exec_fn,
96 			  const struct ftl_mngt_process_desc *process, void *cb_cntx)
97 {
98 	int result = exec_fn(&g_dev, process, fn_finish, cb_cntx);
99 	CU_ASSERT_EQUAL(result, 0);
100 	while (g_thread_send_msg_container.fn != NULL) {
101 		g_thread_send_msg_container.fn(g_thread_send_msg_container.ctx);
102 	}
103 }
104 
105 static void
106 run_ftl_mngt(ftl_execute_fn exec_fn,
107 	     const struct ftl_mngt_process_desc *process)
108 {
109 	run_ftl_mngt_with_cb_cntx(exec_fn, process, NULL);
110 }
111 
112 /*-
113  * test 1
114  * tests simple invoking next steps
115  * it is shown if ftl_mngt_process_execute and ftl_mngt_process_rollback invoke functions in proper order
116  * (functions call only ftl_mngt_next_step)
117  */
118 
119 static void
120 fn_1_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
121 {
122 	add_elem_to_test_list(1);
123 	ftl_mngt_next_step(mngt);
124 }
125 
126 static void
127 fn_1_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
128 {
129 	add_elem_to_test_list(-1);
130 	ftl_mngt_next_step(mngt);
131 }
132 
133 static void
134 fn_1_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
135 {
136 	add_elem_to_test_list(2);
137 	ftl_mngt_next_step(mngt);
138 }
139 
140 static void
141 fn_1_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
142 {
143 	add_elem_to_test_list(3);
144 	ftl_mngt_next_step(mngt);
145 }
146 
147 static void
148 fn_1_3_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
149 {
150 	add_elem_to_test_list(-3);
151 	ftl_mngt_next_step(mngt);
152 }
153 
154 static struct ftl_mngt_process_desc pdesc_test_1 = {
155 	.name = "process 1",
156 	.steps = {
157 		{
158 			.name = "step 1",
159 			.action = fn_1_1_action,
160 			.cleanup = fn_1_1_cleanup
161 		},
162 		{
163 			.name = "step 2",
164 			.action = fn_1_2_action
165 		},
166 		{
167 			.name = "step 3",
168 			.action = fn_1_3_action,
169 			.cleanup = fn_1_3_cleanup
170 		},
171 		{}
172 	}
173 };
174 
175 static void
176 test_next_step(void)
177 {
178 	run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_1);
179 
180 	/* check proper order of action functions */
181 	for (int i = 1; i <= 3; i++) {
182 		check_elem_on_list_and_remove(i);
183 	}
184 
185 	/* check if caller callback was invoked */
186 	check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
187 
188 	run_ftl_mngt(ftl_mngt_process_rollback, &pdesc_test_1);
189 
190 	/* Check proper order of cleanup functions.
191 	 * Cleanup functions add to list opposite values to action functions.
192 	 * Cleanup functions are invoked in reverse order,
193 	 * moreover action 2 does not have cleanup,
194 	 * so expected values are -3, then -1 */
195 	check_elem_on_list_and_remove(-3);
196 	check_elem_on_list_and_remove(-1);
197 
198 	/* check if caller callback was invoked */
199 	check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
200 
201 	check_list_empty();
202 }
203 
204 /*-
205  * test 2
206  * tests action and cleanup function which invoke
207  * ftl_mngt_continue_step function
208  */
209 
210 static void
211 fn_2_common_part(struct ftl_mngt_process *mngt, int elem)
212 {
213 	struct entry *en = TAILQ_LAST(&g_head, listhead);
214 
215 	if (en == NULL || en->data != elem) {
216 		/* if function was invoked 1st time, make it once again */
217 		add_elem_to_test_list(elem);
218 		ftl_mngt_continue_step(mngt);
219 	} else {
220 		/* otherwise go to the next function */
221 		add_elem_to_test_list(elem);
222 		ftl_mngt_next_step(mngt);
223 	}
224 }
225 
226 static void
227 fn_2_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
228 {
229 	fn_2_common_part(mngt, 1);
230 }
231 
232 static void
233 fn_2_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
234 {
235 	fn_2_common_part(mngt, -1);
236 }
237 
238 static void
239 fn_2_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
240 {
241 	fn_2_common_part(mngt, 2);
242 }
243 
244 static void
245 fn_2_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
246 {
247 	fn_2_common_part(mngt, -2);
248 }
249 
250 static struct ftl_mngt_process_desc pdesc_test_2 = {
251 	.name = "process 2",
252 	.steps = {
253 		{
254 			.name = "step 1",
255 			.action = fn_2_1_action,
256 			.cleanup = fn_2_1_cleanup
257 		},
258 		{
259 			.name = "step 2",
260 			.action = fn_2_2_action,
261 			.cleanup = fn_2_2_cleanup
262 		},
263 		{}
264 	}
265 };
266 
267 static void
268 test_continue_step(void)
269 {
270 	run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_2);
271 
272 	/* check proper order of action functions */
273 	check_elem_on_list_and_remove(1);
274 	check_elem_on_list_and_remove(1);
275 	check_elem_on_list_and_remove(2);
276 	check_elem_on_list_and_remove(2);
277 
278 	/* check if caller callback was invoked */
279 	check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
280 
281 	run_ftl_mngt(ftl_mngt_process_rollback, &pdesc_test_2);
282 
283 	/* check proper order of action functions */
284 	check_elem_on_list_and_remove(-2);
285 	check_elem_on_list_and_remove(-2);
286 	check_elem_on_list_and_remove(-1);
287 	check_elem_on_list_and_remove(-1);
288 
289 	/* check if caller callback was invoked */
290 	check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
291 
292 	check_list_empty();
293 }
294 
295 /*-
296  * test 3
297  * tests ftl_mngt_alloc_step_cntx and all ftl_mngt_get functions
298  */
299 
300 const int PROCESS_CNTX_TEST_VAL_0 = 21;
301 const int PROCESS_CNTX_TEST_VAL_1 = 37;
302 const int STEP_CNTX_TEST_VAL = 1;
303 
304 static void
305 put_on_list(void)
306 {
307 	struct entry *en = calloc(1, sizeof(*en));
308 	SPDK_CU_ASSERT_FATAL(en != NULL);
309 	TAILQ_INSERT_TAIL(&g_head, en, entries);
310 }
311 
312 static bool
313 check_if_list_empty_and_clean(void)
314 {
315 	struct entry *en = TAILQ_FIRST(&g_head);
316 	if (en == NULL) {
317 		return true;
318 	} else {
319 		TAILQ_REMOVE(&g_head, en, entries);
320 		free(en);
321 		return false;
322 	}
323 }
324 
325 static void
326 fn_3_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
327 {
328 	int *step_cntx_ptr, *process_cntx_ptr;
329 	char *caller_cntx_ptr;
330 	int status;
331 
332 	step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
333 	if (check_if_list_empty_and_clean()) {
334 		/* In 1st run of this function test list is empty
335 		 * and 'if' is true, this part of function is done.
336 		 * That 'if' part ends with ftl_mngt_continue_step,
337 		 * so function will be called once again.
338 		 * Element is added to the test list
339 		 * to invoke 'else' in second run */
340 		put_on_list();
341 		/* this step descriptor does not locate any context
342 		 * at the beginning,
343 		 * so pointer should contain NULL */
344 		CU_ASSERT_PTR_NULL(step_cntx_ptr);
345 
346 		status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr));
347 		SPDK_CU_ASSERT_FATAL(status == 0);
348 		step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
349 		/* now pointer should point to allocated context */
350 		CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
351 
352 		/* this value should be retrieved in second run of function
353 		 * (in 'else' part) */
354 		*step_cntx_ptr = STEP_CNTX_TEST_VAL;
355 
356 		ftl_mngt_continue_step(mngt);
357 	} else {
358 		/* In second run retrieved pointer is not empty.
359 		 * Moreover it should contain value allocated for this step
360 		 * in previous run of function */
361 		CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
362 		CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL);
363 
364 		/* check getting device */
365 		CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev);
366 		CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev);
367 
368 		/* tests for process context */
369 		process_cntx_ptr = ftl_mngt_get_process_ctx(mngt);
370 
371 		/* 1st get of process context, should be clear ('0' values) */
372 		CU_ASSERT_EQUAL(process_cntx_ptr[0], 0);
373 		CU_ASSERT_EQUAL(process_cntx_ptr[1], 0);
374 
375 		/* Random values put in process context.
376 		 * Should be retrieved in the next function
377 		 * (it is common space for the entire process) */
378 		process_cntx_ptr[0] = PROCESS_CNTX_TEST_VAL_0;
379 		process_cntx_ptr[1] = PROCESS_CNTX_TEST_VAL_1;
380 
381 		/* tests for caller context */
382 		caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt);
383 
384 		/* check previously located values */
385 		CU_ASSERT_EQUAL(caller_cntx_ptr[0], 'd');
386 		CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'a');
387 		CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'j');
388 
389 		/* insert new */
390 		caller_cntx_ptr[0] = ' ';
391 		caller_cntx_ptr[1] = 'k';
392 		caller_cntx_ptr[2] = 'a';
393 
394 		ftl_mngt_next_step(mngt);
395 	}
396 }
397 
398 static void
399 fn_3_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
400 {
401 	int *step_cntx_ptr, *process_cntx_ptr;
402 	char *caller_cntx_ptr;
403 	int status;
404 
405 	step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
406 	/* context of this step descriptor is never empty
407 	 * so pointer cannot contain NULL */
408 	CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
409 
410 	if (check_if_list_empty_and_clean()) {
411 		/* In 1st run of this function test list is empty
412 		 * and 'if' is true, this part of function is done.
413 		 * That 'if' part ends with ftl_mngt_continue_step,
414 		 * so function will be called once again.
415 		 * Element is added to the test list
416 		 * to invoke 'else' in second run */
417 		put_on_list();
418 
419 		/* check getting device */
420 		CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev);
421 		CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev);
422 
423 		/* tests for process context */
424 		process_cntx_ptr = ftl_mngt_get_process_ctx(mngt);
425 
426 		/* check if it is possible to retrieve values located
427 		 * in process context by previous function */
428 		CU_ASSERT_EQUAL(process_cntx_ptr[0], PROCESS_CNTX_TEST_VAL_0);
429 		CU_ASSERT_EQUAL(process_cntx_ptr[1], PROCESS_CNTX_TEST_VAL_1);
430 
431 		/* tests for caller context */
432 		caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt);
433 
434 		/* check previously located values */
435 		CU_ASSERT_EQUAL(caller_cntx_ptr[0], ' ');
436 		CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'k');
437 		CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'a');
438 
439 		/* insert new */
440 		caller_cntx_ptr[0] = 'm';
441 		caller_cntx_ptr[1] = 'i';
442 		caller_cntx_ptr[2] = 'e';
443 
444 		/* first run of step so reserved step context
445 		 * was never used before and should contain 0 */
446 		CU_ASSERT_EQUAL(*step_cntx_ptr, 0);
447 
448 		/* this value should be retrieved in second run of function
449 		 * (in 'else' part) */
450 		*step_cntx_ptr = STEP_CNTX_TEST_VAL;
451 
452 		ftl_mngt_continue_step(mngt);
453 	} else {
454 		/* In second run retrieved pointer should contain value
455 		 * allocated for this step in previous run of function */
456 		CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL);
457 
458 		status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr));
459 		SPDK_CU_ASSERT_FATAL(status == 0);
460 		step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
461 
462 		/* now pointer should point to newly allocated context
463 		 * and be cleaned up (should contain '0') */
464 		CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
465 		CU_ASSERT_EQUAL(*step_cntx_ptr, 0);
466 
467 		ftl_mngt_next_step(mngt);
468 	}
469 }
470 
471 static void
472 fn_3_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
473 {
474 	int *step_cntx_ptr, *process_cntx_ptr;
475 	char *caller_cntx_ptr;
476 	int status;
477 
478 	step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
479 	/* context of this step descriptor is never empty
480 	 * so pointer cannot contain NULL */
481 	CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
482 
483 	if (check_if_list_empty_and_clean()) {
484 		/* In 1st run of this function test list is empty
485 		 * and 'if' is true, this part of function is done.
486 		 * That 'if' part ends with ftl_mngt_continue_step,
487 		 * so function will be called once again.
488 		 * Element is added to the test list
489 		 * to invoke 'else' in second run */
490 		put_on_list();
491 
492 		/* first run of step so reserved step context
493 		 * was never used before and should contain 0 */
494 		CU_ASSERT_EQUAL(*step_cntx_ptr, 0);
495 
496 		/* this value should be retrieved in second run of function
497 		 * (in 'else' part) */
498 		*step_cntx_ptr = STEP_CNTX_TEST_VAL;
499 
500 		ftl_mngt_continue_step(mngt);
501 	} else {
502 		/* In second run retrieved pointer should contain value
503 		 * allocated for this step in previous run of function */
504 		CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL);
505 
506 		status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr));
507 		SPDK_CU_ASSERT_FATAL(status == 0);
508 		step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
509 
510 		/* now pointer should point to newly allocated context
511 		 * and be cleaned up (should contain '0') */
512 		CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
513 		CU_ASSERT_EQUAL(*step_cntx_ptr, 0);
514 
515 		/* check getting device */
516 		CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev);
517 		CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev);
518 
519 		/* tests for process context */
520 		process_cntx_ptr = ftl_mngt_get_process_ctx(mngt);
521 
522 		/* 1st get of process context, should be clear ('0' values) */
523 		CU_ASSERT_EQUAL(process_cntx_ptr[0], 0);
524 		CU_ASSERT_EQUAL(process_cntx_ptr[1], 0);
525 
526 		/* Random values put in process context.
527 		 * Should be retrieved in the next function
528 		 * (it is common space for the entire process) */
529 		process_cntx_ptr[0] = PROCESS_CNTX_TEST_VAL_0;
530 		process_cntx_ptr[1] = PROCESS_CNTX_TEST_VAL_1;
531 
532 		/* tests for caller context */
533 		caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt);
534 
535 		/* check previously located values */
536 		CU_ASSERT_EQUAL(caller_cntx_ptr[0], 'm');
537 		CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'i');
538 		CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'e');
539 
540 		/* insert new */
541 		caller_cntx_ptr[0] = 'n';
542 		caller_cntx_ptr[1] = 'i';
543 		caller_cntx_ptr[2] = 'a';
544 
545 		ftl_mngt_next_step(mngt);
546 	}
547 }
548 
549 static void
550 fn_3_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
551 {
552 	int *step_cntx_ptr, *process_cntx_ptr;
553 	char *caller_cntx_ptr;
554 	int status;
555 
556 	step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
557 	if (check_if_list_empty_and_clean()) {
558 		/* In 1st run of this function test list is empty
559 		 * and 'if' is true, this part of function is done.
560 		 * That 'if' part ends with ftl_mngt_continue_step,
561 		 * so function will be called once again.
562 		 * Element is added to the test list
563 		 * to invoke 'else' in second run */
564 		put_on_list();
565 		/* this step descriptor does not locate any context
566 		 * at the beginning,
567 		 * so pointer should contain NULL */
568 		CU_ASSERT_PTR_NULL(step_cntx_ptr);
569 
570 		/* check getting device */
571 		CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev);
572 		CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev);
573 
574 		/* tests for process context */
575 		process_cntx_ptr = ftl_mngt_get_process_ctx(mngt);
576 
577 		/* check if it is possible to retrieve values located
578 		 * in process context by previous function */
579 		CU_ASSERT_EQUAL(process_cntx_ptr[0], PROCESS_CNTX_TEST_VAL_0);
580 		CU_ASSERT_EQUAL(process_cntx_ptr[1], PROCESS_CNTX_TEST_VAL_1);
581 
582 		/* tests for caller context */
583 		caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt);
584 
585 		/* check previously located values */
586 		CU_ASSERT_EQUAL(caller_cntx_ptr[0], 'n');
587 		CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'i');
588 		CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'a');
589 
590 		/* insert new */
591 		caller_cntx_ptr[0] = '!';
592 		caller_cntx_ptr[1] = '!';
593 		caller_cntx_ptr[2] = '!';
594 
595 		status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr));
596 		SPDK_CU_ASSERT_FATAL(status == 0);
597 		step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
598 		/* now pointer should point to allocated context */
599 		CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
600 
601 		/* this value should be retrieved in second run of function
602 		 * (in 'else' part) */
603 		*step_cntx_ptr = STEP_CNTX_TEST_VAL;
604 
605 		ftl_mngt_continue_step(mngt);
606 	} else {
607 		/* In second run retrieved pointer is not empty.
608 		 * Moreover it should contain value allocated for this step
609 		 * in previous run of function */
610 		CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
611 		CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL);
612 
613 		ftl_mngt_next_step(mngt);
614 	}
615 }
616 
617 static struct ftl_mngt_process_desc pdesc_test_3 = {
618 	.name = "process 3",
619 	.ctx_size = 2 * sizeof(int),
620 	.steps = {
621 		{
622 			.name = "step 1",
623 			.action = fn_3_1_action,
624 			.cleanup = fn_3_1_cleanup
625 		},
626 		{
627 			.name = "step 2",
628 			.ctx_size = sizeof(int),
629 			.action = fn_3_2_action,
630 			.cleanup = fn_3_2_cleanup
631 		},
632 		{}
633 	}
634 };
635 
636 static void
637 test_get_func_and_step_cntx_alloc(void)
638 {
639 	char cb_cntx[4] = "daj";
640 
641 	run_ftl_mngt_with_cb_cntx(ftl_mngt_process_execute, &pdesc_test_3, cb_cntx);
642 
643 	/* check if caller callback was invoked */
644 	check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
645 
646 	/* check if steps changed cb_cntx correctly */
647 	CU_ASSERT_EQUAL(cb_cntx[0], 'm');
648 	CU_ASSERT_EQUAL(cb_cntx[1], 'i');
649 	CU_ASSERT_EQUAL(cb_cntx[2], 'e');
650 
651 	run_ftl_mngt_with_cb_cntx(ftl_mngt_process_rollback, &pdesc_test_3, cb_cntx);
652 
653 	/* check if caller callback was invoked */
654 	check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
655 
656 	/* check if steps changed cb_cntx correctly */
657 	CU_ASSERT_EQUAL(cb_cntx[0], '!');
658 	CU_ASSERT_EQUAL(cb_cntx[1], '!');
659 	CU_ASSERT_EQUAL(cb_cntx[2], '!');
660 
661 	check_list_empty();
662 }
663 
664 
665 
666 /*-
667  * test 4
668  * tests ftl_mngt_fail_step function
669  *
670  * In that test one of the action functions fails (third one).
671  * Because of that expected result (saved on the test result list)
672  * are numbers of the next action function up to failing function.
673  * After that cleanup functions are invoked in reversed order.
674  */
675 
676 static void
677 fn_4_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
678 {
679 	add_elem_to_test_list(1);
680 	ftl_mngt_next_step(mngt);
681 }
682 
683 static void
684 fn_4_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
685 {
686 	add_elem_to_test_list(-1);
687 	ftl_mngt_next_step(mngt);
688 }
689 
690 static void
691 fn_4_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
692 {
693 	add_elem_to_test_list(2);
694 	ftl_mngt_next_step(mngt);
695 }
696 
697 static void
698 fn_4_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
699 {
700 	add_elem_to_test_list(-2);
701 	ftl_mngt_next_step(mngt);
702 }
703 
704 static void
705 fn_4_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
706 {
707 	add_elem_to_test_list(3);
708 	/* this action fails, so cleanup should begin now */
709 	ftl_mngt_fail_step(mngt);
710 }
711 
712 static void
713 fn_4_3_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
714 {
715 	add_elem_to_test_list(-3);
716 	ftl_mngt_next_step(mngt);
717 }
718 
719 static void
720 fn_4_4_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
721 {
722 	CU_FAIL("failure cannot start another action");
723 	ftl_mngt_next_step(mngt);
724 }
725 
726 static struct ftl_mngt_process_desc pdesc_test_4 = {
727 	.name = "process 4",
728 	.steps = {
729 		{
730 			.name = "step 1",
731 			.action = fn_4_1_action,
732 			.cleanup = fn_4_1_cleanup
733 		},
734 		{
735 			.name = "step 2",
736 			.action = fn_4_2_action,
737 			.cleanup = fn_4_2_cleanup
738 		},
739 		{
740 			.name = "step 3",
741 			.action = fn_4_3_action,
742 			.cleanup = fn_4_3_cleanup
743 		},
744 		{
745 			.name = "step 2",
746 			.action = fn_4_4_action
747 		},
748 		{}
749 	}
750 };
751 
752 static void
753 test_fail_step(void)
754 {
755 	run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_4);
756 
757 	/* check proper order of action functions */
758 	for (int i = 1; i <= 3; i++) {
759 		check_elem_on_list_and_remove(i);
760 	}
761 
762 	/* 3rd action function fails, so now should be
763 	 * cleanup functions in reverse order */
764 	for (int i = 3; i > 0; i--) {
765 		check_elem_on_list_and_remove(-i);
766 	}
767 
768 	/* check if caller callback was invoked */
769 	check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
770 
771 	check_list_empty();
772 }
773 
774 /*-
775  * test 5
776  * tests ftl_mngt_call_process and ftl_mngt_call_process_rollback functions
777  * tests only proper flow without failures
778  */
779 
780 static void
781 fn_5_2_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
782 {
783 	add_elem_to_test_list(21);
784 	ftl_mngt_next_step(mngt);
785 }
786 
787 static void
788 fn_5_2_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
789 {
790 	add_elem_to_test_list(-21);
791 	ftl_mngt_next_step(mngt);
792 }
793 
794 static void
795 fn_5_2_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
796 {
797 	add_elem_to_test_list(22);
798 	ftl_mngt_next_step(mngt);
799 }
800 
801 static void
802 fn_5_2_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
803 {
804 	add_elem_to_test_list(-22);
805 	ftl_mngt_next_step(mngt);
806 }
807 
808 static struct ftl_mngt_process_desc pdesc_test_5_2 = {
809 	.name = "process nested inside step 2 from process 5",
810 	.steps = {
811 		{
812 			.name = "step 2_1",
813 			.action = fn_5_2_1_action,
814 			.cleanup = fn_5_2_1_cleanup
815 		},
816 		{
817 			.name = "step 2_2",
818 			.action = fn_5_2_2_action,
819 			.cleanup = fn_5_2_2_cleanup
820 		},
821 		{}
822 	}
823 };
824 
825 static void
826 fn_5_3_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
827 {
828 	add_elem_to_test_list(31);
829 	ftl_mngt_next_step(mngt);
830 }
831 
832 static void
833 fn_5_3_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
834 {
835 	add_elem_to_test_list(-31);
836 	ftl_mngt_next_step(mngt);
837 }
838 
839 static void
840 fn_5_3_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
841 {
842 	add_elem_to_test_list(32);
843 	ftl_mngt_next_step(mngt);
844 }
845 
846 static void
847 fn_5_3_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
848 {
849 	add_elem_to_test_list(-32);
850 	ftl_mngt_next_step(mngt);
851 }
852 
853 static struct ftl_mngt_process_desc pdesc_test_5_3 = {
854 	.name = "process nested inside step 2 from process 5",
855 	.steps = {
856 		{
857 			.name = "step 3_1",
858 			.action = fn_5_3_1_action,
859 			.cleanup = fn_5_3_1_cleanup
860 		},
861 		{
862 			.name = "step 3_2",
863 			.action = fn_5_3_2_action,
864 			.cleanup = fn_5_3_2_cleanup
865 		},
866 		{}
867 	}
868 };
869 
870 static void
871 fn_5_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
872 {
873 	add_elem_to_test_list(1);
874 	ftl_mngt_next_step(mngt);
875 }
876 
877 static void
878 fn_5_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
879 {
880 	add_elem_to_test_list(-1);
881 	ftl_mngt_next_step(mngt);
882 }
883 
884 static void
885 fn_5_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
886 {
887 	add_elem_to_test_list(2);
888 	ftl_mngt_call_process(mngt, &pdesc_test_5_2);
889 }
890 
891 static void
892 fn_5_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
893 {
894 	add_elem_to_test_list(-2);
895 	ftl_mngt_call_process_rollback(mngt, &pdesc_test_5_2);
896 }
897 
898 static void
899 fn_5_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
900 {
901 	add_elem_to_test_list(3);
902 	ftl_mngt_call_process_rollback(mngt, &pdesc_test_5_3);
903 }
904 
905 static void
906 fn_5_3_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
907 {
908 	add_elem_to_test_list(-3);
909 	ftl_mngt_call_process(mngt, &pdesc_test_5_3);
910 }
911 
912 static struct ftl_mngt_process_desc pdesc_test_5 = {
913 	.name = "process 5 main",
914 	.steps = {
915 		{
916 			.name = "step 1",
917 			.action = fn_5_1_action,
918 			.cleanup = fn_5_1_cleanup
919 		},
920 		{
921 			.name = "step 2",
922 			.action = fn_5_2_action,
923 			.cleanup = fn_5_2_cleanup
924 		},
925 		{
926 			.name = "step 3",
927 			.action = fn_5_3_action,
928 			.cleanup = fn_5_3_cleanup
929 		},
930 		{}
931 	}
932 };
933 
934 static void
935 test_mngt_call_and_call_rollback(void)
936 {
937 	run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_5);
938 
939 	check_elem_on_list_and_remove(1);
940 	check_elem_on_list_and_remove(2);
941 	check_elem_on_list_and_remove(21);
942 	check_elem_on_list_and_remove(22);
943 	check_elem_on_list_and_remove(3);
944 	check_elem_on_list_and_remove(-32);
945 	check_elem_on_list_and_remove(-31);
946 
947 	/* check if caller callback was invoked */
948 	check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
949 
950 	run_ftl_mngt(ftl_mngt_process_rollback, &pdesc_test_5);
951 
952 	check_elem_on_list_and_remove(-3);
953 	check_elem_on_list_and_remove(31);
954 	check_elem_on_list_and_remove(32);
955 	check_elem_on_list_and_remove(-2);
956 	check_elem_on_list_and_remove(-22);
957 	check_elem_on_list_and_remove(-21);
958 	check_elem_on_list_and_remove(-1);
959 
960 	/* check if caller callback was invoked */
961 	check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
962 
963 	check_list_empty();
964 }
965 
966 /*
967  * test 6
968  * tests failure inside nested process
969  */
970 
971 static void
972 fn_6_2_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
973 {
974 	add_elem_to_test_list(21);
975 	ftl_mngt_next_step(mngt);
976 }
977 
978 static void
979 fn_6_2_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
980 {
981 	add_elem_to_test_list(-21);
982 	ftl_mngt_next_step(mngt);
983 }
984 
985 static void
986 fn_6_2_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
987 {
988 	add_elem_to_test_list(22);
989 	/* this action fails, so cleanup should begin now */
990 	ftl_mngt_fail_step(mngt);
991 }
992 
993 static void
994 fn_6_2_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
995 {
996 	CU_FAIL("failure cannot start another action");
997 	ftl_mngt_next_step(mngt);
998 }
999 
1000 static struct ftl_mngt_process_desc pdesc_test_6_2 = {
1001 	.name = "process nested inside step 2 from process 6",
1002 	.steps = {
1003 		{
1004 			.name = "step 6_1",
1005 			.action = fn_6_2_1_action,
1006 			.cleanup = fn_6_2_1_cleanup
1007 		},
1008 		{
1009 			.name = "step 6_2",
1010 			.action = fn_6_2_2_action
1011 		},
1012 		{
1013 			.name = "step 6_3",
1014 			.action = fn_6_2_3_action
1015 		},
1016 		{}
1017 	}
1018 };
1019 
1020 static void
1021 fn_6_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
1022 {
1023 	add_elem_to_test_list(1);
1024 	ftl_mngt_next_step(mngt);
1025 }
1026 
1027 static void
1028 fn_6_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
1029 {
1030 	add_elem_to_test_list(2);
1031 	ftl_mngt_call_process(mngt, &pdesc_test_6_2);
1032 }
1033 
1034 static void
1035 fn_6_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
1036 {
1037 	add_elem_to_test_list(-2);
1038 	ftl_mngt_next_step(mngt);
1039 }
1040 
1041 static void
1042 fn_6_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
1043 {
1044 	CU_FAIL("failure cannot start another action");
1045 	ftl_mngt_next_step(mngt);
1046 }
1047 
1048 static struct ftl_mngt_process_desc pdesc_test_6 = {
1049 	.name = "process 6 main",
1050 	.steps = {
1051 		{
1052 			.name = "step 1",
1053 			.action = fn_6_1_action
1054 		},
1055 		{
1056 			.name = "step 2",
1057 			.action = fn_6_2_action,
1058 			.cleanup = fn_6_2_cleanup
1059 		},
1060 		{
1061 			.name = "step 3",
1062 			.action = fn_6_3_action
1063 		},
1064 		{}
1065 	}
1066 };
1067 
1068 static void
1069 test_nested_process_failure(void)
1070 {
1071 	run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_6);
1072 
1073 	check_elem_on_list_and_remove(1);
1074 	check_elem_on_list_and_remove(2);
1075 	check_elem_on_list_and_remove(21);
1076 	check_elem_on_list_and_remove(22);
1077 	check_elem_on_list_and_remove(-21);
1078 	check_elem_on_list_and_remove(-2);
1079 
1080 	/* check if caller callback was invoked */
1081 	check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
1082 
1083 	check_list_empty();
1084 }
1085 
1086 int
1087 main(int argc, char **argv)
1088 {
1089 	CU_pSuite suite = NULL;
1090 	unsigned int num_failures;
1091 
1092 	CU_initialize_registry();
1093 
1094 	suite = CU_add_suite("ftl_mngt", setup_test_list, NULL);
1095 
1096 	CU_ADD_TEST(suite, test_next_step);
1097 	CU_ADD_TEST(suite, test_continue_step);
1098 	CU_ADD_TEST(suite, test_get_func_and_step_cntx_alloc);
1099 	CU_ADD_TEST(suite, test_fail_step);
1100 	CU_ADD_TEST(suite, test_mngt_call_and_call_rollback);
1101 	CU_ADD_TEST(suite, test_nested_process_failure);
1102 
1103 	num_failures = spdk_ut_run_tests(argc, argv, NULL);
1104 	CU_cleanup_registry();
1105 
1106 	return num_failures;
1107 }
1108