1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (C) 2022 Intel Corporation.
3 * All rights reserved.
4 */
5
6 #include "spdk/queue.h"
7 #include "spdk/assert.h"
8 #include "spdk/env.h"
9
10 #include "ftl_mngt.h"
11 #include "ftl_core.h"
12
13 struct ftl_mngt_step_status {
14 uint64_t start;
15 uint64_t stop;
16 int status;
17 int silent;
18 TAILQ_ENTRY(ftl_mngt_step) entry;
19 };
20
21 struct ftl_mngt_step {
22 void *ctx;
23 const struct ftl_mngt_step_desc *desc;
24 struct ftl_mngt_step_status action;
25 struct ftl_mngt_step_status rollback;
26 };
27
28 struct ftl_mngt_process {
29 struct spdk_ftl_dev *dev;
30 int status;
31 bool silent;
32 bool rollback;
33 bool continuing;
34 struct {
35 ftl_mngt_completion cb;
36 void *cb_ctx;
37 struct spdk_thread *thread;
38 } caller;
39 void *ctx;
40 uint64_t tsc_start;
41 uint64_t tsc_stop;
42 const struct ftl_mngt_process_desc *desc;
43 TAILQ_HEAD(, ftl_mngt_step) action_queue_todo;
44 TAILQ_HEAD(, ftl_mngt_step) action_queue_done;
45 TAILQ_HEAD(, ftl_mngt_step) rollback_queue_todo;
46 TAILQ_HEAD(, ftl_mngt_step) rollback_queue_done;
47 struct {
48 struct ftl_mngt_step step;
49 struct ftl_mngt_step_desc desc;
50 } cleanup;
51 struct ftl_mng_tracer *tracer;
52 };
53
54 static void action_next(struct ftl_mngt_process *mngt);
55 static void action_msg(void *ctx);
56 static void action_execute(struct ftl_mngt_process *mngt);
57 static void action_done(struct ftl_mngt_process *mngt, int status);
58 static void rollback_next(struct ftl_mngt_process *mngt);
59 static void rollback_msg(void *ctx);
60 static void rollback_execute(struct ftl_mngt_process *mngt);
61 static void rollback_done(struct ftl_mngt_process *mngt, int status);
62
63 static inline struct ftl_mngt_step *
get_current_step(struct ftl_mngt_process * mngt)64 get_current_step(struct ftl_mngt_process *mngt)
65 {
66 if (!mngt->rollback) {
67 return TAILQ_FIRST(&mngt->action_queue_todo);
68 } else {
69 return TAILQ_FIRST(&mngt->rollback_queue_todo);
70 }
71 }
72
73 static int
init_step(struct ftl_mngt_process * mngt,const struct ftl_mngt_step_desc * desc)74 init_step(struct ftl_mngt_process *mngt,
75 const struct ftl_mngt_step_desc *desc)
76 {
77 struct ftl_mngt_step *step;
78
79 step = calloc(1, sizeof(*step));
80 if (!step) {
81 return -ENOMEM;
82 }
83
84 /* Initialize the step's argument */
85 if (desc->ctx_size) {
86 step->ctx = calloc(1, desc->ctx_size);
87 if (!step->ctx) {
88 free(step);
89 return -ENOMEM;
90 }
91 }
92 step->desc = desc;
93 TAILQ_INSERT_TAIL(&mngt->action_queue_todo, step, action.entry);
94
95 return 0;
96 }
97
98 static void
free_mngt(struct ftl_mngt_process * mngt)99 free_mngt(struct ftl_mngt_process *mngt)
100 {
101 TAILQ_HEAD(, ftl_mngt_step) steps;
102
103 if (!mngt) {
104 return;
105 }
106
107 TAILQ_INIT(&steps);
108 TAILQ_CONCAT(&steps, &mngt->action_queue_todo, action.entry);
109 TAILQ_CONCAT(&steps, &mngt->action_queue_done, action.entry);
110
111 while (!TAILQ_EMPTY(&steps)) {
112 struct ftl_mngt_step *step = TAILQ_FIRST(&steps);
113 TAILQ_REMOVE(&steps, step, action.entry);
114
115 free(step->ctx);
116 free(step);
117 }
118
119 free(mngt->ctx);
120 free(mngt);
121 }
122
123 static struct ftl_mngt_process *
allocate_mngt(struct spdk_ftl_dev * dev,const struct ftl_mngt_process_desc * pdesc,ftl_mngt_completion cb,void * cb_ctx,bool silent)124 allocate_mngt(struct spdk_ftl_dev *dev, const struct ftl_mngt_process_desc *pdesc,
125 ftl_mngt_completion cb, void *cb_ctx, bool silent)
126 {
127 struct ftl_mngt_process *mngt;
128
129 /* Initialize management process */
130 mngt = calloc(1, sizeof(*mngt));
131 if (!mngt) {
132 goto error;
133 }
134 mngt->dev = dev;
135 mngt->silent = silent;
136 mngt->caller.cb = cb;
137 mngt->caller.cb_ctx = cb_ctx;
138 mngt->caller.thread = spdk_get_thread();
139
140 /* Initialize process context */
141 if (pdesc->ctx_size) {
142 mngt->ctx = calloc(1, pdesc->ctx_size);
143 if (!mngt->ctx) {
144 goto error;
145 }
146 }
147 mngt->tsc_start = spdk_get_ticks();
148 mngt->desc = pdesc;
149 TAILQ_INIT(&mngt->action_queue_todo);
150 TAILQ_INIT(&mngt->action_queue_done);
151 TAILQ_INIT(&mngt->rollback_queue_todo);
152 TAILQ_INIT(&mngt->rollback_queue_done);
153
154 return mngt;
155 error:
156 free_mngt(mngt);
157 return NULL;
158 }
159
160 static int
invoke_init_handler(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt,const struct ftl_mngt_process_desc * pdesc,void * init_ctx)161 invoke_init_handler(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt,
162 const struct ftl_mngt_process_desc *pdesc, void *init_ctx)
163 {
164 int rc = 0;
165
166 if (init_ctx || pdesc->init_handler) {
167 ftl_bug(!init_ctx);
168 ftl_bug(!pdesc->init_handler);
169 rc = pdesc->init_handler(dev, mngt, init_ctx);
170 }
171
172 return rc;
173 }
174
175 static int
_ftl_mngt_process_execute(struct spdk_ftl_dev * dev,const struct ftl_mngt_process_desc * pdesc,ftl_mngt_completion cb,void * cb_ctx,bool silent,void * init_ctx)176 _ftl_mngt_process_execute(struct spdk_ftl_dev *dev, const struct ftl_mngt_process_desc *pdesc,
177 ftl_mngt_completion cb, void *cb_ctx, bool silent, void *init_ctx)
178 {
179 const struct ftl_mngt_step_desc *sdesc;
180 struct ftl_mngt_process *mngt;
181 int rc = 0;
182
183 mngt = allocate_mngt(dev, pdesc, cb, cb_ctx, silent);
184 if (!mngt) {
185 rc = -ENOMEM;
186 goto error;
187 }
188
189 if (pdesc->error_handler) {
190 /* Initialize a step for error handler */
191 mngt->cleanup.step.desc = &mngt->cleanup.desc;
192 mngt->cleanup.desc.name = "Handle ERROR";
193 mngt->cleanup.desc.cleanup = pdesc->error_handler;
194
195 /* Queue error handler to the rollback queue, it will be executed at the end */
196 TAILQ_INSERT_HEAD(&mngt->rollback_queue_todo, &mngt->cleanup.step,
197 rollback.entry);
198 }
199
200 /* Initialize steps */
201 sdesc = mngt->desc->steps;
202 while (sdesc->action) {
203 rc = init_step(mngt, sdesc);
204 if (rc) {
205 goto error;
206 }
207 sdesc++;
208 }
209
210 rc = invoke_init_handler(dev, mngt, pdesc, init_ctx);
211 if (rc) {
212 goto error;
213 }
214
215 action_execute(mngt);
216 return 0;
217 error:
218 free_mngt(mngt);
219 return rc;
220 }
221
222 int
ftl_mngt_process_execute(struct spdk_ftl_dev * dev,const struct ftl_mngt_process_desc * pdesc,ftl_mngt_completion cb,void * cb_ctx)223 ftl_mngt_process_execute(struct spdk_ftl_dev *dev, const struct ftl_mngt_process_desc *pdesc,
224 ftl_mngt_completion cb, void *cb_ctx)
225 {
226 return _ftl_mngt_process_execute(dev, pdesc, cb, cb_ctx, false, NULL);
227 }
228
229 int
ftl_mngt_process_rollback(struct spdk_ftl_dev * dev,const struct ftl_mngt_process_desc * pdesc,ftl_mngt_completion cb,void * cb_ctx)230 ftl_mngt_process_rollback(struct spdk_ftl_dev *dev, const struct ftl_mngt_process_desc *pdesc,
231 ftl_mngt_completion cb, void *cb_ctx)
232 {
233 const struct ftl_mngt_step_desc *sdesc;
234 struct ftl_mngt_process *mngt;
235 int rc = 0;
236
237 mngt = allocate_mngt(dev, pdesc, cb, cb_ctx, true);
238 if (!mngt) {
239 rc = -ENOMEM;
240 goto error;
241 }
242
243 /* Initialize steps for rollback */
244 sdesc = mngt->desc->steps;
245 while (sdesc->action) {
246 if (!sdesc->cleanup) {
247 sdesc++;
248 continue;
249 }
250 rc = init_step(mngt, sdesc);
251 if (rc) {
252 goto error;
253 }
254 sdesc++;
255 }
256
257 /* Build rollback list */
258 struct ftl_mngt_step *step;
259 TAILQ_FOREACH(step, &mngt->action_queue_todo, action.entry) {
260 step->action.silent = true;
261 TAILQ_INSERT_HEAD(&mngt->rollback_queue_todo, step,
262 rollback.entry);
263 }
264
265 mngt->rollback = true;
266 rollback_execute(mngt);
267 return 0;
268 error:
269 free_mngt(mngt);
270 return rc;
271 }
272
273 struct spdk_ftl_dev *
ftl_mngt_get_dev(struct ftl_mngt_process * mngt)274 ftl_mngt_get_dev(struct ftl_mngt_process *mngt)
275 {
276 return mngt->dev;
277 }
278
279 int
ftl_mngt_alloc_step_ctx(struct ftl_mngt_process * mngt,size_t size)280 ftl_mngt_alloc_step_ctx(struct ftl_mngt_process *mngt, size_t size)
281 {
282 struct ftl_mngt_step *step = get_current_step(mngt);
283 void *arg = calloc(1, size);
284
285 if (!arg) {
286 return -ENOMEM;
287 }
288
289 free(step->ctx);
290 step->ctx = arg;
291
292 return 0;
293 }
294
295 void *
ftl_mngt_get_step_ctx(struct ftl_mngt_process * mngt)296 ftl_mngt_get_step_ctx(struct ftl_mngt_process *mngt)
297 {
298 return get_current_step(mngt)->ctx;
299 }
300
301 void *
ftl_mngt_get_process_ctx(struct ftl_mngt_process * mngt)302 ftl_mngt_get_process_ctx(struct ftl_mngt_process *mngt)
303 {
304 return mngt->ctx;
305 }
306
307 void *
ftl_mngt_get_caller_ctx(struct ftl_mngt_process * mngt)308 ftl_mngt_get_caller_ctx(struct ftl_mngt_process *mngt)
309 {
310 return mngt->caller.cb_ctx;
311 }
312
313 void
ftl_mngt_next_step(struct ftl_mngt_process * mngt)314 ftl_mngt_next_step(struct ftl_mngt_process *mngt)
315 {
316 if (false == mngt->rollback) {
317 action_next(mngt);
318 } else {
319 rollback_next(mngt);
320 }
321 }
322
323 void
ftl_mngt_skip_step(struct ftl_mngt_process * mngt)324 ftl_mngt_skip_step(struct ftl_mngt_process *mngt)
325 {
326 if (mngt->rollback) {
327 get_current_step(mngt)->rollback.silent = true;
328 } else {
329 get_current_step(mngt)->action.silent = true;
330 }
331 ftl_mngt_next_step(mngt);
332 }
333
334 void
ftl_mngt_continue_step(struct ftl_mngt_process * mngt)335 ftl_mngt_continue_step(struct ftl_mngt_process *mngt)
336 {
337
338 if (!mngt->continuing) {
339 if (false == mngt->rollback) {
340 action_execute(mngt);
341 } else {
342 rollback_execute(mngt);
343 }
344 }
345
346 mngt->continuing = true;
347 }
348
349 static void
child_cb(struct spdk_ftl_dev * dev,void * ctx,int status)350 child_cb(struct spdk_ftl_dev *dev, void *ctx, int status)
351 {
352 struct ftl_mngt_process *parent = ctx;
353
354 if (status) {
355 ftl_mngt_fail_step(parent);
356 } else {
357 ftl_mngt_next_step(parent);
358 }
359 }
360
361 void
ftl_mngt_call_process(struct ftl_mngt_process * mngt,const struct ftl_mngt_process_desc * pdesc,void * init_ctx)362 ftl_mngt_call_process(struct ftl_mngt_process *mngt,
363 const struct ftl_mngt_process_desc *pdesc,
364 void *init_ctx)
365 {
366 if (_ftl_mngt_process_execute(mngt->dev, pdesc, child_cb, mngt, true, init_ctx)) {
367 ftl_mngt_fail_step(mngt);
368 } else {
369 if (mngt->rollback) {
370 get_current_step(mngt)->rollback.silent = true;
371 } else {
372 get_current_step(mngt)->action.silent = true;
373 }
374 }
375 }
376
377 void
ftl_mngt_call_process_rollback(struct ftl_mngt_process * mngt,const struct ftl_mngt_process_desc * pdesc)378 ftl_mngt_call_process_rollback(struct ftl_mngt_process *mngt,
379 const struct ftl_mngt_process_desc *pdesc)
380 {
381 if (ftl_mngt_process_rollback(mngt->dev, pdesc, child_cb, mngt)) {
382 ftl_mngt_fail_step(mngt);
383 } else {
384 if (mngt->rollback) {
385 get_current_step(mngt)->rollback.silent = true;
386 } else {
387 get_current_step(mngt)->action.silent = true;
388 }
389 }
390 }
391
392 void
ftl_mngt_fail_step(struct ftl_mngt_process * mngt)393 ftl_mngt_fail_step(struct ftl_mngt_process *mngt)
394 {
395 mngt->status = -1;
396
397 if (false == mngt->rollback) {
398 action_done(mngt, -1);
399 } else {
400 rollback_done(mngt, -1);
401 }
402
403 mngt->rollback = true;
404 rollback_execute(mngt);
405 }
406
407 static inline float
tsc_to_ms(uint64_t tsc)408 tsc_to_ms(uint64_t tsc)
409 {
410 float ms = tsc;
411 ms /= (float)spdk_get_ticks_hz();
412 ms *= 1000.0;
413 return ms;
414 }
415
416 static void
trace_step(struct spdk_ftl_dev * dev,struct ftl_mngt_step * step,bool rollback)417 trace_step(struct spdk_ftl_dev *dev, struct ftl_mngt_step *step, bool rollback)
418 {
419 uint64_t duration;
420 const char *what = rollback ? "Rollback" : "Action";
421 int silent = rollback ? step->rollback.silent : step->action.silent;
422
423 if (silent) {
424 return;
425 }
426
427 FTL_NOTICELOG(dev, "%s\n", what);
428 FTL_NOTICELOG(dev, "\t name: %s\n", step->desc->name);
429 duration = step->action.stop - step->action.start;
430 FTL_NOTICELOG(dev, "\t duration: %.3f ms\n", tsc_to_ms(duration));
431 FTL_NOTICELOG(dev, "\t status: %d\n", step->action.status);
432 }
433
434 static void
finish_msg(void * ctx)435 finish_msg(void *ctx)
436 {
437 struct ftl_mngt_process *mngt = ctx;
438 char *devname = NULL;
439
440 if (!mngt->silent && mngt->dev->conf.name) {
441 /* the callback below can free the device so make a temp copy of the name */
442 devname = strdup(mngt->dev->conf.name);
443 }
444
445 mngt->caller.cb(mngt->dev, mngt->caller.cb_ctx, mngt->status);
446
447 if (mngt->desc->deinit_handler) {
448 mngt->desc->deinit_handler(mngt->dev, mngt);
449 }
450
451 if (!mngt->silent) {
452 /* TODO: refactor the logging macros to pass just the name instead of device */
453 struct spdk_ftl_dev tmpdev = {
454 .conf = {
455 .name = devname
456 }
457 };
458
459 FTL_NOTICELOG(&tmpdev, "Management process finished, name '%s', duration = %.3f ms, result %d\n",
460 mngt->desc->name,
461 tsc_to_ms(mngt->tsc_stop - mngt->tsc_start),
462 mngt->status);
463 }
464 free_mngt(mngt);
465 free(devname);
466 }
467
468 void
ftl_mngt_finish(struct ftl_mngt_process * mngt)469 ftl_mngt_finish(struct ftl_mngt_process *mngt)
470 {
471 mngt->tsc_stop = spdk_get_ticks();
472 spdk_thread_send_msg(mngt->caller.thread, finish_msg, mngt);
473 }
474
475 /*
476 * Actions
477 */
478 static void
action_next(struct ftl_mngt_process * mngt)479 action_next(struct ftl_mngt_process *mngt)
480 {
481 if (TAILQ_EMPTY(&mngt->action_queue_todo)) {
482 /* Nothing to do, finish the management process */
483 ftl_mngt_finish(mngt);
484 return;
485 } else {
486 action_done(mngt, 0);
487 action_execute(mngt);
488 }
489 }
490
491 static void
action_msg(void * ctx)492 action_msg(void *ctx)
493 {
494 struct ftl_mngt_process *mngt = ctx;
495 struct ftl_mngt_step *step;
496
497 mngt->continuing = false;
498
499 if (TAILQ_EMPTY(&mngt->action_queue_todo)) {
500 ftl_mngt_finish(mngt);
501 return;
502 }
503
504 step = TAILQ_FIRST(&mngt->action_queue_todo);
505 if (!step->action.start) {
506 step->action.start = spdk_get_ticks();
507 }
508 step->desc->action(mngt->dev, mngt);
509 }
510
511 static void
action_execute(struct ftl_mngt_process * mngt)512 action_execute(struct ftl_mngt_process *mngt)
513 {
514 spdk_thread_send_msg(mngt->dev->core_thread, action_msg, mngt);
515 }
516
517 static void
action_done(struct ftl_mngt_process * mngt,int status)518 action_done(struct ftl_mngt_process *mngt, int status)
519 {
520 struct ftl_mngt_step *step;
521
522 assert(!TAILQ_EMPTY(&mngt->action_queue_todo));
523 step = TAILQ_FIRST(&mngt->action_queue_todo);
524 TAILQ_REMOVE(&mngt->action_queue_todo, step, action.entry);
525
526 TAILQ_INSERT_TAIL(&mngt->action_queue_done, step, action.entry);
527 if (step->desc->cleanup) {
528 TAILQ_INSERT_HEAD(&mngt->rollback_queue_todo, step,
529 rollback.entry);
530 }
531
532 step->action.stop = spdk_get_ticks();
533 step->action.status = status;
534
535 trace_step(mngt->dev, step, false);
536 }
537
538 /*
539 * Rollback
540 */
541 static void
rollback_next(struct ftl_mngt_process * mngt)542 rollback_next(struct ftl_mngt_process *mngt)
543 {
544 if (TAILQ_EMPTY(&mngt->rollback_queue_todo)) {
545 /* Nothing to do, finish the management process */
546 ftl_mngt_finish(mngt);
547 return;
548 } else {
549 rollback_done(mngt, 0);
550 rollback_execute(mngt);
551 }
552 }
553
554 static void
rollback_msg(void * ctx)555 rollback_msg(void *ctx)
556 {
557 struct ftl_mngt_process *mngt = ctx;
558 struct ftl_mngt_step *step;
559
560 mngt->continuing = false;
561
562 if (TAILQ_EMPTY(&mngt->rollback_queue_todo)) {
563 ftl_mngt_finish(mngt);
564 return;
565 }
566
567 step = TAILQ_FIRST(&mngt->rollback_queue_todo);
568 if (!step->rollback.start) {
569 step->rollback.start = spdk_get_ticks();
570 }
571 step->desc->cleanup(mngt->dev, mngt);
572 }
573
574 static void
rollback_execute(struct ftl_mngt_process * mngt)575 rollback_execute(struct ftl_mngt_process *mngt)
576 {
577 spdk_thread_send_msg(mngt->dev->core_thread, rollback_msg, mngt);
578 }
579
580 void
rollback_done(struct ftl_mngt_process * mngt,int status)581 rollback_done(struct ftl_mngt_process *mngt, int status)
582 {
583 struct ftl_mngt_step *step;
584
585 assert(!TAILQ_EMPTY(&mngt->rollback_queue_todo));
586 step = TAILQ_FIRST(&mngt->rollback_queue_todo);
587 TAILQ_REMOVE(&mngt->rollback_queue_todo, step, rollback.entry);
588 TAILQ_INSERT_TAIL(&mngt->rollback_queue_done, step, rollback.entry);
589
590 step->rollback.stop = spdk_get_ticks();
591 step->rollback.status = status;
592
593 trace_step(mngt->dev, step, true);
594 }
595