per Intel policy to include file commit date using git cmd below. The policy does not apply to non-Intel (C) notices. git log --follow -C90% --format=%ad --date default <file> | tail -1 and then pull just the 4 digit year from the result. Intel copyrights were not added to files where Intel either had no contribution ot the contribution lacked substance (ie license header updates, formatting changes, etc). Contribution date used "--follow -C95%" to get the most accurate date. Note that several files in this patch didn't end the license/(c) block with a blank comment line so these were added as the vast majority of files do have this last blank line. Simply there for consistency. Signed-off-by: paul luse <paul.e.luse@intel.com> Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Community-CI: Mellanox Build Bot
570 lines
12 KiB
C
570 lines
12 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (C) 2022 Intel Corporation.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "spdk/queue.h"
|
|
#include "spdk/assert.h"
|
|
#include "spdk/env.h"
|
|
|
|
#include "ftl_mngt.h"
|
|
#include "ftl_core.h"
|
|
|
|
struct ftl_mngt_step_status {
|
|
uint64_t start;
|
|
uint64_t stop;
|
|
int status;
|
|
int silent;
|
|
TAILQ_ENTRY(ftl_mngt_step) entry;
|
|
};
|
|
|
|
struct ftl_mngt_step {
|
|
void *ctx;
|
|
const struct ftl_mngt_step_desc *desc;
|
|
struct ftl_mngt_step_status action;
|
|
struct ftl_mngt_step_status rollback;
|
|
};
|
|
|
|
struct ftl_mngt_process {
|
|
struct spdk_ftl_dev *dev;
|
|
int status;
|
|
bool silent;
|
|
bool rollback;
|
|
bool continuing;
|
|
struct {
|
|
ftl_mngt_completion cb;
|
|
void *cb_ctx;
|
|
struct spdk_thread *thread;
|
|
} caller;
|
|
void *ctx;
|
|
uint64_t tsc_start;
|
|
uint64_t tsc_stop;
|
|
const struct ftl_mngt_process_desc *desc;
|
|
TAILQ_HEAD(, ftl_mngt_step) action_queue_todo;
|
|
TAILQ_HEAD(, ftl_mngt_step) action_queue_done;
|
|
TAILQ_HEAD(, ftl_mngt_step) rollback_queue_todo;
|
|
TAILQ_HEAD(, ftl_mngt_step) rollback_queue_done;
|
|
struct {
|
|
struct ftl_mngt_step step;
|
|
struct ftl_mngt_step_desc desc;
|
|
} cleanup;
|
|
struct ftl_mng_tracer *tracer;
|
|
};
|
|
|
|
static void action_next(struct ftl_mngt_process *mngt);
|
|
static void action_msg(void *ctx);
|
|
static void action_execute(struct ftl_mngt_process *mngt);
|
|
static void action_done(struct ftl_mngt_process *mngt, int status);
|
|
static void rollback_next(struct ftl_mngt_process *mngt);
|
|
static void rollback_msg(void *ctx);
|
|
static void rollback_execute(struct ftl_mngt_process *mngt);
|
|
static void rollback_done(struct ftl_mngt_process *mngt, int status);
|
|
|
|
static inline struct ftl_mngt_step *
|
|
get_current_step(struct ftl_mngt_process *mngt)
|
|
{
|
|
if (!mngt->rollback) {
|
|
return TAILQ_FIRST(&mngt->action_queue_todo);
|
|
} else {
|
|
return TAILQ_FIRST(&mngt->rollback_queue_todo);
|
|
}
|
|
}
|
|
|
|
static int
|
|
init_step(struct ftl_mngt_process *mngt,
|
|
const struct ftl_mngt_step_desc *desc)
|
|
{
|
|
struct ftl_mngt_step *step;
|
|
|
|
step = calloc(1, sizeof(*step));
|
|
if (!step) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Initialize the step's argument */
|
|
if (desc->ctx_size) {
|
|
step->ctx = calloc(1, desc->ctx_size);
|
|
if (!step->ctx) {
|
|
free(step);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
step->desc = desc;
|
|
TAILQ_INSERT_TAIL(&mngt->action_queue_todo, step, action.entry);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
free_mngt(struct ftl_mngt_process *mngt)
|
|
{
|
|
TAILQ_HEAD(, ftl_mngt_step) steps;
|
|
|
|
if (!mngt) {
|
|
return;
|
|
}
|
|
|
|
TAILQ_INIT(&steps);
|
|
TAILQ_CONCAT(&steps, &mngt->action_queue_todo, action.entry);
|
|
TAILQ_CONCAT(&steps, &mngt->action_queue_done, action.entry);
|
|
|
|
while (!TAILQ_EMPTY(&steps)) {
|
|
struct ftl_mngt_step *step = TAILQ_FIRST(&steps);
|
|
TAILQ_REMOVE(&steps, step, action.entry);
|
|
|
|
free(step->ctx);
|
|
free(step);
|
|
}
|
|
|
|
free(mngt->ctx);
|
|
free(mngt);
|
|
}
|
|
|
|
static struct ftl_mngt_process *
|
|
allocate_mngt(struct spdk_ftl_dev *dev, const struct ftl_mngt_process_desc *pdesc,
|
|
ftl_mngt_completion cb, void *cb_ctx, bool silent)
|
|
{
|
|
struct ftl_mngt_process *mngt;
|
|
|
|
/* Initialize management process */
|
|
mngt = calloc(1, sizeof(*mngt));
|
|
if (!mngt) {
|
|
goto error;
|
|
}
|
|
mngt->dev = dev;
|
|
mngt->silent = silent;
|
|
mngt->caller.cb = cb;
|
|
mngt->caller.cb_ctx = cb_ctx;
|
|
mngt->caller.thread = spdk_get_thread();
|
|
|
|
/* Initialize process context */
|
|
if (pdesc->ctx_size) {
|
|
mngt->ctx = calloc(1, pdesc->ctx_size);
|
|
if (!mngt->ctx) {
|
|
goto error;
|
|
}
|
|
}
|
|
mngt->tsc_start = spdk_get_ticks();
|
|
mngt->desc = pdesc;
|
|
TAILQ_INIT(&mngt->action_queue_todo);
|
|
TAILQ_INIT(&mngt->action_queue_done);
|
|
TAILQ_INIT(&mngt->rollback_queue_todo);
|
|
TAILQ_INIT(&mngt->rollback_queue_done);
|
|
|
|
return mngt;
|
|
error:
|
|
free_mngt(mngt);
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
_ftl_mngt_process_execute(struct spdk_ftl_dev *dev, const struct ftl_mngt_process_desc *pdesc,
|
|
ftl_mngt_completion cb, void *cb_ctx, bool silent)
|
|
{
|
|
const struct ftl_mngt_step_desc *sdesc;
|
|
struct ftl_mngt_process *mngt;
|
|
int rc = 0;
|
|
|
|
mngt = allocate_mngt(dev, pdesc, cb, cb_ctx, silent);
|
|
if (!mngt) {
|
|
rc = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
if (pdesc->error_handler) {
|
|
/* Initialize a step for error handler */
|
|
mngt->cleanup.step.desc = &mngt->cleanup.desc;
|
|
mngt->cleanup.desc.name = "Handle ERROR";
|
|
mngt->cleanup.desc.cleanup = pdesc->error_handler;
|
|
|
|
/* Queue error handler to the rollback queue, it will be executed at the end */
|
|
TAILQ_INSERT_HEAD(&mngt->rollback_queue_todo, &mngt->cleanup.step,
|
|
rollback.entry);
|
|
}
|
|
|
|
/* Initialize steps */
|
|
sdesc = mngt->desc->steps;
|
|
while (sdesc->action) {
|
|
rc = init_step(mngt, sdesc);
|
|
if (rc) {
|
|
goto error;
|
|
}
|
|
sdesc++;
|
|
}
|
|
|
|
action_execute(mngt);
|
|
return 0;
|
|
error:
|
|
free_mngt(mngt);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ftl_mngt_process_execute(struct spdk_ftl_dev *dev, const struct ftl_mngt_process_desc *pdesc,
|
|
ftl_mngt_completion cb, void *cb_ctx)
|
|
{
|
|
return _ftl_mngt_process_execute(dev, pdesc, cb, cb_ctx, false);
|
|
}
|
|
|
|
int
|
|
ftl_mngt_process_rollback(struct spdk_ftl_dev *dev, const struct ftl_mngt_process_desc *pdesc,
|
|
ftl_mngt_completion cb, void *cb_ctx)
|
|
{
|
|
const struct ftl_mngt_step_desc *sdesc;
|
|
struct ftl_mngt_process *mngt;
|
|
int rc = 0;
|
|
|
|
mngt = allocate_mngt(dev, pdesc, cb, cb_ctx, true);
|
|
if (!mngt) {
|
|
rc = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
/* Initialize steps for rollback */
|
|
sdesc = mngt->desc->steps;
|
|
while (sdesc->action) {
|
|
if (!sdesc->cleanup) {
|
|
sdesc++;
|
|
continue;
|
|
}
|
|
rc = init_step(mngt, sdesc);
|
|
if (rc) {
|
|
goto error;
|
|
}
|
|
sdesc++;
|
|
}
|
|
|
|
/* Build rollback list */
|
|
struct ftl_mngt_step *step;
|
|
TAILQ_FOREACH(step, &mngt->action_queue_todo, action.entry) {
|
|
step->action.silent = true;
|
|
TAILQ_INSERT_HEAD(&mngt->rollback_queue_todo, step,
|
|
rollback.entry);
|
|
}
|
|
|
|
mngt->rollback = true;
|
|
rollback_execute(mngt);
|
|
return 0;
|
|
error:
|
|
free_mngt(mngt);
|
|
return rc;
|
|
}
|
|
|
|
struct spdk_ftl_dev *
|
|
ftl_mngt_get_dev(struct ftl_mngt_process *mngt)
|
|
{
|
|
return mngt->dev;
|
|
}
|
|
|
|
int
|
|
ftl_mngt_alloc_step_ctx(struct ftl_mngt_process *mngt, size_t size)
|
|
{
|
|
struct ftl_mngt_step *step = get_current_step(mngt);
|
|
void *arg = calloc(1, size);
|
|
|
|
if (!arg) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
free(step->ctx);
|
|
step->ctx = arg;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void *
|
|
ftl_mngt_get_step_ctx(struct ftl_mngt_process *mngt)
|
|
{
|
|
return get_current_step(mngt)->ctx;
|
|
}
|
|
|
|
void *
|
|
ftl_mngt_get_process_ctx(struct ftl_mngt_process *mngt)
|
|
{
|
|
return mngt->ctx;
|
|
}
|
|
|
|
void *
|
|
ftl_mngt_get_caller_ctx(struct ftl_mngt_process *mngt)
|
|
{
|
|
return mngt->caller.cb_ctx;
|
|
}
|
|
|
|
void
|
|
ftl_mngt_next_step(struct ftl_mngt_process *mngt)
|
|
{
|
|
if (false == mngt->rollback) {
|
|
action_next(mngt);
|
|
} else {
|
|
rollback_next(mngt);
|
|
}
|
|
}
|
|
|
|
void
|
|
ftl_mngt_skip_step(struct ftl_mngt_process *mngt)
|
|
{
|
|
if (mngt->rollback) {
|
|
get_current_step(mngt)->rollback.silent = true;
|
|
} else {
|
|
get_current_step(mngt)->action.silent = true;
|
|
}
|
|
ftl_mngt_next_step(mngt);
|
|
}
|
|
|
|
void
|
|
ftl_mngt_continue_step(struct ftl_mngt_process *mngt)
|
|
{
|
|
|
|
if (!mngt->continuing) {
|
|
if (false == mngt->rollback) {
|
|
action_execute(mngt);
|
|
} else {
|
|
rollback_execute(mngt);
|
|
}
|
|
}
|
|
|
|
mngt->continuing = true;
|
|
}
|
|
|
|
static void
|
|
child_cb(struct spdk_ftl_dev *dev, void *ctx, int status)
|
|
{
|
|
struct ftl_mngt_process *parent = ctx;
|
|
|
|
if (status) {
|
|
ftl_mngt_fail_step(parent);
|
|
} else {
|
|
ftl_mngt_next_step(parent);
|
|
}
|
|
}
|
|
|
|
void
|
|
ftl_mngt_call_process(struct ftl_mngt_process *mngt,
|
|
const struct ftl_mngt_process_desc *pdesc)
|
|
{
|
|
if (_ftl_mngt_process_execute(mngt->dev, pdesc, child_cb, mngt, true)) {
|
|
ftl_mngt_fail_step(mngt);
|
|
} else {
|
|
if (mngt->rollback) {
|
|
get_current_step(mngt)->rollback.silent = true;
|
|
} else {
|
|
get_current_step(mngt)->action.silent = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ftl_mngt_call_process_rollback(struct ftl_mngt_process *mngt,
|
|
const struct ftl_mngt_process_desc *pdesc)
|
|
{
|
|
if (ftl_mngt_process_rollback(mngt->dev, pdesc, child_cb, mngt)) {
|
|
ftl_mngt_fail_step(mngt);
|
|
} else {
|
|
if (mngt->rollback) {
|
|
get_current_step(mngt)->rollback.silent = true;
|
|
} else {
|
|
get_current_step(mngt)->action.silent = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ftl_mngt_fail_step(struct ftl_mngt_process *mngt)
|
|
{
|
|
mngt->status = -1;
|
|
|
|
if (false == mngt->rollback) {
|
|
action_done(mngt, -1);
|
|
} else {
|
|
rollback_done(mngt, -1);
|
|
}
|
|
|
|
mngt->rollback = true;
|
|
rollback_execute(mngt);
|
|
}
|
|
|
|
static inline float
|
|
tsc_to_ms(uint64_t tsc)
|
|
{
|
|
float ms = tsc;
|
|
ms /= (float)spdk_get_ticks_hz();
|
|
ms *= 1000.0;
|
|
return ms;
|
|
}
|
|
|
|
static void
|
|
trace_step(struct spdk_ftl_dev *dev, struct ftl_mngt_step *step, bool rollback)
|
|
{
|
|
uint64_t duration;
|
|
const char *what = rollback ? "Rollback" : "Action";
|
|
int silent = rollback ? step->rollback.silent : step->action.silent;
|
|
|
|
if (silent) {
|
|
return;
|
|
}
|
|
|
|
FTL_NOTICELOG(dev, "%s\n", what);
|
|
FTL_NOTICELOG(dev, "\t name: %s\n", step->desc->name);
|
|
duration = step->action.stop - step->action.start;
|
|
FTL_NOTICELOG(dev, "\t duration: %.3f ms\n", tsc_to_ms(duration));
|
|
FTL_NOTICELOG(dev, "\t status: %d\n", step->action.status);
|
|
}
|
|
|
|
static void
|
|
finish_msg(void *ctx)
|
|
{
|
|
struct ftl_mngt_process *mngt = ctx;
|
|
char *devname = NULL;
|
|
|
|
if (!mngt->silent && mngt->dev->conf.name) {
|
|
/* the callback below can free the device so make a temp copy of the name */
|
|
devname = strdup(mngt->dev->conf.name);
|
|
}
|
|
|
|
mngt->caller.cb(mngt->dev, mngt->caller.cb_ctx, mngt->status);
|
|
|
|
if (!mngt->silent) {
|
|
/* TODO: refactor the logging macros to pass just the name instead of device */
|
|
struct spdk_ftl_dev tmpdev = {
|
|
.conf = {
|
|
.name = devname
|
|
}
|
|
};
|
|
|
|
FTL_NOTICELOG(&tmpdev, "Management process finished, name '%s', duration = %.3f ms, result %d\n",
|
|
mngt->desc->name,
|
|
tsc_to_ms(mngt->tsc_stop - mngt->tsc_start),
|
|
mngt->status);
|
|
}
|
|
free_mngt(mngt);
|
|
free(devname);
|
|
}
|
|
|
|
void
|
|
ftl_mngt_finish(struct ftl_mngt_process *mngt)
|
|
{
|
|
mngt->tsc_stop = spdk_get_ticks();
|
|
spdk_thread_send_msg(mngt->caller.thread, finish_msg, mngt);
|
|
}
|
|
|
|
/*
|
|
* Actions
|
|
*/
|
|
static void
|
|
action_next(struct ftl_mngt_process *mngt)
|
|
{
|
|
if (TAILQ_EMPTY(&mngt->action_queue_todo)) {
|
|
/* Nothing to do, finish the management process */
|
|
ftl_mngt_finish(mngt);
|
|
return;
|
|
} else {
|
|
action_done(mngt, 0);
|
|
action_execute(mngt);
|
|
}
|
|
}
|
|
|
|
static void
|
|
action_msg(void *ctx)
|
|
{
|
|
struct ftl_mngt_process *mngt = ctx;
|
|
struct ftl_mngt_step *step;
|
|
|
|
mngt->continuing = false;
|
|
|
|
if (TAILQ_EMPTY(&mngt->action_queue_todo)) {
|
|
ftl_mngt_finish(mngt);
|
|
return;
|
|
}
|
|
|
|
step = TAILQ_FIRST(&mngt->action_queue_todo);
|
|
if (!step->action.start) {
|
|
step->action.start = spdk_get_ticks();
|
|
}
|
|
step->desc->action(mngt->dev, mngt);
|
|
}
|
|
|
|
static void
|
|
action_execute(struct ftl_mngt_process *mngt)
|
|
{
|
|
spdk_thread_send_msg(mngt->dev->core_thread, action_msg, mngt);
|
|
}
|
|
|
|
static void
|
|
action_done(struct ftl_mngt_process *mngt, int status)
|
|
{
|
|
struct ftl_mngt_step *step;
|
|
|
|
assert(!TAILQ_EMPTY(&mngt->action_queue_todo));
|
|
step = TAILQ_FIRST(&mngt->action_queue_todo);
|
|
TAILQ_REMOVE(&mngt->action_queue_todo, step, action.entry);
|
|
|
|
TAILQ_INSERT_TAIL(&mngt->action_queue_done, step, action.entry);
|
|
if (step->desc->cleanup) {
|
|
TAILQ_INSERT_HEAD(&mngt->rollback_queue_todo, step,
|
|
rollback.entry);
|
|
}
|
|
|
|
step->action.stop = spdk_get_ticks();
|
|
step->action.status = status;
|
|
|
|
trace_step(mngt->dev, step, false);
|
|
}
|
|
|
|
/*
|
|
* Rollback
|
|
*/
|
|
static void
|
|
rollback_next(struct ftl_mngt_process *mngt)
|
|
{
|
|
if (TAILQ_EMPTY(&mngt->rollback_queue_todo)) {
|
|
/* Nothing to do, finish the management process */
|
|
ftl_mngt_finish(mngt);
|
|
return;
|
|
} else {
|
|
rollback_done(mngt, 0);
|
|
rollback_execute(mngt);
|
|
}
|
|
}
|
|
|
|
static void
|
|
rollback_msg(void *ctx)
|
|
{
|
|
struct ftl_mngt_process *mngt = ctx;
|
|
struct ftl_mngt_step *step;
|
|
|
|
mngt->continuing = false;
|
|
|
|
if (TAILQ_EMPTY(&mngt->rollback_queue_todo)) {
|
|
ftl_mngt_finish(mngt);
|
|
return;
|
|
}
|
|
|
|
step = TAILQ_FIRST(&mngt->rollback_queue_todo);
|
|
if (!step->rollback.start) {
|
|
step->rollback.start = spdk_get_ticks();
|
|
}
|
|
step->desc->cleanup(mngt->dev, mngt);
|
|
}
|
|
|
|
static void
|
|
rollback_execute(struct ftl_mngt_process *mngt)
|
|
{
|
|
spdk_thread_send_msg(mngt->dev->core_thread, rollback_msg, mngt);
|
|
}
|
|
|
|
void
|
|
rollback_done(struct ftl_mngt_process *mngt, int status)
|
|
{
|
|
struct ftl_mngt_step *step;
|
|
|
|
assert(!TAILQ_EMPTY(&mngt->rollback_queue_todo));
|
|
step = TAILQ_FIRST(&mngt->rollback_queue_todo);
|
|
TAILQ_REMOVE(&mngt->rollback_queue_todo, step, rollback.entry);
|
|
TAILQ_INSERT_TAIL(&mngt->rollback_queue_done, step, rollback.entry);
|
|
|
|
step->rollback.stop = spdk_get_ticks();
|
|
step->rollback.status = status;
|
|
|
|
trace_step(mngt->dev, step, true);
|
|
}
|