xref: /spdk/lib/ftl/mngt/ftl_mngt.c (revision 95d6c9fac17572b107042103439aafd696d60b0e)
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 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
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
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 *
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
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
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
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
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 *
274 ftl_mngt_get_dev(struct ftl_mngt_process *mngt)
275 {
276 	return mngt->dev;
277 }
278 
279 int
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 *
296 ftl_mngt_get_step_ctx(struct ftl_mngt_process *mngt)
297 {
298 	return get_current_step(mngt)->ctx;
299 }
300 
301 void *
302 ftl_mngt_get_process_ctx(struct ftl_mngt_process *mngt)
303 {
304 	return mngt->ctx;
305 }
306 
307 void *
308 ftl_mngt_get_caller_ctx(struct ftl_mngt_process *mngt)
309 {
310 	return mngt->caller.cb_ctx;
311 }
312 
313 void
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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