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
spdk_thread_send_msg(const struct spdk_thread * thread,spdk_msg_fn fn,void * ctx)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 *
spdk_get_thread(void)41 spdk_get_thread(void)
42 {
43 struct spdk_thread *thd = (struct spdk_thread *)0x1;
44 return thd;
45 }
46
47 static int
setup_test_list(void)48 setup_test_list(void)
49 {
50 TAILQ_INIT(&g_head);
51 return 0;
52 }
53
54 static void
check_list_empty(void)55 check_list_empty(void)
56 {
57 CU_ASSERT_TRUE(TAILQ_EMPTY(&g_head));
58 }
59
60 static void
add_elem_to_test_list(int data)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
check_elem_on_list_and_remove(int compared_elem)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
fn_finish(struct spdk_ftl_dev * dev,void * ctx,int status)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
run_ftl_mngt_with_cb_cntx(ftl_execute_fn exec_fn,const struct ftl_mngt_process_desc * process,void * cb_cntx)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
run_ftl_mngt(ftl_execute_fn exec_fn,const struct ftl_mngt_process_desc * process)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
fn_1_1_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_1_1_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_1_2_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_1_3_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_1_3_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
test_next_step(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
fn_2_common_part(struct ftl_mngt_process * mngt,int elem)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
fn_2_1_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_2_1_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_2_2_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_2_2_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
test_continue_step(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
put_on_list(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
check_if_list_empty_and_clean(void)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
fn_3_1_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_3_2_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_3_2_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_3_1_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
test_get_func_and_step_cntx_alloc(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
fn_4_1_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_4_1_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_4_2_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_4_2_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_4_3_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_4_3_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_4_4_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
test_fail_step(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
fn_5_2_1_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_2_1_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_2_2_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_2_2_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_3_1_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_3_1_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_3_2_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_3_2_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_1_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_1_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_2_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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, NULL);
889 }
890
891 static void
fn_5_2_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_3_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_5_3_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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, NULL);
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
test_mngt_call_and_call_rollback(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
fn_6_2_1_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_6_2_1_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_6_2_2_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_6_2_3_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_6_1_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_6_2_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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, NULL);
1032 }
1033
1034 static void
fn_6_2_cleanup(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
fn_6_3_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)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
test_nested_process_failure(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 struct test_call_init_ctx {
1087 int init_handler_result;
1088 };
1089
1090 static int
test_call_init_child_init_hndlr(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt,void * _init_ctx)1091 test_call_init_child_init_hndlr(struct spdk_ftl_dev *dev,
1092 struct ftl_mngt_process *mngt, void *_init_ctx)
1093 {
1094 struct test_call_init_ctx *init_ctx = _init_ctx;
1095
1096 add_elem_to_test_list(10);
1097 return init_ctx->init_handler_result;
1098 }
1099
1100 static void
test_call_init_child_deinit_hndlr(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)1101 test_call_init_child_deinit_hndlr(struct spdk_ftl_dev *dev,
1102 struct ftl_mngt_process *mngt)
1103 {
1104 add_elem_to_test_list(1000);
1105 }
1106
1107 static void
test_call_init_child_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)1108 test_call_init_child_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
1109 {
1110 add_elem_to_test_list(100);
1111 ftl_mngt_next_step(mngt);
1112 }
1113
1114 static struct ftl_mngt_process_desc pdesc_test_call_init_child = {
1115 .name = "Test call init, child",
1116 .init_handler = test_call_init_child_init_hndlr,
1117 .deinit_handler = test_call_init_child_deinit_hndlr,
1118 .steps = {
1119 {
1120 .name = "Test call init, child step",
1121 .action = test_call_init_child_action
1122 },
1123 {}
1124 }
1125 };
1126
1127 static void
test_call_init_parent_action(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt)1128 test_call_init_parent_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
1129 {
1130 struct test_call_init_ctx *init_ctx = ftl_mngt_get_caller_ctx(mngt);
1131
1132 add_elem_to_test_list(1);
1133 ftl_mngt_call_process(mngt, &pdesc_test_call_init_child, init_ctx);
1134 }
1135
1136 static struct ftl_mngt_process_desc pdesc_test_call_init_parent = {
1137 .name = "Test call init, parent",
1138 .steps = {
1139 {
1140 .name = "Test call init, parent step",
1141 .action = test_call_init_parent_action
1142 },
1143 {}
1144 }
1145 };
1146
1147 static void
test_call_init_success(void)1148 test_call_init_success(void)
1149 {
1150 struct test_call_init_ctx init_ctx = {
1151 .init_handler_result = 0
1152 };
1153
1154 run_ftl_mngt_with_cb_cntx(ftl_mngt_process_execute, &pdesc_test_call_init_parent, &init_ctx);
1155
1156 check_elem_on_list_and_remove(1);
1157 check_elem_on_list_and_remove(10);
1158 check_elem_on_list_and_remove(100);
1159
1160 /* check if caller deinit handler ws invoked */
1161 check_elem_on_list_and_remove(1000);
1162
1163 /* check if caller callback was invoked */
1164 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
1165
1166 check_list_empty();
1167 }
1168
1169 static void
test_call_init_failure(void)1170 test_call_init_failure(void)
1171 {
1172 struct test_call_init_ctx init_ctx = {
1173 .init_handler_result = -1
1174 };
1175
1176 run_ftl_mngt_with_cb_cntx(ftl_mngt_process_execute, &pdesc_test_call_init_parent, &init_ctx);
1177
1178 check_elem_on_list_and_remove(1);
1179 check_elem_on_list_and_remove(10);
1180
1181 /* check if caller callback was invoked */
1182 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
1183
1184 check_list_empty();
1185 }
1186
1187 int
main(int argc,char ** argv)1188 main(int argc, char **argv)
1189 {
1190 CU_pSuite suite = NULL;
1191 unsigned int num_failures;
1192
1193 CU_initialize_registry();
1194
1195 suite = CU_add_suite("ftl_mngt", setup_test_list, NULL);
1196
1197 CU_ADD_TEST(suite, test_next_step);
1198 CU_ADD_TEST(suite, test_continue_step);
1199 CU_ADD_TEST(suite, test_get_func_and_step_cntx_alloc);
1200 CU_ADD_TEST(suite, test_fail_step);
1201 CU_ADD_TEST(suite, test_mngt_call_and_call_rollback);
1202 CU_ADD_TEST(suite, test_nested_process_failure);
1203 CU_ADD_TEST(suite, test_call_init_success);
1204 CU_ADD_TEST(suite, test_call_init_failure);
1205
1206 num_failures = spdk_ut_run_tests(argc, argv, NULL);
1207 CU_cleanup_registry();
1208
1209 return num_failures;
1210 }
1211