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