Commit cbd7ae6d authored by Kozlowski Mateusz's avatar Kozlowski Mateusz Committed by Ben Walker
Browse files

FTL: Add metadata restore functionality



Adds necessary functions for setting up the state of FTL components
based on loaded in metadata.

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


Community-CI: Mellanox Build Bot
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 55147295
Loading
Loading
Loading
Loading
+31 −2
Original line number Original line Diff line number Diff line
@@ -72,6 +72,8 @@ _ftl_band_set_free(struct ftl_band *band)


	dev->num_free++;
	dev->num_free++;
	ftl_apply_limits(dev);
	ftl_apply_limits(dev);

	band->md->p2l_map_checksum = 0;
}
}


static void
static void
@@ -137,8 +139,6 @@ ftl_band_set_state(struct ftl_band *band, enum ftl_band_state state)
	case FTL_BAND_STATE_FREE:
	case FTL_BAND_STATE_FREE:
		assert(band->md->state == FTL_BAND_STATE_CLOSED);
		assert(band->md->state == FTL_BAND_STATE_CLOSED);
		_ftl_band_set_free(band);
		_ftl_band_set_free(band);

		band->md->p2l_map_checksum = 0;
		break;
		break;


	case FTL_BAND_STATE_PREP:
	case FTL_BAND_STATE_PREP:
@@ -626,3 +626,32 @@ ftl_band_init_gc_iter(struct spdk_ftl_dev *dev)
	/* We lost GC state due to dirty shutdown, reset GC state to start over */
	/* We lost GC state due to dirty shutdown, reset GC state to start over */
	ftl_band_reset_gc_iter(dev);
	ftl_band_reset_gc_iter(dev);
}
}

void
ftl_valid_map_load_state(struct spdk_ftl_dev *dev)
{
	uint64_t i;
	struct ftl_band *band;

	for (i = 0; i < dev->num_bands; i++) {
		band = &dev->bands[i];
		band->p2l_map.num_valid = ftl_bitmap_count_set(band->p2l_map.valid);
	}
}

void
ftl_bands_load_state(struct spdk_ftl_dev *dev)
{
	uint64_t i;
	struct ftl_band *band;

	for (i = 0; i < dev->num_bands; i++) {
		band = &dev->bands[i];

		if (band->md->state == FTL_BAND_STATE_FREE) {
			/* All bands start on the shut list during startup, removing it manually here */
			TAILQ_REMOVE(&dev->shut_bands, band, queue_entry);
			_ftl_band_set_free(band);
		}
	}
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -160,6 +160,8 @@ size_t ftl_p2l_map_pool_elem_size(struct spdk_ftl_dev *dev);
struct ftl_band *ftl_band_search_next_to_reloc(struct spdk_ftl_dev *dev);
struct ftl_band *ftl_band_search_next_to_reloc(struct spdk_ftl_dev *dev);
void ftl_band_init_gc_iter(struct spdk_ftl_dev *dev);
void ftl_band_init_gc_iter(struct spdk_ftl_dev *dev);
ftl_addr ftl_band_p2l_map_addr(struct ftl_band *band);
ftl_addr ftl_band_p2l_map_addr(struct ftl_band *band);
void ftl_valid_map_load_state(struct spdk_ftl_dev *dev);
void ftl_bands_load_state(struct spdk_ftl_dev *dev);
void ftl_band_open(struct ftl_band *band, enum ftl_band_type type);
void ftl_band_open(struct ftl_band *band, enum ftl_band_type type);
void ftl_band_close(struct ftl_band *band);
void ftl_band_close(struct ftl_band *band);
void ftl_band_free(struct ftl_band *band);
void ftl_band_free(struct ftl_band *band);
+68 −0
Original line number Original line Diff line number Diff line
@@ -1343,6 +1343,74 @@ chunk_alloc_p2l_map(struct ftl_nv_cache_chunk *chunk)
	return 0;
	return 0;
}
}


int
ftl_nv_cache_load_state(struct ftl_nv_cache *nv_cache)
{
	struct ftl_nv_cache_chunk *chunk;
	uint64_t chunks_number, offset, i;
	int status = 0;
	struct spdk_ftl_dev *dev = SPDK_CONTAINEROF(nv_cache, struct spdk_ftl_dev, nv_cache);

	nv_cache->chunk_current = NULL;
	TAILQ_INIT(&nv_cache->chunk_free_list);
	TAILQ_INIT(&nv_cache->chunk_full_list);
	nv_cache->chunk_full_count = nv_cache->chunk_free_count = 0;

	assert(nv_cache->chunk_open_count == 0);
	offset = nvc_data_offset(nv_cache);
	chunk = nv_cache->chunks;
	if (!chunk) {
		FTL_ERRLOG(dev, "No NV cache metadata\n");
		return -1;
	}

	for (i = 0; i < nv_cache->chunk_count; i++, chunk++) {
		chunk->nv_cache = nv_cache;
		nvc_validate_md(nv_cache, chunk->md);

		if (offset != chunk->offset) {
			status = -EINVAL;
			goto error;
		}

		if (chunk->md->blocks_written == nv_cache->chunk_blocks) {
			/* Chunk full, move it on full list */
			TAILQ_INSERT_TAIL(&nv_cache->chunk_full_list, chunk, entry);
			nv_cache->chunk_full_count++;
		} else if (0 == chunk->md->blocks_written) {
			/* Chunk empty, move it on empty list */
			TAILQ_INSERT_TAIL(&nv_cache->chunk_free_list, chunk, entry);
			nv_cache->chunk_free_count++;
		} else {
			status = -EINVAL;
			goto error;
		}

		offset += nv_cache->chunk_blocks;
	}

	chunks_number = nv_cache->chunk_free_count + nv_cache->chunk_full_count;
	assert(nv_cache->chunk_current == NULL);

	if (chunks_number != nv_cache->chunk_count) {
		FTL_ERRLOG(dev, "Inconsistent NV cache metadata\n");
		status = -EINVAL;
		goto error;
	}

	FTL_NOTICELOG(dev, "FTL NV Cache: full chunks = %lu, empty chunks = %lu\n",
		      nv_cache->chunk_full_count, nv_cache->chunk_free_count);

	if (0 == status) {
		FTL_NOTICELOG(dev, "FTL NV Cache: state loaded successfully\n");
	} else {
		FTL_ERRLOG(dev, "FTL NV Cache: loading state ERROR\n");
	}

error:
	return status;
}

typedef void (*ftl_chunk_ops_cb)(struct ftl_nv_cache_chunk *chunk, void *cntx, bool status);
typedef void (*ftl_chunk_ops_cb)(struct ftl_nv_cache_chunk *chunk, void *cntx, bool status);


static void
static void
+2 −0
Original line number Original line Diff line number Diff line
@@ -180,6 +180,8 @@ void ftl_nv_cache_set_addr(struct spdk_ftl_dev *dev, uint64_t lba, ftl_addr addr


int ftl_nv_cache_save_state(struct ftl_nv_cache *nv_cache);
int ftl_nv_cache_save_state(struct ftl_nv_cache *nv_cache);


int ftl_nv_cache_load_state(struct ftl_nv_cache *nv_cache);

void ftl_nv_cache_halt(struct ftl_nv_cache *nv_cache);
void ftl_nv_cache_halt(struct ftl_nv_cache *nv_cache);


int ftl_nv_cache_chunks_busy(struct ftl_nv_cache *nv_cache);
int ftl_nv_cache_chunks_busy(struct ftl_nv_cache *nv_cache);
+227 −1
Original line number Original line Diff line number Diff line
@@ -127,6 +127,65 @@ persist(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt,
	ftl_md_persist(md);
	ftl_md_persist(md);
}
}


static int
ftl_md_restore_region(struct spdk_ftl_dev *dev, int region_type)
{
	int status = 0;
	switch (region_type) {
	case FTL_LAYOUT_REGION_TYPE_NVC_MD:
		status = ftl_nv_cache_load_state(&dev->nv_cache);
		break;
	case FTL_LAYOUT_REGION_TYPE_VALID_MAP:
		ftl_valid_map_load_state(dev);
		break;
	case FTL_LAYOUT_REGION_TYPE_BAND_MD:
		ftl_bands_load_state(dev);
		break;
	default:
		break;
	}
	return status;
}

static void
restore_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
{
	struct ftl_mngt_process *mngt = md->owner.cb_ctx;
	const struct ftl_layout_region *region = ftl_md_get_region(md);

	if (status) {
		/* Restore error, end step */
		ftl_mngt_fail_step(mngt);
		return;
	}

	assert(region);
	status = ftl_md_restore_region(dev, region->type);

	if (status) {
		ftl_mngt_fail_step(mngt);
	} else {
		ftl_mngt_next_step(mngt);
	}
}

static void
restore(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt, enum ftl_layout_region_type type)
{
	struct ftl_layout *layout = &dev->layout;
	assert(type < FTL_LAYOUT_REGION_TYPE_MAX);
	struct ftl_md *md = layout->md[type];

	if (!md) {
		ftl_mngt_fail_step(mngt);
		return;
	}

	md->owner.cb_ctx = mngt;
	md->cb = restore_cb;
	ftl_md_restore(md);
}

void
void
ftl_mngt_persist_nv_cache_metadata(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
ftl_mngt_persist_nv_cache_metadata(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
{
@@ -322,6 +381,84 @@ ftl_mngt_set_shm_clean(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
	ftl_mngt_next_step(mngt);
	ftl_mngt_next_step(mngt);
}
}


void
ftl_mngt_load_sb(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	FTL_NOTICELOG(dev, "SHM: clean %"PRIu64", shm_clean %d\n", dev->sb->clean, dev->sb_shm->shm_clean);

	if (!ftl_fast_startup(dev)) {
		restore(dev, mngt, FTL_LAYOUT_REGION_TYPE_SB);
		return;
	}

	FTL_DEBUGLOG(dev, "SHM: found SB\n");
	if (ftl_md_restore_region(dev, FTL_LAYOUT_REGION_TYPE_SB)) {
		ftl_mngt_fail_step(mngt);
		return;
	}
	ftl_mngt_next_step(mngt);
}

void
ftl_mngt_validate_sb(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	struct ftl_superblock *sb = dev->sb;

	if (!ftl_superblock_check_magic(sb)) {
		FTL_ERRLOG(dev, "Invalid FTL superblock magic\n");
		ftl_mngt_fail_step(mngt);
		return;
	}

	if (sb->header.crc != get_sb_crc(sb)) {
		FTL_ERRLOG(dev, "Invalid FTL superblock CRC\n");
		ftl_mngt_fail_step(mngt);
		return;
	}

	if (spdk_uuid_compare(&sb->uuid, &dev->conf.uuid) != 0) {
		FTL_ERRLOG(dev, "Invalid FTL superblock UUID\n");
		ftl_mngt_fail_step(mngt);
		return;
	}

	if (sb->lba_cnt == 0) {
		FTL_ERRLOG(dev, "Invalid FTL superblock lba_cnt\n");
		ftl_mngt_fail_step(mngt);
		return;
	}
	dev->num_lbas = sb->lba_cnt;

	/* The sb has just been read. Validate and update the conf */
	if (sb->overprovisioning == 0 || sb->overprovisioning >= 100) {
		FTL_ERRLOG(dev, "Invalid FTL superblock lba_rsvd\n");
		ftl_mngt_fail_step(mngt);
		return;
	}
	dev->conf.overprovisioning = sb->overprovisioning;

	ftl_mngt_next_step(mngt);
}

/*
 * Loads and verifies superblock contents - utilized during the load of an FTL
 * instance (both from a clean and dirty shutdown).
 */
static const struct ftl_mngt_process_desc desc_restore_sb = {
	.name = "SB restore",
	.steps = {
		{
			.name = "Load super block",
			.action = ftl_mngt_load_sb
		},
		{
			.name = "Validate super block",
			.action = ftl_mngt_validate_sb
		},
		{}
	}
};

/*
/*
 * Initializes the superblock fields during first startup of FTL
 * Initializes the superblock fields during first startup of FTL
 */
 */
@@ -442,7 +579,7 @@ shm_retry:
	if (dev->conf.mode & SPDK_FTL_MODE_CREATE) {
	if (dev->conf.mode & SPDK_FTL_MODE_CREATE) {
		ftl_mngt_call_process(mngt, &desc_init_sb);
		ftl_mngt_call_process(mngt, &desc_init_sb);
	} else {
	} else {
		ftl_mngt_fail_step(mngt);
		ftl_mngt_call_process(mngt, &desc_restore_sb);
	}
	}
}
}


@@ -468,3 +605,92 @@ ftl_mngt_superblock_deinit(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mn


	ftl_mngt_next_step(mngt);
	ftl_mngt_next_step(mngt);
}
}

static void
ftl_mngt_restore_nv_cache_metadata(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	if (ftl_fast_startup(dev)) {
		FTL_DEBUGLOG(dev, "SHM: found nv cache md\n");
		if (ftl_md_restore_region(dev, FTL_LAYOUT_REGION_TYPE_NVC_MD)) {
			ftl_mngt_fail_step(mngt);
			return;
		}
		ftl_mngt_next_step(mngt);
		return;
	}
	restore(dev, mngt, FTL_LAYOUT_REGION_TYPE_NVC_MD);
}

static void
ftl_mngt_restore_vld_map_metadata(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	if (ftl_fast_startup(dev)) {
		FTL_DEBUGLOG(dev, "SHM: found vldmap\n");
		if (ftl_md_restore_region(dev, FTL_LAYOUT_REGION_TYPE_VALID_MAP)) {
			ftl_mngt_fail_step(mngt);
			return;
		}
		ftl_mngt_next_step(mngt);
		return;
	}
	restore(dev, mngt, FTL_LAYOUT_REGION_TYPE_VALID_MAP);
}

static void
ftl_mngt_restore_band_info_metadata(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	if (ftl_fast_startup(dev)) {
		FTL_DEBUGLOG(dev, "SHM: found band md\n");
		if (ftl_md_restore_region(dev, FTL_LAYOUT_REGION_TYPE_BAND_MD)) {
			ftl_mngt_fail_step(mngt);
			return;
		}
		ftl_mngt_next_step(mngt);
		return;
	}
	restore(dev, mngt, FTL_LAYOUT_REGION_TYPE_BAND_MD);
}



#ifdef SPDK_FTL_VSS_EMU
static void
ftl_mngt_restore_vss_metadata(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	restore(dev, mngt, FTL_LAYOUT_REGION_TYPE_VSS);
}
#endif

/*
 * Loads metadata after a clean shutdown.
 */
static const struct ftl_mngt_process_desc desc_restore = {
	.name = "Restore metadata",
	.steps = {
#ifdef SPDK_FTL_VSS_EMU
		{
			.name = "Restore VSS metadata",
			.action = ftl_mngt_restore_vss_metadata,
		},
#endif
		{
			.name = "Restore NV cache metadata",
			.action = ftl_mngt_restore_nv_cache_metadata,
		},
		{
			.name = "Restore valid map metadata",
			.action = ftl_mngt_restore_vld_map_metadata,
		},
		{
			.name = "Restore band info metadata",
			.action = ftl_mngt_restore_band_info_metadata,
		},
		{}
	}
};

void
ftl_mngt_restore_md(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	ftl_mngt_call_process(mngt, &desc_restore);
}
Loading