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