Spdk/lib/ftl/upgrade/ftl_layout_upgrade.c

282 lines
6.6 KiB
C
Raw Permalink Normal View History

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2022 Intel Corporation.
* All rights reserved.
*/
#include "ftl_layout_upgrade.h"
#include "ftl_layout.h"
#include "ftl_sb_current.h"
#include "ftl_sb_prev.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[] = {
[FTL_LAYOUT_REGION_TYPE_SB] = {
.count = FTL_SB_VERSION_CURRENT,
.desc = sb_upgrade_desc,
},
[FTL_LAYOUT_REGION_TYPE_SB_BASE] = {
.count = FTL_SB_VERSION_CURRENT,
.desc = sb_upgrade_desc,
},
[FTL_LAYOUT_REGION_TYPE_L2P] = {},
[FTL_LAYOUT_REGION_TYPE_BAND_MD] = {
.count = FTL_BAND_VERSION_CURRENT,
.desc = band_upgrade_desc,
},
[FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR] = {
.count = FTL_BAND_VERSION_CURRENT,
.desc = band_upgrade_desc,
},
[FTL_LAYOUT_REGION_TYPE_VALID_MAP] = {},
[FTL_LAYOUT_REGION_TYPE_NVC_MD] = {
.count = FTL_NVC_VERSION_CURRENT,
.desc = nvc_upgrade_desc,
},
[FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR] = {
.count = FTL_NVC_VERSION_CURRENT,
.desc = nvc_upgrade_desc,
},
[FTL_LAYOUT_REGION_TYPE_DATA_NVC] = {},
[FTL_LAYOUT_REGION_TYPE_DATA_BASE] = {},
[FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC] = {
.count = FTL_P2L_VERSION_CURRENT,
.desc = p2l_upgrade_desc,
},
[FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC_NEXT] = {
.count = FTL_P2L_VERSION_CURRENT,
.desc = p2l_upgrade_desc,
},
[FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP] = {
.count = FTL_P2L_VERSION_CURRENT,
.desc = p2l_upgrade_desc,
},
[FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP_NEXT] = {
.count = FTL_P2L_VERSION_CURRENT,
.desc = p2l_upgrade_desc,
},
[FTL_LAYOUT_REGION_TYPE_TRIM_MD] = {},
[FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR] = {},
#ifdef SPDK_FTL_VSS_EMU
[FTL_LAYOUT_REGION_TYPE_VSS] = {},
#endif
};
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);
}