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