diff --git a/lib/ftl/ftl_band.c b/lib/ftl/ftl_band.c index 643d6d841..0dd538db8 100644 --- a/lib/ftl/ftl_band.c +++ b/lib/ftl/ftl_band.c @@ -72,6 +72,8 @@ _ftl_band_set_free(struct ftl_band *band) dev->num_free++; ftl_apply_limits(dev); + + band->md->p2l_map_checksum = 0; } static void @@ -137,8 +139,6 @@ ftl_band_set_state(struct ftl_band *band, enum ftl_band_state state) case FTL_BAND_STATE_FREE: assert(band->md->state == FTL_BAND_STATE_CLOSED); _ftl_band_set_free(band); - - band->md->p2l_map_checksum = 0; break; 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 */ 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); + } + } +} diff --git a/lib/ftl/ftl_band.h b/lib/ftl/ftl_band.h index f49c450b5..8fa06f728 100644 --- a/lib/ftl/ftl_band.h +++ b/lib/ftl/ftl_band.h @@ -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); void ftl_band_init_gc_iter(struct spdk_ftl_dev *dev); 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_close(struct ftl_band *band); void ftl_band_free(struct ftl_band *band); diff --git a/lib/ftl/ftl_nv_cache.c b/lib/ftl/ftl_nv_cache.c index 7b616755b..e0e29fa0f 100644 --- a/lib/ftl/ftl_nv_cache.c +++ b/lib/ftl/ftl_nv_cache.c @@ -1343,6 +1343,74 @@ chunk_alloc_p2l_map(struct ftl_nv_cache_chunk *chunk) 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); static void diff --git a/lib/ftl/ftl_nv_cache.h b/lib/ftl/ftl_nv_cache.h index a0a737b3c..91e6a6a01 100644 --- a/lib/ftl/ftl_nv_cache.h +++ b/lib/ftl/ftl_nv_cache.h @@ -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_load_state(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); diff --git a/lib/ftl/mngt/ftl_mngt_md.c b/lib/ftl/mngt/ftl_mngt_md.c index 62d9269c6..32d4a0a67 100644 --- a/lib/ftl/mngt/ftl_mngt_md.c +++ b/lib/ftl/mngt/ftl_mngt_md.c @@ -127,6 +127,65 @@ persist(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt, 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 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); } +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 */ @@ -442,7 +579,7 @@ shm_retry: if (dev->conf.mode & SPDK_FTL_MODE_CREATE) { ftl_mngt_call_process(mngt, &desc_init_sb); } 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); } + +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); +} diff --git a/lib/ftl/mngt/ftl_mngt_steps.h b/lib/ftl/mngt/ftl_mngt_steps.h index ced9e0351..ae7c008e0 100644 --- a/lib/ftl/mngt/ftl_mngt_steps.h +++ b/lib/ftl/mngt/ftl_mngt_steps.h @@ -100,6 +100,12 @@ void ftl_mngt_set_clean(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) void ftl_mngt_set_shm_clean(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); +void ftl_mngt_load_sb(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); + +void ftl_mngt_validate_sb(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); + +void ftl_mngt_restore_md(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); + void ftl_mngt_init_vld_map(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); void ftl_mngt_deinit_vld_map(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); diff --git a/test/unit/lib/ftl/ftl_band.c/ftl_band_ut.c b/test/unit/lib/ftl/ftl_band.c/ftl_band_ut.c index de6e51666..dd0d63c1d 100644 --- a/test/unit/lib/ftl/ftl_band.c/ftl_band_ut.c +++ b/test/unit/lib/ftl/ftl_band.c/ftl_band_ut.c @@ -122,6 +122,7 @@ DEFINE_STUB_V(ftl_writer_run, (struct ftl_writer *writer)); DEFINE_STUB(ftl_writer_is_halted, bool, (struct ftl_writer *writer), true); DEFINE_STUB(ftl_mempool_claim_df, void *, (struct ftl_mempool *mpool, ftl_df_obj_id df_obj_id), NULL); +DEFINE_STUB(ftl_bitmap_count_set, uint64_t, (struct ftl_bitmap *bitmap), 0); static void adjust_bitmap(struct ftl_bitmap **bitmap, uint64_t *bit)