Commit 44b6d585 authored by Artur Paszkiewicz's avatar Artur Paszkiewicz Committed by Jim Harris
Browse files

FTL: Add helper functions for superblock upgrade



Adds extra functions which will be used during upgrade (changing
versions) of superblock metadata.

Signed-off-by: default avatarKozlowski Mateusz <mateusz.kozlowski@intel.com>
Signed-off-by: default avatarArtur Paszkiewicz <artur.paszkiewicz@intel.com>
Change-Id: I08642deaf509f613cc8b22043dcdded6c329daa9
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13383


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
parent 1bc356bb
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -81,6 +81,8 @@ struct ftl_layout_region_descriptor {

	/* Number of blocks in FTL_BLOCK_SIZE unit */
	uint64_t blocks;

	struct ftl_superblock_md_region *sb_md_reg;
};

/* Data or metadata region on devices */
+284 −0
Original line number Diff line number Diff line
@@ -12,3 +12,287 @@ ftl_superblock_check_magic(struct ftl_superblock *sb)
{
	return sb->header.magic == FTL_SUPERBLOCK_MAGIC;
}
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;
	}

	/* 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;
	}

	/* Make sure the entry doesn't overflow the pointer value (probably overkill to check) */
	if ((uintptr_t)(sb_reg + 1) < (uintptr_t)sb_reg) {
		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;
		}

		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;
}

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;
		}
	}
}
+8 −0
Original line number Diff line number Diff line
@@ -14,4 +14,12 @@ struct spdk_ftl_dev;

bool ftl_superblock_check_magic(struct ftl_superblock *sb);

bool ftl_superblock_md_layout_is_empty(struct ftl_superblock *sb);

int ftl_superblock_md_layout_build(struct spdk_ftl_dev *dev);

int ftl_superblock_md_layout_load_all(struct spdk_ftl_dev *dev);

void ftl_superblock_md_layout_dump(struct spdk_ftl_dev *dev);

#endif /* FTL_SB_H */
+2 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@

#include "spdk/stdinc.h"
#include "utils/ftl_defs.h"
#include "utils/ftl_df.h"

/* Size of superblock on NV cache, make it bigger for future fields */
#define FTL_SUPERBLOCK_SIZE (128ULL * KiB)
@@ -45,6 +46,7 @@ struct ftl_superblock_md_region {
	uint32_t		version;
	uint64_t		blk_offs;
	uint64_t		blk_sz;
	ftl_df_obj_id		df_next;
};

struct ftl_superblock_shm {
+1 −0
Original line number Diff line number Diff line
@@ -373,6 +373,7 @@ ftl_mngt_init_default_sb(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt
	/* md layout isn't initialized yet.
	 * empty region list => all regions in the default location */
	sb->md_layout_head.type = FTL_LAYOUT_REGION_TYPE_INVALID;
	sb->md_layout_head.df_next = FTL_DF_OBJ_ID_INVALID;

	sb->header.crc = get_sb_crc(sb);