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