xref: /spdk/test/unit/lib/ftl/ftl_mngt/ftl_mngt_ut.c (revision 12fbe739a31b09aff0d05f354d4f3bbef99afc55)
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