diff --git a/lib/ftl/ftl_core.c b/lib/ftl/ftl_core.c index 0b663b54e..ce2f54722 100644 --- a/lib/ftl/ftl_core.c +++ b/lib/ftl/ftl_core.c @@ -1064,7 +1064,7 @@ ftl_nv_cache_wrap(void *ctx) struct ftl_nv_cache *nv_cache = ctx; int rc; - rc = ftl_nv_cache_write_header(nv_cache, ftl_nv_cache_wrap_cb, nv_cache); + rc = ftl_nv_cache_write_header(nv_cache, false, ftl_nv_cache_wrap_cb, nv_cache); if (spdk_unlikely(rc != 0)) { SPDK_ERRLOG("Unable to write non-volatile cache metadata header: %s\n", spdk_strerror(-rc)); @@ -1254,8 +1254,8 @@ ftl_write_nv_cache(struct ftl_io *parent) } int -ftl_nv_cache_write_header(struct ftl_nv_cache *nv_cache, spdk_bdev_io_completion_cb cb_fn, - void *cb_arg) +ftl_nv_cache_write_header(struct ftl_nv_cache *nv_cache, bool shutdown, + spdk_bdev_io_completion_cb cb_fn, void *cb_arg) { struct spdk_ftl_dev *dev = SPDK_CONTAINEROF(nv_cache, struct spdk_ftl_dev, nv_cache); struct ftl_nv_cache_header *hdr = nv_cache->dma_buf; @@ -1271,6 +1271,7 @@ ftl_nv_cache_write_header(struct ftl_nv_cache *nv_cache, spdk_bdev_io_completion hdr->size = spdk_bdev_get_num_blocks(bdev); hdr->uuid = dev->uuid; hdr->version = FTL_NV_CACHE_HEADER_VERSION; + hdr->current_addr = shutdown ? nv_cache->current_addr : FTL_LBA_INVALID; hdr->checksum = spdk_crc32c_update(hdr, offsetof(struct ftl_nv_cache_header, checksum), 0); return spdk_bdev_write_blocks(nv_cache->bdev_desc, ioch->cache_ioch, hdr, 0, 1, diff --git a/lib/ftl/ftl_core.h b/lib/ftl/ftl_core.h index 8803d1b8c..1435d0a90 100644 --- a/lib/ftl/ftl_core.h +++ b/lib/ftl/ftl_core.h @@ -262,6 +262,8 @@ struct ftl_nv_cache_header { struct spdk_uuid uuid; /* Size of the non-volatile cache (in blocks) */ uint64_t size; + /* Contains the next address to be written after clean shutdown, invalid LBA otherwise */ + uint64_t current_addr; /* Current phase */ uint8_t phase; /* Checksum of the header, needs to be last element */ @@ -294,8 +296,8 @@ int ftl_retrieve_chunk_info(struct spdk_ftl_dev *dev, struct ftl_ppa ppa, unsigned int num_entries); bool ftl_ppa_is_written(struct ftl_band *band, struct ftl_ppa ppa); int ftl_flush_active_bands(struct spdk_ftl_dev *dev, spdk_ftl_fn cb_fn, void *cb_arg); -int ftl_nv_cache_write_header(struct ftl_nv_cache *nv_cache, spdk_bdev_io_completion_cb cb_fn, - void *cb_arg); +int ftl_nv_cache_write_header(struct ftl_nv_cache *nv_cache, bool shutdown, + spdk_bdev_io_completion_cb cb_fn, void *cb_arg); int ftl_nv_cache_scrub(struct ftl_nv_cache *nv_cache, spdk_bdev_io_completion_cb cb_fn, void *cb_arg); diff --git a/lib/ftl/ftl_init.c b/lib/ftl/ftl_init.c index d3b18a148..fd7595482 100644 --- a/lib/ftl/ftl_init.c +++ b/lib/ftl/ftl_init.c @@ -899,7 +899,7 @@ ftl_clear_nv_cache_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) } nv_cache->phase = 1; - if (ftl_nv_cache_write_header(nv_cache, ftl_write_nv_cache_md_cb, dev)) { + if (ftl_nv_cache_write_header(nv_cache, false, ftl_write_nv_cache_md_cb, dev)) { SPDK_ERRLOG("Unable to write non-volatile cache metadata header\n"); ftl_init_fail(dev); } @@ -1260,15 +1260,46 @@ ftl_dev_free_sync(struct spdk_ftl_dev *dev) } static void -ftl_call_fini_complete_cb(void *_ctx) +ftl_call_fini_complete(struct spdk_ftl_dev *dev, int status) { - struct spdk_ftl_dev *dev = _ctx; struct ftl_init_context ctx = dev->fini_ctx; ftl_dev_free_sync(dev); - if (ctx.cb_fn != NULL) { - ctx.cb_fn(NULL, ctx.cb_arg, 0); + ctx.cb_fn(NULL, ctx.cb_arg, status); + } +} + +static void +ftl_nv_cache_header_fini_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) +{ + int status = 0; + + spdk_bdev_free_io(bdev_io); + if (spdk_unlikely(!success)) { + SPDK_ERRLOG("Failed to write non-volatile cache metadata header\n"); + status = -EIO; + } + + ftl_call_fini_complete((struct spdk_ftl_dev *)cb_arg, status); +} + +static void +ftl_halt_complete_cb(void *ctx) +{ + struct spdk_ftl_dev *dev = ctx; + struct ftl_nv_cache *nv_cache = &dev->nv_cache; + int rc = 0; + + if (!ftl_dev_has_nv_cache(dev)) { + ftl_call_fini_complete(dev, 0); + } else { + rc = ftl_nv_cache_write_header(nv_cache, true, ftl_nv_cache_header_fini_cb, dev); + if (spdk_unlikely(rc != 0)) { + SPDK_ERRLOG("Failed to write non-volatile cache metadata header: %s\n", + spdk_strerror(-rc)); + ftl_call_fini_complete(dev, rc); + } } } @@ -1285,7 +1316,7 @@ ftl_halt_poller(void *ctx) ftl_anm_unregister_device(dev); - spdk_thread_send_msg(dev->fini_ctx.thread, ftl_call_fini_complete_cb, dev); + spdk_thread_send_msg(dev->fini_ctx.thread, ftl_halt_complete_cb, dev); } return 0; diff --git a/lib/ftl/ftl_restore.c b/lib/ftl/ftl_restore.c index 0cdf58d1b..ef8598b05 100644 --- a/lib/ftl/ftl_restore.c +++ b/lib/ftl/ftl_restore.c @@ -106,8 +106,6 @@ struct ftl_nv_cache_restore { size_t num_outstanding; /* Recovery/scan status */ int status; - /* Recover the data from non-volatile cache */ - bool recovery; /* Current phase of the recovery */ unsigned int phase; }; @@ -491,7 +489,7 @@ ftl_nv_cache_scrub_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) } nv_cache->phase = 1; - rc = ftl_nv_cache_write_header(nv_cache, ftl_nv_cache_write_header_cb, restore); + rc = ftl_nv_cache_write_header(nv_cache, false, ftl_nv_cache_write_header_cb, restore); if (spdk_unlikely(rc != 0)) { SPDK_ERRLOG("Unable to write the non-volatile cache metadata header: %s\n", spdk_strerror(-rc)); @@ -538,7 +536,7 @@ ftl_nv_cache_band_flush_cb(void *ctx, int status) * this process, we'll know it needs to be resumed. */ nv_cache->phase = 0; - rc = ftl_nv_cache_write_header(nv_cache, ftl_nv_cache_scrub_header_cb, restore); + rc = ftl_nv_cache_write_header(nv_cache, false, ftl_nv_cache_scrub_header_cb, restore); if (spdk_unlikely(rc != 0)) { SPDK_ERRLOG("Unable to write non-volatile cache metadata header: %s\n", spdk_strerror(-rc)); @@ -770,20 +768,17 @@ ftl_nv_cache_scan_done(struct ftl_nv_cache_restore *restore) } assert(num_blocks == nv_cache->num_data_blocks); #endif - if (!restore->recovery) { - ftl_nv_cache_recovery_done(restore); - } else { - restore->phase = ftl_nv_cache_prev_phase(nv_cache->phase); - /* - * Only the latest two phases need to be recovered. The third one, even if present, - * already has to be stored on the main storage, as it's already started to be - * overwritten (only present here because of reordering of requests' completions). - */ - restore->range[nv_cache->phase].recovery = true; - restore->range[restore->phase].recovery = true; + restore->phase = ftl_nv_cache_prev_phase(nv_cache->phase); - ftl_nv_cache_recover_range(restore); - } + /* + * Only the latest two phases need to be recovered. The third one, even if present, + * already has to be stored on the main storage, as it's already started to be + * overwritten (only present here because of reordering of requests' completions). + */ + restore->range[nv_cache->phase].recovery = true; + restore->range[restore->phase].recovery = true; + + ftl_nv_cache_recover_range(restore); } static int ftl_nv_cache_scan_block(struct ftl_nv_cache_block *block); @@ -863,6 +858,21 @@ ftl_nv_cache_scan_block(struct ftl_nv_cache_block *block) return 0; } +static void +ftl_nv_cache_clean_header_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) +{ + struct ftl_nv_cache_restore *restore = cb_arg; + + spdk_bdev_free_io(bdev_io); + if (spdk_unlikely(!success)) { + SPDK_ERRLOG("Unable to write the non-volatile cache metadata header\n"); + ftl_nv_cache_restore_complete(restore, -EIO); + return; + } + + ftl_nv_cache_restore_done(restore, restore->current_addr); +} + static bool ftl_nv_cache_header_valid(struct spdk_ftl_dev *dev, const struct ftl_nv_cache_header *hdr) { @@ -897,6 +907,14 @@ ftl_nv_cache_header_valid(struct spdk_ftl_dev *dev, const struct ftl_nv_cache_he return false; } + if ((hdr->current_addr >= spdk_bdev_get_num_blocks(bdev) || + hdr->current_addr < FTL_NV_CACHE_DATA_OFFSET) && + (hdr->current_addr != FTL_LBA_INVALID)) { + SPDK_ERRLOG("Unexpected value of non-volatile cache's current address: %"PRIu64"\n", + hdr->current_addr); + return false; + } + return true; } @@ -943,6 +961,25 @@ ftl_nv_cache_read_header_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb goto out; } + /* Valid current_addr means that the shutdown was clean, so we just need to overwrite the + * header to make sure that any power loss occurring before the cache is wrapped won't be + * mistaken for a clean shutdown. + */ + if (hdr->current_addr != FTL_LBA_INVALID) { + restore->nv_cache.current_addr = hdr->current_addr; + + rc = ftl_nv_cache_write_header(nv_cache, false, ftl_nv_cache_clean_header_cb, + &restore->nv_cache); + if (spdk_unlikely(rc != 0)) { + SPDK_ERRLOG("Failed to overwrite the non-volatile cache header: %s\n", + spdk_strerror(-rc)); + ftl_restore_complete(restore, -ENOTRECOVERABLE); + } + + goto out; + } + + /* Otherwise the shutdown was unexpected, so we need to recover the data from the cache */ restore->nv_cache.current_addr = FTL_NV_CACHE_DATA_OFFSET; for (i = 0; i < FTL_NV_CACHE_RESTORE_DEPTH; ++i) { @@ -1218,7 +1255,6 @@ ftl_restore_tail_md_cb(struct ftl_io *io, void *ctx, int status) SPDK_ERRLOG("%s while restoring tail md. Will attempt to pad band %u.\n", spdk_strerror(-status), rband->band->id); STAILQ_INSERT_TAIL(&restore->pad_bands, rband, stailq); - restore->nv_cache.recovery = true; } }