From 7ff285193f5dfa8d4f648289d31898114196179b Mon Sep 17 00:00:00 2001 From: Kozlowski Mateusz Date: Fri, 27 May 2022 14:57:35 +0200 Subject: [PATCH] ftl: Add metadata upgrade framework Added the ability for minor metadata upgrade - updating the internal fields of metadata structures, without changing the overall layout. Signed-off-by: Kozlowski Mateusz Signed-off-by: Artur Paszkiewicz Change-Id: Iec98c62b45b099d6d476d486ba7e4ff6b648bb95 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13384 Tested-by: SPDK CI Jenkins Reviewed-by: Jim Harris Reviewed-by: Ben Walker --- lib/ftl/Makefile | 5 +- lib/ftl/ftl_sb.c | 74 ++++++++ lib/ftl/ftl_sb.h | 3 + lib/ftl/mngt/ftl_mngt_md.c | 8 + lib/ftl/mngt/ftl_mngt_steps.h | 6 + lib/ftl/mngt/ftl_mngt_upgrade.c | 153 ++++++++++++++++ lib/ftl/upgrade/ftl_layout_upgrade.c | 250 +++++++++++++++++++++++++++ lib/ftl/upgrade/ftl_layout_upgrade.h | 178 +++++++++++++++++++ 8 files changed, 675 insertions(+), 2 deletions(-) create mode 100644 lib/ftl/mngt/ftl_mngt_upgrade.c create mode 100644 lib/ftl/upgrade/ftl_layout_upgrade.c create mode 100644 lib/ftl/upgrade/ftl_layout_upgrade.h diff --git a/lib/ftl/Makefile b/lib/ftl/Makefile index 2524681a1..851805e2d 100644 --- a/lib/ftl/Makefile +++ b/lib/ftl/Makefile @@ -23,7 +23,7 @@ endif CFLAGS += -I. -FTL_SUBDIRS := mngt utils +FTL_SUBDIRS := mngt utils upgrade C_SRCS = ftl_core.c ftl_init.c ftl_layout.c ftl_debug.c ftl_io.c ftl_sb.c ftl_l2p.c ftl_l2p_flat.c C_SRCS += ftl_nv_cache.c ftl_band.c ftl_band_ops.c ftl_writer.c ftl_rq.c ftl_reloc.c ftl_l2p_cache.c @@ -31,8 +31,9 @@ C_SRCS += ftl_p2l.c C_SRCS += mngt/ftl_mngt.c mngt/ftl_mngt_bdev.c mngt/ftl_mngt_shutdown.c mngt/ftl_mngt_startup.c C_SRCS += mngt/ftl_mngt_md.c mngt/ftl_mngt_misc.c mngt/ftl_mngt_ioch.c mngt/ftl_mngt_l2p.c C_SRCS += mngt/ftl_mngt_band.c mngt/ftl_mngt_self_test.c mngt/ftl_mngt_p2l.c -C_SRCS += mngt/ftl_mngt_recovery.c +C_SRCS += mngt/ftl_mngt_recovery.c mngt/ftl_mngt_upgrade.c C_SRCS += utils/ftl_conf.c utils/ftl_md.c utils/ftl_mempool.c utils/ftl_bitmap.c +C_SRCS += upgrade/ftl_layout_upgrade.c SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_ftl.map) diff --git a/lib/ftl/ftl_sb.c b/lib/ftl/ftl_sb.c index d60405c3a..76d5a9213 100644 --- a/lib/ftl/ftl_sb.c +++ b/lib/ftl/ftl_sb.c @@ -273,6 +273,80 @@ next_sb_reg: return 0; } +static void +ftl_superblock_md_layout_free_region(struct spdk_ftl_dev *dev, + struct ftl_superblock_md_region *sb_reg) +{ + /* TODO: major upgrades: implement */ + sb_reg->type = FTL_LAYOUT_REGION_TYPE_FREE_NVC; +} + +int +ftl_superblock_md_layout_upgrade_region(struct spdk_ftl_dev *dev, + struct ftl_superblock_md_region *sb_reg, uint32_t new_version) +{ + struct ftl_superblock *sb = dev->sb; + struct ftl_superblock_md_region *sb_reg_iter = &sb->md_layout_head; + struct ftl_layout *layout = &dev->layout; + struct ftl_layout_region *reg = &layout->region[sb_reg->type]; + uint32_t old_version = sb_reg->version; + + assert(sb_reg); + assert(reg->prev.sb_md_reg == sb_reg); + assert(new_version > old_version); + + while (sb_reg_iter->type != FTL_LAYOUT_REGION_TYPE_INVALID) { + if (sb_reg_iter->type != sb_reg->type) { + goto next_sb_reg_iter; + } + + /* Verify all region versions up to new_version are updated: */ + if (sb_reg_iter->version != old_version && sb_reg_iter->version < new_version) { + FTL_ERRLOG(dev, "Region upgrade skipped\n"); + return -1; + } + + if (sb_reg_iter->version == new_version) { + /* Major upgrades: update region prev version to the new version region found */ + assert(sb_reg != sb_reg_iter); + reg->prev.offset = sb_reg_iter->blk_offs; + reg->prev.blocks = sb_reg_iter->blk_sz; + reg->prev.version = sb_reg_iter->version; + reg->prev.sb_md_reg = sb_reg_iter; + + ftl_superblock_md_layout_free_region(dev, sb_reg); + goto exit; + } + +next_sb_reg_iter: + if (sb_reg_iter->df_next == FTL_DF_OBJ_ID_INVALID) { + break; + } + + sb_reg_iter = ftl_df_get_obj_ptr(sb, sb_reg_iter->df_next); + if (superblock_md_region_overflow(dev, sb_reg_iter)) { + FTL_ERRLOG(dev, "Buffer overflow\n"); + return -EOVERFLOW; + } + } + + /* Minor upgrades: update the region in place (only the new version) */ + assert(sb_reg == reg->prev.sb_md_reg); + sb_reg->version = new_version; + reg->prev.version = new_version; + +exit: + /* Update the region current version */ + if (new_version == reg->current.version) { + reg->current.offset = sb_reg->blk_offs; + reg->current.blocks = sb_reg->blk_sz; + reg->current.version = sb_reg->version; + reg->current.sb_md_reg = sb_reg; + } + + return 0; +} + void ftl_superblock_md_layout_dump(struct spdk_ftl_dev *dev) { diff --git a/lib/ftl/ftl_sb.h b/lib/ftl/ftl_sb.h index 1615b0b75..ac2ae8922 100644 --- a/lib/ftl/ftl_sb.h +++ b/lib/ftl/ftl_sb.h @@ -20,6 +20,9 @@ int ftl_superblock_md_layout_build(struct spdk_ftl_dev *dev); int ftl_superblock_md_layout_load_all(struct spdk_ftl_dev *dev); +int ftl_superblock_md_layout_upgrade_region(struct spdk_ftl_dev *dev, + struct ftl_superblock_md_region *sb_reg, uint32_t new_version); + void ftl_superblock_md_layout_dump(struct spdk_ftl_dev *dev); #endif /* FTL_SB_H */ diff --git a/lib/ftl/mngt/ftl_mngt_md.c b/lib/ftl/mngt/ftl_mngt_md.c index 14bd5a127..c03b801e1 100644 --- a/lib/ftl/mngt/ftl_mngt_md.c +++ b/lib/ftl/mngt/ftl_mngt_md.c @@ -13,6 +13,7 @@ #include "ftl_band.h" #include "ftl_internal.h" #include "ftl_sb.h" +#include "upgrade/ftl_layout_upgrade.h" void ftl_mngt_init_layout(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) @@ -747,3 +748,10 @@ ftl_mngt_restore_md(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) { ftl_mngt_call_process(mngt, &desc_restore); } + +void +ftl_mngt_persist_superblock(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) +{ + dev->sb->header.crc = get_sb_crc(dev->sb); + persist(dev, mngt, FTL_LAYOUT_REGION_TYPE_SB); +} diff --git a/lib/ftl/mngt/ftl_mngt_steps.h b/lib/ftl/mngt/ftl_mngt_steps.h index 197b64e59..11894a8b9 100644 --- a/lib/ftl/mngt/ftl_mngt_steps.h +++ b/lib/ftl/mngt/ftl_mngt_steps.h @@ -82,6 +82,10 @@ void ftl_mngt_persist_l2p(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mng void ftl_mngt_init_layout(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); +void ftl_mngt_layout_verify(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); + +void ftl_mngt_layout_upgrade(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); + void ftl_mngt_init_md(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); void ftl_mngt_deinit_md(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); @@ -136,4 +140,6 @@ void ftl_mngt_persist_band_info_metadata(struct spdk_ftl_dev *dev, struct ftl_mn void ftl_mngt_persist_nv_cache_metadata(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); +void ftl_mngt_persist_superblock(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); + #endif /* FTL_MNGT_STEPS_H */ diff --git a/lib/ftl/mngt/ftl_mngt_upgrade.c b/lib/ftl/mngt/ftl_mngt_upgrade.c new file mode 100644 index 000000000..3d64987d4 --- /dev/null +++ b/lib/ftl/mngt/ftl_mngt_upgrade.c @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) Intel Corporation. + * All rights reserved. + */ + +#include "ftl_core.h" +#include "ftl_mngt.h" +#include "ftl_mngt_steps.h" +#include "upgrade/ftl_layout_upgrade.h" + +struct ftl_mngt_upgrade_ctx { + struct ftl_mngt_process *parent; + struct ftl_mngt_process *mngt; + struct ftl_layout_upgrade_ctx upgrade_ctx; +}; + +static void +region_upgrade_cb(struct spdk_ftl_dev *dev, void *_ctx, int status) +{ + struct ftl_mngt_upgrade_ctx *ctx = _ctx; + + free(ctx->upgrade_ctx.ctx); + ctx->upgrade_ctx.ctx = NULL; + + if (status) { + FTL_ERRLOG(dev, "Upgrade failed for region %d (rc=%d)\n", ctx->upgrade_ctx.reg->type, status); + ftl_mngt_fail_step(ctx->mngt); + } else { + ftl_mngt_next_step(ctx->mngt); + } +} + +static void +region_upgrade(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) +{ + struct ftl_mngt_upgrade_ctx *ctx = ftl_mngt_get_caller_ctx(mngt); + struct ftl_layout_upgrade_ctx *upgrade_ctx = &ctx->upgrade_ctx; + size_t ctx_size = upgrade_ctx->upgrade->desc[upgrade_ctx->reg->prev.version].ctx_size; + int rc = -1; + + assert(upgrade_ctx->ctx == NULL); + if (ctx_size) { + upgrade_ctx->ctx = calloc(1, ctx_size); + if (!upgrade_ctx->ctx) { + goto exit; + } + } + upgrade_ctx->cb = region_upgrade_cb; + upgrade_ctx->cb_ctx = ctx; + ctx->mngt = mngt; + + rc = ftl_region_upgrade(dev, upgrade_ctx); +exit: + if (rc) { + region_upgrade_cb(dev, ctx, rc); + } +} + +static const struct ftl_mngt_process_desc desc_region_upgrade = { + .name = "FTL region upgrade", + .steps = { + { + .name = "Region upgrade", + .action = region_upgrade, + }, + { + .name = "Persist superblock", + .action = ftl_mngt_persist_superblock, + }, + {} + } +}; + +static void +layout_upgrade_cb(struct spdk_ftl_dev *dev, void *_ctx, int status) +{ + struct ftl_mngt_upgrade_ctx *ctx = _ctx; + + if (status) { + free(ctx->upgrade_ctx.ctx); + ctx->upgrade_ctx.ctx = NULL; + ftl_mngt_fail_step(ctx->parent); + return; + } + + /* go back to ftl_mngt_upgrade() step and select the next region/version to upgrade */ + ftl_mngt_continue_step(ctx->parent); +} + +static void +layout_upgrade(struct spdk_ftl_dev *dev, struct ftl_mngt_process *parent) +{ + struct ftl_mngt_upgrade_ctx *ctx = ftl_mngt_get_process_ctx(parent); + int rc; + + ctx->parent = parent; + rc = ftl_layout_upgrade_init_ctx(dev, &ctx->upgrade_ctx); + + switch (rc) { + case FTL_LAYOUT_UPGRADE_CONTINUE: + if (!ftl_mngt_process_execute(dev, &desc_region_upgrade, layout_upgrade_cb, ctx)) { + return; + } + + ftl_mngt_fail_step(parent); + break; + + case FTL_LAYOUT_UPGRADE_DONE: + if (ftl_upgrade_layout_dump(dev)) { + FTL_ERRLOG(dev, "MD layout verification failed after upgrade.\n"); + ftl_mngt_fail_step(parent); + } else { + ftl_mngt_next_step(parent); + } + break; + + case FTL_LAYOUT_UPGRADE_FAULT: + ftl_mngt_fail_step(parent); + break; + } + if (ctx->upgrade_ctx.ctx) { + free(ctx->upgrade_ctx.ctx); + } +} + +static const struct ftl_mngt_process_desc desc_layout_upgrade = { + .name = "FTL layout upgrade", + .ctx_size = sizeof(struct ftl_mngt_upgrade_ctx), + .steps = { + { + .name = "Layout upgrade", + .action = layout_upgrade, + }, + {} + } +}; + + +void +ftl_mngt_layout_verify(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) +{ + if (ftl_layout_verify(dev)) { + ftl_mngt_fail_step(mngt); + } else { + ftl_mngt_next_step(mngt); + } +} + +void +ftl_mngt_layout_upgrade(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) +{ + ftl_mngt_call_process(mngt, &desc_layout_upgrade); +} diff --git a/lib/ftl/upgrade/ftl_layout_upgrade.c b/lib/ftl/upgrade/ftl_layout_upgrade.c new file mode 100644 index 000000000..7a68b2156 --- /dev/null +++ b/lib/ftl/upgrade/ftl_layout_upgrade.c @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) Intel Corporation. + * All rights reserved. + */ + +#include "ftl_layout_upgrade.h" +#include "ftl_layout.h" +#include "ftl_sb_current.h" +#include "ftl_core.h" +#include "ftl_band.h" + +int +ftl_region_upgrade_disabled(struct spdk_ftl_dev *dev, struct ftl_layout_region *region) +{ + return -1; +} + +int +ftl_region_upgrade_enabled(struct spdk_ftl_dev *dev, struct ftl_layout_region *region) +{ + if (!(dev->sb->clean == 1 && dev->sb_shm->shm_clean == 0)) { + FTL_ERRLOG(dev, "FTL region upgrade: SB dirty\n"); + return -1; + } + return 0; +} + +#ifndef UTEST +extern struct ftl_region_upgrade_desc sb_upgrade_desc[]; +extern struct ftl_region_upgrade_desc p2l_upgrade_desc[]; +extern struct ftl_region_upgrade_desc nvc_upgrade_desc[]; +extern struct ftl_region_upgrade_desc band_upgrade_desc[]; + +static struct ftl_layout_upgrade_desc_list layout_upgrade_desc[] = { +#ifdef SPDK_FTL_VSS_EMU + [FTL_LAYOUT_REGION_TYPE_VSS] = {}, +#endif + [FTL_LAYOUT_REGION_TYPE_SB] = {}, + [FTL_LAYOUT_REGION_TYPE_SB_BASE] = {}, + [FTL_LAYOUT_REGION_TYPE_L2P] = {}, + [FTL_LAYOUT_REGION_TYPE_BAND_MD] = {}, + [FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR] = {}, + [FTL_LAYOUT_REGION_TYPE_VALID_MAP] = {}, + [FTL_LAYOUT_REGION_TYPE_NVC_MD] = {}, + [FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR] = {}, + [FTL_LAYOUT_REGION_TYPE_DATA_NVC] = {}, + [FTL_LAYOUT_REGION_TYPE_DATA_BASE] = {}, + [FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC] = {}, + [FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC_NEXT] = {}, + [FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP] = {}, + [FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP_NEXT] = {}, + [FTL_LAYOUT_REGION_TYPE_TRIM_MD] = {}, + [FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR] = {}, +}; + +SPDK_STATIC_ASSERT(sizeof(layout_upgrade_desc) / sizeof(*layout_upgrade_desc) == + FTL_LAYOUT_REGION_TYPE_MAX, + "Missing layout upgrade descriptors"); +#endif + +static int +region_verify(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *ctx) +{ + uint64_t ver; + + assert(ctx->reg); + assert(ctx->reg->current.version == ctx->upgrade->count); + ver = ctx->reg->prev.version; + if (ver > ctx->upgrade->count) { + FTL_ERRLOG(dev, "Unknown region version\n"); + return -1; + } + + while (ver < ctx->reg->current.version) { + int rc = ctx->upgrade->desc[ver].verify(dev, ctx->reg); + if (rc) { + return rc; + } + ver = ctx->upgrade->desc[ver].new_version; + } + return 0; +} + +int +ftl_region_upgrade(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *ctx) +{ + int rc = 0; + uint64_t ver; + + assert(ctx->reg); + assert(ctx->reg->prev.version <= ctx->reg->current.version); + assert(ctx->reg->current.version == ctx->upgrade->count); + ver = ctx->reg->prev.version; + if (ver < ctx->upgrade->count) { + ctx->next_reg_ver = ctx->upgrade->desc[ver].new_version; + rc = ctx->upgrade->desc[ver].upgrade(dev, ctx); + } + return rc; +} + +void +ftl_region_upgrade_completed(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *ctx, + int status) +{ + assert(ctx->reg); + assert(ctx->reg->prev.version < ctx->next_reg_ver); + assert(ctx->next_reg_ver <= ctx->reg->current.version); + + /* SB MD isn't tracked in SB MD region list */ + if (!status) { + if (ctx->reg->prev.sb_md_reg) { + int rc = ftl_superblock_md_layout_upgrade_region(dev, ctx->reg->prev.sb_md_reg, ctx->next_reg_ver); + ftl_bug(rc != 0); + } + + ctx->reg->prev.version = ctx->next_reg_ver; + } + + if (ctx->cb) { + ctx->cb(dev, ctx->cb_ctx, status); + } +} + +int +ftl_layout_verify(struct spdk_ftl_dev *dev) +{ + int rc = 0; + struct ftl_layout *layout = &dev->layout; + struct ftl_layout_upgrade_ctx ctx = {0}; + + if (ftl_superblock_md_layout_is_empty(dev->sb)) { + rc = ftl_superblock_md_layout_build(dev); + goto exit; + } + + if (ftl_superblock_md_layout_load_all(dev)) { + return -1; + } + + if (ftl_validate_regions(dev, layout)) { + return -1; + } + + ctx.reg = &dev->layout.region[0]; + ctx.upgrade = &layout_upgrade_desc[0]; + + while (true) { + if (region_verify(dev, &ctx)) { + return -1; + } + + if (ctx.reg->type == FTL_LAYOUT_REGION_TYPE_MAX - 1) { + break; + } + + ctx.reg++; + ctx.upgrade++; + } + +exit: + return rc; +} + +int +ftl_upgrade_layout_dump(struct spdk_ftl_dev *dev) +{ + if (ftl_validate_regions(dev, &dev->layout)) { + return -1; + } + + ftl_layout_dump(dev); + ftl_superblock_md_layout_dump(dev); + return 0; +} + +int +ftl_superblock_upgrade(struct spdk_ftl_dev *dev) +{ + struct ftl_layout_upgrade_ctx ctx = {0}; + struct ftl_layout_region *reg = &dev->layout.region[FTL_LAYOUT_REGION_TYPE_SB]; + int rc; + + ctx.reg = reg; + ctx.upgrade = &layout_upgrade_desc[FTL_LAYOUT_REGION_TYPE_SB]; + reg->prev.version = dev->sb->header.version; + + rc = region_verify(dev, &ctx); + if (rc) { + return rc; + } + + while (reg->prev.version < reg->current.version) { + rc = ftl_region_upgrade(dev, &ctx); + if (rc) { + return rc; + } + /* SB upgrades are all synchronous */ + ftl_region_upgrade_completed(dev, &ctx, rc); + } + + dev->layout.region[FTL_LAYOUT_REGION_TYPE_SB_BASE].prev.version = reg->prev.version; + return 0; +} + +static int +layout_upgrade_select_next_region(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *ctx) +{ + struct ftl_layout_region *reg; + uint64_t reg_ver; + uint32_t reg_type = ctx->reg->type; + + while (reg_type != FTL_LAYOUT_REGION_TYPE_MAX) { + assert(ctx->reg); + assert(ctx->upgrade); + reg = ctx->reg; + reg_ver = reg->prev.version; + + if (reg_ver < reg->current.version) { + /* qualify region version to upgrade */ + return FTL_LAYOUT_UPGRADE_CONTINUE; + } else if (reg_ver == reg->current.version) { + /* select the next region to upgrade */ + reg_type++; + if (reg_type == FTL_LAYOUT_REGION_TYPE_MAX) { + break; + } + ctx->reg++; + ctx->upgrade++; + } else { + /* unknown version */ + assert(reg_ver > reg->current.version); + FTL_ERRLOG(dev, "Region %d upgrade fault: version %"PRIu64"/%"PRIu64"\n", reg_type, reg_ver, + reg->current.version); + return FTL_LAYOUT_UPGRADE_FAULT; + } + } + + return FTL_LAYOUT_UPGRADE_DONE; +} + +int +ftl_layout_upgrade_init_ctx(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *ctx) +{ + if (!ctx->reg) { + ctx->reg = &dev->layout.region[0]; + ctx->upgrade = &layout_upgrade_desc[0]; + } + + return layout_upgrade_select_next_region(dev, ctx); +} diff --git a/lib/ftl/upgrade/ftl_layout_upgrade.h b/lib/ftl/upgrade/ftl_layout_upgrade.h new file mode 100644 index 000000000..ed7126281 --- /dev/null +++ b/lib/ftl/upgrade/ftl_layout_upgrade.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) Intel Corporation. + * All rights reserved. + */ + +#ifndef FTL_LAYOUT_UPGRADE_H +#define FTL_LAYOUT_UPGRADE_H + +#include "ftl_core.h" +#include "ftl_layout.h" + +struct spdk_ftl_dev; +struct ftl_layout_region; +struct ftl_layout_upgrade_ctx; + +enum ftl_layout_upgrade_result { + /* Continue with the selected region upgrade */ + FTL_LAYOUT_UPGRADE_CONTINUE = 0, + + /* Layout upgrade done */ + FTL_LAYOUT_UPGRADE_DONE, + + /* Layout upgrade fault */ + FTL_LAYOUT_UPGRADE_FAULT, +}; + +/* MD region upgrade verify fn: return 0 on success */ +typedef int (*ftl_region_upgrade_verify_fn)(struct spdk_ftl_dev *dev, + struct ftl_layout_region *region); + +/* MD region upgrade fn: return 0 on success */ +typedef int (*ftl_region_upgrade_fn)(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *ctx); + +/* MD region upgrade descriptor */ +struct ftl_region_upgrade_desc { + /* Qualifies the region for upgrade */ + ftl_region_upgrade_verify_fn verify; + + /* Upgrades the region */ + ftl_region_upgrade_fn upgrade; + + /* New region version (i.e. after the upgrade) */ + uint32_t new_version; + + /* Context buffer allocated for upgrade() */ + size_t ctx_size; +}; + +/* MD layout upgrade descriptor - one instance contains information about the upgrade steps necessary + * for updating to latest metadata version. For example version 0 of metadata region will have no descriptors + * (as it's by definition up to date and there's no need to upgrade it), while version 2 will have 2 (0 -> 1 + * and 1 -> 2). + */ +struct ftl_layout_upgrade_desc_list { + /* # of entries in the region upgrade descriptor */ + size_t count; + + /* Region upgrade descriptor */ + struct ftl_region_upgrade_desc *desc; +}; + +/* Region upgrade callback */ +typedef void (*ftl_region_upgrade_cb)(struct spdk_ftl_dev *dev, void *ctx, int status); + +/* MD layout upgrade context */ +struct ftl_layout_upgrade_ctx { + /* MD region being upgraded */ + struct ftl_layout_region *reg; + + /* MD region upgrade descriptor */ + struct ftl_layout_upgrade_desc_list *upgrade; + + /* New region version (i.e. after the upgrade) */ + uint64_t next_reg_ver; + + /* Context buffer for the region upgrade */ + void *ctx; + + /* Region upgrade callback */ + ftl_region_upgrade_cb cb; + + /* Ctx for the region upgrade callback */ + void *cb_ctx; +}; + +/** + * @brief Disable region upgrade for particular version. + * + * @param dev FTL device + * @param region FTL layout region descriptor + * @return int -1 + */ +int ftl_region_upgrade_disabled(struct spdk_ftl_dev *dev, struct ftl_layout_region *region); + +/** + * @brief Enable region upgrade for particular version. + * + * @param dev FTL device + * @param region FTL layout region descriptor + * @return int -1 (i.e. disable) if SB is dirty or SHM clean, 0 otherwise (i.e. enable) + */ +int ftl_region_upgrade_enabled(struct spdk_ftl_dev *dev, struct ftl_layout_region *region); + +/** + * @brief Upgrade the superblock. + * + * This call is sychronous. + * + * @param dev FTL device + * @return int 0: success, error code otherwise + */ +int ftl_superblock_upgrade(struct spdk_ftl_dev *dev); + +/** + * @brief Qualify the MD layout for upgrade. + * + * The SB MD layout is built or loaded. + * If loaded, walk through all MD layout and filter out all MD regions that need an upgrade. + * Call .verify() on region upgrade descriptors for all such regions. + * + * @param dev FTL device + * @return int 0: success, error code otherwise + */ +int ftl_layout_verify(struct spdk_ftl_dev *dev); + +/** + * @brief Dump the FTL layout + * + * Verify MD layout in terms of region overlaps. + * + * @param dev FTL device + * @return int 0: success, error code otherwise + */ +int ftl_upgrade_layout_dump(struct spdk_ftl_dev *dev); + +/** + * @brief Upgrade the MD region. + * + * Call .upgrade() on the selected region upgrade descriptor. + * When returned 0, this call is asynchronous. + * The .upgrade() is expected to convert and persist the metadata. + * When that's done, the call to ftl_region_upgrade_completed() is expected + * to continue with the layout upgrade. + * + * When returned an error code, the caller is responsible to abort the mngt pipeline. + * + * @param dev FTL device + * @param ctx Layout upgrade context + * @return int 0: upgrade in progress, error code otherwise + */ +int ftl_region_upgrade(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *ctx); + +/** + * @brief Called when MD region upgrade is completed - see ftl_region_upgrade(). + * + * Upgrades the SB MD layout and region's prev version descriptor to the just upgraded version. + * Executes the layout upgrade owner's callback (mngt region_upgrade_cb()) to continue + * with the layout upgrade. + * + * @param dev FTL device + * @param ctx Layout upgrade context + * @param status Region upgrade status: 0: success, error code otherwise + */ +void ftl_region_upgrade_completed(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *ctx, + int status); + +/** + * @brief Initialize the layout upgrade context. + * + * Select the next region to be upgraded. + * + * @param dev FTL device + * @param ctx Layout upgrade context + * @return int see enum ftl_layout_upgrade_result + */ +int ftl_layout_upgrade_init_ctx(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *ctx); + +#endif /* FTL_LAYOUT_UPGRADE_H */