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