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