Spdk/lib/ftl/mngt/ftl_mngt.c

570 lines
12 KiB
C
Raw Normal View History

/* 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);
}