From fb15434659f27cf360d61519f91d571104789bb9 Mon Sep 17 00:00:00 2001 From: Konrad Sztyber Date: Fri, 5 Jul 2019 11:43:15 +0200 Subject: [PATCH] lib/ftl: store non-volatile cache's current write address Remember current write position within the cache when shutting down. It allows for faster recovery after clean shutdown (no need to scan the device) as well as grants a quick way to distinguish between clean and dirty shutdowns. Change-Id: I79c22caa0b1ca4373951ac43f747b085d331cdd0 Signed-off-by: Konrad Sztyber Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/460796 Tested-by: SPDK CI Jenkins Reviewed-by: Jim Harris Reviewed-by: Ben Walker Reviewed-by: Wojciech Malikowski Reviewed-by: Mateusz Kozlowski --- lib/ftl/ftl_core.c | 7 +++-- lib/ftl/ftl_core.h | 6 ++-- lib/ftl/ftl_init.c | 43 ++++++++++++++++++++++---- lib/ftl/ftl_restore.c | 72 ++++++++++++++++++++++++++++++++----------- 4 files changed, 99 insertions(+), 29 deletions(-) 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; } }