2022-05-27 12:57:35 +00:00
|
|
|
/* SPDX-License-Identifier: BSD-3-Clause
|
2022-11-01 20:26:26 +00:00
|
|
|
* Copyright (C) 2022 Intel Corporation.
|
2022-05-27 12:57:35 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ftl_layout_upgrade.h"
|
|
|
|
#include "ftl_layout.h"
|
|
|
|
#include "ftl_sb_current.h"
|
2022-05-25 10:13:38 +00:00
|
|
|
#include "ftl_sb_prev.h"
|
2022-05-27 12:57:35 +00:00
|
|
|
#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[] = {
|
2022-05-25 10:13:38 +00:00
|
|
|
[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,
|
|
|
|
},
|
2022-05-27 12:57:35 +00:00
|
|
|
[FTL_LAYOUT_REGION_TYPE_L2P] = {},
|
2022-05-25 09:55:36 +00:00
|
|
|
[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,
|
|
|
|
},
|
2022-05-27 12:57:35 +00:00
|
|
|
[FTL_LAYOUT_REGION_TYPE_VALID_MAP] = {},
|
2022-05-25 09:55:36 +00:00
|
|
|
[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,
|
|
|
|
},
|
2022-05-27 12:57:35 +00:00
|
|
|
[FTL_LAYOUT_REGION_TYPE_DATA_NVC] = {},
|
|
|
|
[FTL_LAYOUT_REGION_TYPE_DATA_BASE] = {},
|
2022-05-25 09:55:36 +00:00
|
|
|
[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,
|
|
|
|
},
|
2022-05-27 12:57:35 +00:00
|
|
|
[FTL_LAYOUT_REGION_TYPE_TRIM_MD] = {},
|
|
|
|
[FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR] = {},
|
2022-07-08 23:14:11 +00:00
|
|
|
#ifdef SPDK_FTL_VSS_EMU
|
|
|
|
[FTL_LAYOUT_REGION_TYPE_VSS] = {},
|
|
|
|
#endif
|
2022-05-27 12:57:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|