Commit fa1fc76c authored by Lukasz Lasek's avatar Lukasz Lasek Committed by Tomasz Zawadzki
Browse files

lib/ftl: API for superblock layout upgrade



Moved the content of ftl_sb.c to ftl_sb_v3.c file.
The ftl_sb.c is now a wrapper to dispatch the SB version and call the
appropriate implementation.
Upon the change of FTL SB layout, a new implementation will be added for
the new version.

This is necessary as FTL SB upgrades were meant to be incremental.
Changing the layout requires the FTL to be able to reference the SB,
regardless of its version, at runtime.

Change-Id: I2ac955b267ff4abb302b95f2da3678ff81345c18
Signed-off-by: default avatarLukasz Lasek <lukasz.lasek@solidigm.com>
Signed-off-by: default avatarMariusz Barczak <Mariusz.Barczak@solidigmtechnology.com>
Signed-off-by: default avatarMateusz Kozlowski <mateusz.kozlowski@solidigm.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/19594


Reviewed-by: default avatarArtur Paszkiewicz <artur.paszkiewicz@intel.com>
Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
Community-CI: Mellanox Build Bot
Reviewed-by: default avatarBen Walker <ben@nvidia.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
parent e50abbe6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ 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 utils/ftl_property.c
C_SRCS += upgrade/ftl_layout_upgrade.c upgrade/ftl_sb_upgrade.c upgrade/ftl_p2l_upgrade.c
C_SRCS += upgrade/ftl_band_upgrade.c upgrade/ftl_chunk_upgrade.c
C_SRCS += upgrade/ftl_sb_v3.c
C_SRCS += nvc/ftl_nvc_dev.c nvc/ftl_nvc_bdev_vss.c
C_SRCS += base/ftl_base_dev.c base/ftl_base_bdev.c

+1 −1
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ struct ftl_layout_region_descriptor {
	/* Number of blocks in FTL_BLOCK_SIZE unit */
	uint64_t blocks;

	struct ftl_superblock_md_region *sb_md_reg;
	struct ftl_superblock_v3_md_region *sb_md_reg;
};

/* Data or metadata region on devices */
+87 −323
Original line number Diff line number Diff line
@@ -7,374 +7,138 @@
#include "ftl_sb.h"
#include "ftl_core.h"
#include "ftl_layout.h"
#include "upgrade/ftl_sb_prev.h"
#include "upgrade/ftl_sb_upgrade.h"
#include "upgrade/ftl_sb_v3.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 ftl_superblock_v2_check_magic(union ftl_superblock_ver *sb_ver);

static bool
md_region_is_fixed(int reg_type)
{
	switch (reg_type) {
	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;
	}
}
struct sb_ops {
	bool (*check_magic)(union ftl_superblock_ver *sb_ver);
	bool (*layout_is_empty)(union ftl_superblock_ver *sb_ver);
	void (*layout_dump)(struct spdk_ftl_dev *dev);

static uint32_t
md_regions_count(void)
{
	uint32_t reg_cnt = 0, reg_type;
	int (*blob_store)(struct spdk_ftl_dev *dev);
	int (*blob_load)(struct spdk_ftl_dev *dev);

	for (reg_type = 0; reg_type < FTL_LAYOUT_REGION_TYPE_MAX; reg_type++) {
		if (md_region_is_fixed(reg_type)) {
			continue;
		}
	int (*upgrade_region)(struct spdk_ftl_dev *dev, struct ftl_layout_region *reg,
			      uint32_t new_version);
};

		reg_cnt++;
	}
	return reg_cnt;
static struct sb_ops *
sb_get_ops(uint64_t version)
{
	static struct sb_ops ops[] = {
		[FTL_SB_VERSION_0] = {
			.check_magic = ftl_superblock_v2_check_magic,
		},
		[FTL_SB_VERSION_1] = {
			.check_magic = ftl_superblock_v2_check_magic,
		},
		[FTL_SB_VERSION_2] = {
			.check_magic = ftl_superblock_v2_check_magic,
		},
		[FTL_SB_VERSION_3] = {
			.check_magic = ftl_superblock_v3_check_magic,
			.layout_is_empty = ftl_superblock_v3_md_layout_is_empty,
			.layout_dump = ftl_superblock_v3_md_layout_dump,
			.blob_store = ftl_superblock_v3_md_layout_build,
			.blob_load = ftl_superblock_v3_md_layout_load_all,
			.upgrade_region = ftl_superblock_v3_md_layout_upgrade_region,
		},
		[FTL_SB_VERSION_4] = {
			.check_magic = ftl_superblock_v3_check_magic,
			.layout_is_empty = ftl_superblock_v3_md_layout_is_empty,
			.layout_dump = ftl_superblock_v3_md_layout_dump,
			.blob_store = ftl_superblock_v3_md_layout_build,
			.blob_load = ftl_superblock_v3_md_layout_load_all,
			.upgrade_region = ftl_superblock_v3_md_layout_upgrade_region,
		},
	};

	if (version >= SPDK_COUNTOF(ops)) {
		return NULL;
	}

	return &ops[version];
}

static bool
superblock_md_region_overflow(struct spdk_ftl_dev *dev, struct ftl_superblock_md_region *sb_reg)
ftl_superblock_v2_check_magic(union ftl_superblock_ver *sb_ver)
{
	/* 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;
	return sb_ver->header.magic == FTL_SUPERBLOCK_MAGIC_V2;
}

	/* 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;
	}
bool
ftl_superblock_check_magic(struct ftl_superblock *sb)
{
	union ftl_superblock_ver *sb_ver = (union ftl_superblock_ver *)sb;
	struct sb_ops *ops = sb_get_ops(sb_ver->header.version);

	if (!ops || !ops->check_magic) {
		ftl_abort();
		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;
	return ops->check_magic(sb_ver);
}

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)
bool
ftl_superblock_md_layout_is_empty(struct ftl_superblock *sb)
{
	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;
	union ftl_superblock_ver *sb_ver = (union ftl_superblock_ver *)sb;
	struct sb_ops *ops = sb_get_ops(sb_ver->header.version);

	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;
	if (!ops || !ops->layout_is_empty) {
		ftl_abort();
		return false;
	}

	(*sb_reg)->df_next = FTL_DF_OBJ_ID_INVALID;

	return 0;
	return ops->layout_is_empty(sb_ver);
}

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;
		}
	struct sb_ops *ops = sb_get_ops(dev->sb->header.version);

		if (superblock_md_layout_add(dev, sb_reg, reg->type, reg->current.version,
					     reg->current.offset, reg->current.blocks)) {
	if (!ops || !ops->blob_store) {
		ftl_abort();
		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;
	return ops->blob_store(dev);
}

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;
	struct sb_ops *ops = sb_get_ops(dev->sb->header.version);

		/* 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");
	if (!ops || !ops->blob_load) {
		ftl_abort();
		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;
	return ops->blob_load(dev);
}

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_layout_region *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;
		}
	struct sb_ops *ops = sb_get_ops(dev->sb->header.version);

		/* 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");
	if (!ops || !ops->upgrade_region) {
		ftl_abort();
		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;
	return ops->upgrade_region(dev, reg, new_version);
}

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);
	struct sb_ops *ops = sb_get_ops(dev->sb->header.version);

		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");
	if (!ops || !ops->layout_dump) {
		ftl_abort();
		return;
	}
	}
	return ops->layout_dump(dev);
}
+2 −1
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include "ftl_sb_current.h"

struct spdk_ftl_dev;
struct ftl_layout_region;

bool ftl_superblock_check_magic(struct ftl_superblock *sb);

@@ -21,7 +22,7 @@ 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);
		struct ftl_layout_region *reg, uint32_t new_version);

void ftl_superblock_md_layout_dump(struct spdk_ftl_dev *dev);

+3 −3
Original line number Diff line number Diff line
@@ -45,15 +45,15 @@ struct ftl_superblock_header {
SPDK_STATIC_ASSERT(sizeof(struct ftl_superblock_header) == 24,
		   "ftl_superblock_header incorrect size");

struct ftl_superblock_md_region {
struct ftl_superblock_v3_md_region {
	uint32_t		type;
	uint32_t		version;
	uint64_t		blk_offs;
	uint64_t		blk_sz;
	ftl_df_obj_id		df_next;
} __attribute__((packed));
SPDK_STATIC_ASSERT(sizeof(struct ftl_superblock_md_region) == 32,
		   "ftl_superblock_md_region incorrect size");
SPDK_STATIC_ASSERT(sizeof(struct ftl_superblock_v3_md_region) == 32,
		   "ftl_superblock_v3_md_region incorrect size");

struct ftl_superblock_shm {
	/* SHM initialization completed */
Loading