Spdk/lib/ftl/ftl_sb.c
paul luse a6dbe3721e update Intel copyright notices
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
2022-11-10 08:28:53 +00:00

383 lines
10 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2022 Intel Corporation.
* All rights reserved.
*/
#include "ftl_sb.h"
#include "ftl_core.h"
#include "ftl_layout.h"
#include "upgrade/ftl_sb_prev.h"
bool
ftl_superblock_check_magic(struct ftl_superblock *sb)
{
if (sb->header.version >= FTL_SB_VERSION_3) {
return sb->header.magic == FTL_SUPERBLOCK_MAGIC;
} else {
return sb->header.magic == FTL_SUPERBLOCK_MAGIC_V2;
}
}
bool
ftl_superblock_md_layout_is_empty(struct ftl_superblock *sb)
{
return sb->md_layout_head.type == FTL_LAYOUT_REGION_TYPE_INVALID;
}
static bool
md_region_is_fixed(int reg_type)
{
switch (reg_type) {
#ifdef SPDK_FTL_VSS_EMU
case FTL_LAYOUT_REGION_TYPE_VSS:
#endif
case FTL_LAYOUT_REGION_TYPE_SB:
case FTL_LAYOUT_REGION_TYPE_SB_BASE:
case FTL_LAYOUT_REGION_TYPE_DATA_BASE:
return true;
default:
return false;
}
}
static uint32_t
md_regions_count(void)
{
uint32_t reg_cnt = 0, reg_type;
for (reg_type = 0; reg_type < FTL_LAYOUT_REGION_TYPE_MAX; reg_type++) {
if (md_region_is_fixed(reg_type)) {
continue;
}
reg_cnt++;
}
return reg_cnt;
}
static bool
superblock_md_region_overflow(struct spdk_ftl_dev *dev, struct ftl_superblock_md_region *sb_reg)
{
/* sb_reg is part of the sb structure - the pointer should be at a positive offset */
if ((uintptr_t)sb_reg < (uintptr_t)dev->sb) {
return true;
}
/* Make sure the entry doesn't overflow the pointer value (probably overkill to check) */
if (UINT64_MAX - (uintptr_t)sb_reg <= sizeof(*sb_reg)) {
return true;
}
/* There's only a finite (FTL_SUPERBLOCK_SIZE) amount of space in the superblock. Make sure the region wholly fits in that space. */
if ((uintptr_t)(sb_reg + 1) > ((uintptr_t)(dev->sb) + FTL_SUPERBLOCK_SIZE)) {
return true;
}
return false;
}
static int
superblock_md_layout_add(struct spdk_ftl_dev *dev, struct ftl_superblock_md_region *sb_reg,
uint32_t reg_type, uint32_t reg_version, uint64_t blk_offs, uint64_t blk_sz)
{
if (superblock_md_region_overflow(dev, sb_reg)) {
FTL_ERRLOG(dev, "Buffer overflow\n");
return -EOVERFLOW;
}
sb_reg->type = reg_type;
sb_reg->version = reg_version;
sb_reg->blk_offs = blk_offs;
sb_reg->blk_sz = blk_sz;
return 0;
}
static int
superblock_md_layout_add_free(struct spdk_ftl_dev *dev, struct ftl_superblock_md_region **sb_reg,
uint32_t reg_type, uint32_t free_type, uint64_t total_blocks)
{
struct ftl_layout *layout = &dev->layout;
struct ftl_layout_region *reg = &layout->region[reg_type];
uint64_t blks_left = total_blocks - reg->current.offset - reg->current.blocks;
if (blks_left == 0) {
return 0;
}
(*sb_reg)->df_next = ftl_df_get_obj_id(dev->sb, (*sb_reg) + 1);
(*sb_reg) = (*sb_reg) + 1;
if (superblock_md_layout_add(dev, *sb_reg, free_type, 0,
reg->current.offset + reg->current.blocks, blks_left)) {
return -1;
}
(*sb_reg)->df_next = FTL_DF_OBJ_ID_INVALID;
return 0;
}
int
ftl_superblock_md_layout_build(struct spdk_ftl_dev *dev)
{
struct ftl_superblock *sb = dev->sb;
struct ftl_layout *layout = &dev->layout;
struct ftl_layout_region *reg;
int n = 0;
bool is_empty = ftl_superblock_md_layout_is_empty(dev->sb);
struct ftl_superblock_md_region *sb_reg = &sb->md_layout_head;
/* TODO: major upgrades: add all free regions being tracked
* For now SB MD layout must be empty - otherwise md free regions may be lost */
assert(is_empty);
for (; n < FTL_LAYOUT_REGION_TYPE_MAX;) {
reg = &layout->region[n];
if (md_region_is_fixed(reg->type)) {
reg->current.sb_md_reg = NULL;
n++;
if (n >= FTL_LAYOUT_REGION_TYPE_MAX) {
/* For VSS emulation the last layout type is a fixed region, we need to move back the list and end the list on previous entry */
sb_reg--;
break;
}
continue;
}
if (superblock_md_layout_add(dev, sb_reg, reg->type, reg->current.version,
reg->current.offset, reg->current.blocks)) {
return -1;
}
reg->current.sb_md_reg = sb_reg;
n++;
if (n < FTL_LAYOUT_REGION_TYPE_MAX) {
/* next region */
sb_reg->df_next = ftl_df_get_obj_id(sb, sb_reg + 1);
sb_reg++;
}
}
/* terminate the list */
sb_reg->df_next = FTL_DF_OBJ_ID_INVALID;
/* create free_nvc/free_base regions on the first run */
if (is_empty) {
superblock_md_layout_add_free(dev, &sb_reg, FTL_LAYOUT_REGION_LAST_NVC,
FTL_LAYOUT_REGION_TYPE_FREE_NVC, layout->nvc.total_blocks);
superblock_md_layout_add_free(dev, &sb_reg, FTL_LAYOUT_REGION_LAST_BASE,
FTL_LAYOUT_REGION_TYPE_FREE_BASE, layout->base.total_blocks);
}
return 0;
}
int
ftl_superblock_md_layout_load_all(struct spdk_ftl_dev *dev)
{
struct ftl_superblock *sb = dev->sb;
struct ftl_superblock_md_region *sb_reg = &sb->md_layout_head;
struct ftl_layout *layout = &dev->layout;
uint32_t regs_found = 0;
for (int n = 0; n < FTL_LAYOUT_REGION_TYPE_MAX; n++) {
if (md_region_is_fixed(sb_reg->type)) {
continue;
}
layout->region[n].current.sb_md_reg = NULL;
layout->region[n].prev = layout->region[n].current;
}
while (sb_reg->type != FTL_LAYOUT_REGION_TYPE_INVALID) {
struct ftl_layout_region *reg;
/* TODO: major upgrades: add free regions tracking */
if (sb_reg->type == FTL_LAYOUT_REGION_TYPE_FREE_NVC ||
sb_reg->type == FTL_LAYOUT_REGION_TYPE_FREE_BASE) {
goto next_sb_reg;
}
if (sb_reg->type >= FTL_LAYOUT_REGION_TYPE_MAX) {
FTL_ERRLOG(dev, "Invalid MD region type found\n");
return -1;
}
if (md_region_is_fixed(sb_reg->type)) {
FTL_ERRLOG(dev, "Unsupported MD region type found\n");
return -1;
}
reg = &layout->region[sb_reg->type];
if (sb_reg->version == reg->current.version) {
if (reg->current.sb_md_reg) {
FTL_ERRLOG(dev, "Multiple/looping current regions found\n");
return -1;
}
reg->current.offset = sb_reg->blk_offs;
reg->current.blocks = sb_reg->blk_sz;
reg->current.sb_md_reg = sb_reg;
if (!reg->prev.sb_md_reg) {
regs_found++;
}
} else {
if (sb_reg->version > reg->current.version) {
FTL_ERRLOG(dev, "Unknown region version found\n");
return -1;
}
/*
* The metadata regions are kept as a linked list, it's therefore possible to have it corrupted and contain loops.
* If the prev region has already been updated to the oldest found version (see following comment/block) and we see
* the same version again, it's a loop and error.
*/
if (sb_reg->version == reg->prev.version) {
FTL_ERRLOG(dev, "Multiple/looping prev regions found\n");
return -1;
}
/*
* The following check is in preparation for major metadata upgrade.
* It's possible that a region will need to be increased in size. It will allocate an additional region in this case
* and upgrade each entries one by one. If in the middle of this upgrade process a dirty shutdown occurs, there will be
* multiple entries of the same metadata region in the superblock (old original and partially updated one). This will then
* effectively rollback the upgrade to the oldest found metadata section and start from the beginning.
*/
if (sb_reg->version < reg->prev.version) {
if ((!reg->current.sb_md_reg) && (!reg->prev.sb_md_reg)) {
regs_found++;
}
reg->prev.offset = sb_reg->blk_offs;
reg->prev.blocks = sb_reg->blk_sz;
reg->prev.version = sb_reg->version;
reg->prev.sb_md_reg = sb_reg;
}
}
next_sb_reg:
if (sb_reg->df_next == FTL_DF_OBJ_ID_INVALID) {
break;
}
if (UINT64_MAX - (uintptr_t)sb <= sb_reg->df_next) {
FTL_ERRLOG(dev, "Buffer overflow\n");
return -EOVERFLOW;
}
sb_reg = ftl_df_get_obj_ptr(sb, sb_reg->df_next);
if (superblock_md_region_overflow(dev, sb_reg)) {
FTL_ERRLOG(dev, "Buffer overflow\n");
return -EOVERFLOW;
}
}
if (regs_found != md_regions_count()) {
FTL_ERRLOG(dev, "Missing regions\n");
return -1;
}
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)
{
struct ftl_superblock *sb = dev->sb;
struct ftl_superblock_md_region *sb_reg = &sb->md_layout_head;
FTL_NOTICELOG(dev, "SB metadata layout:\n");
while (sb_reg->type != FTL_LAYOUT_REGION_TYPE_INVALID) {
FTL_NOTICELOG(dev,
"Region df:0x%"PRIx64" type:0x%"PRIx32" ver:%"PRIu32" blk_offs:0x%"PRIx64" blk_sz:0x%"PRIx64"\n",
ftl_df_get_obj_id(sb, sb_reg), sb_reg->type, sb_reg->version, sb_reg->blk_offs, sb_reg->blk_sz);
if (sb_reg->df_next == FTL_DF_OBJ_ID_INVALID) {
break;
}
sb_reg = ftl_df_get_obj_ptr(sb, sb_reg->df_next);
if (superblock_md_region_overflow(dev, sb_reg)) {
FTL_ERRLOG(dev, "Buffer overflow\n");
return;
}
}
}