From 1243c9306dde9cbdb31d469da892196084e1ccc6 Mon Sep 17 00:00:00 2001 From: Konrad Sztyber Date: Wed, 29 May 2019 15:01:40 +0200 Subject: [PATCH] lib/ftl: prepare non-volatile cache area When creating FTL device using non-volatile cache, zero out the non-volatile cache and store metadata (device's UUID, size of the cache) in the first block. Change-Id: Id8f212aef756e86e8a215582ab7c32a635e18938 Signed-off-by: Konrad Sztyber Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/458094 Tested-by: SPDK CI Jenkins Reviewed-by: Darek Stojaczyk Reviewed-by: Ben Walker Reviewed-by: Shuhei Matsumoto Reviewed-by: Mateusz Kozlowski --- lib/ftl/ftl_core.c | 9 +-- lib/ftl/ftl_core.h | 16 +++++ lib/ftl/ftl_init.c | 157 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 141 insertions(+), 41 deletions(-) diff --git a/lib/ftl/ftl_core.c b/lib/ftl/ftl_core.c index 70432a639..62d1938b6 100644 --- a/lib/ftl/ftl_core.c +++ b/lib/ftl/ftl_core.c @@ -203,7 +203,6 @@ ftl_md_write_cb(struct ftl_io *io, void *arg, int status) struct spdk_ftl_dev *dev = io->dev; struct ftl_nv_cache *nv_cache = &dev->nv_cache; struct ftl_wptr *wptr; - struct spdk_bdev *bdev; wptr = ftl_wptr_from_band(io->band); @@ -215,13 +214,11 @@ ftl_md_write_cb(struct ftl_io *io, void *arg, int status) ftl_band_set_next_state(io->band); if (io->band->state == FTL_BAND_STATE_CLOSED) { if (nv_cache->bdev_desc) { - bdev = spdk_bdev_desc_get_bdev(nv_cache->bdev_desc); - pthread_spin_lock(&nv_cache->lock); nv_cache->num_available += ftl_band_user_lbks(io->band); - if (spdk_unlikely(nv_cache->num_available > spdk_bdev_get_num_blocks(bdev))) { - nv_cache->num_available = spdk_bdev_get_num_blocks(bdev); + if (spdk_unlikely(nv_cache->num_available > nv_cache->num_data_blocks)) { + nv_cache->num_available = nv_cache->num_data_blocks; } pthread_spin_unlock(&nv_cache->lock); } @@ -975,7 +972,7 @@ ftl_reserve_nv_cache(struct ftl_nv_cache *nv_cache, size_t *num_lbks) nv_cache->num_available -= *num_lbks; if (nv_cache->current_addr == spdk_bdev_get_num_blocks(bdev)) { - nv_cache->current_addr = 0; + nv_cache->current_addr = FTL_NV_CACHE_DATA_OFFSET; } out: pthread_spin_unlock(&nv_cache->lock); diff --git a/lib/ftl/ftl_core.h b/lib/ftl/ftl_core.h index 2455a1aac..deb3aba1f 100644 --- a/lib/ftl/ftl_core.h +++ b/lib/ftl/ftl_core.h @@ -110,6 +110,8 @@ struct ftl_nv_cache { uint64_t current_addr; /* Number of available blocks left */ uint64_t num_available; + /* Maximum number of blocks */ + uint64_t num_data_blocks; /* Metadata pool */ struct spdk_mempool *md_pool; /* Cache lock */ @@ -234,6 +236,17 @@ struct spdk_ftl_dev { STAILQ_ENTRY(spdk_ftl_dev) stailq; }; +struct ftl_nv_cache_header { + /* Version of the header */ + uint32_t version; + /* UUID of the FTL device */ + struct spdk_uuid uuid; + /* Size of the non-volatile cache (in blocks) */ + uint64_t size; + /* Checksum of the header, needs to be last element */ + uint32_t checksum; +} __attribute__((packed)); + typedef void (*ftl_restore_fn)(struct spdk_ftl_dev *, struct ftl_restore *, int); void ftl_apply_limits(struct spdk_ftl_dev *dev); @@ -463,4 +476,7 @@ ftl_vld_map_size(const struct spdk_ftl_dev *dev) return (size_t)spdk_divide_round_up(ftl_num_band_lbks(dev), CHAR_BIT); } +#define FTL_NV_CACHE_HEADER_VERSION 1 +#define FTL_NV_CACHE_DATA_OFFSET 1 + #endif /* FTL_CORE_H */ diff --git a/lib/ftl/ftl_init.c b/lib/ftl/ftl_init.c index ad9293532..ef2dae551 100644 --- a/lib/ftl/ftl_init.c +++ b/lib/ftl/ftl_init.c @@ -39,6 +39,10 @@ #include "spdk/likely.h" #include "spdk_internal/log.h" #include "spdk/ftl.h" +#include "spdk/likely.h" +#include "spdk/string.h" +#include "spdk/crc32.h" + #include "ftl_core.h" #include "ftl_anm.h" #include "ftl_io.h" @@ -522,11 +526,13 @@ ftl_dev_init_nv_cache(struct spdk_ftl_dev *dev, struct spdk_bdev_desc *bdev_desc /* The cache needs to be capable of storing at least two full bands. This requirement comes * from the fact that cache works as a protection against power loss, so before the data - * inside the cache can be overwritten, the band it's stored on has to be closed. + * inside the cache can be overwritten, the band it's stored on has to be closed. Plus one + * extra block is needed to store the header. */ - if (spdk_bdev_get_num_blocks(bdev) < ftl_num_band_lbks(dev) * 2) { - SPDK_ERRLOG("Insufficient number of blocks for write buffer cache(%"PRIu64"\n", - spdk_bdev_get_num_blocks(bdev)); + if (spdk_bdev_get_num_blocks(bdev) < ftl_num_band_lbks(dev) * 2 + 1) { + SPDK_ERRLOG("Insufficient number of blocks for write buffer cache (available: %" + PRIu64", required: %"PRIu64")\n", spdk_bdev_get_num_blocks(bdev), + ftl_num_band_lbks(dev) * 2 + 1); return -1; } @@ -551,8 +557,9 @@ ftl_dev_init_nv_cache(struct spdk_ftl_dev *dev, struct spdk_bdev_desc *bdev_desc } nv_cache->bdev_desc = bdev_desc; - nv_cache->current_addr = 0; - nv_cache->num_available = spdk_bdev_get_num_blocks(bdev); + nv_cache->current_addr = FTL_NV_CACHE_DATA_OFFSET; + nv_cache->num_data_blocks = spdk_bdev_get_num_blocks(bdev) - 1; + nv_cache->num_available = nv_cache->num_data_blocks; return 0; } @@ -802,35 +809,6 @@ ftl_init_complete(struct spdk_ftl_dev *dev) dev->init_arg = NULL; } -static int -ftl_setup_initial_state(struct spdk_ftl_dev *dev) -{ - struct spdk_ftl_conf *conf = &dev->conf; - size_t i; - - spdk_uuid_generate(&dev->uuid); - - dev->num_lbas = 0; - for (i = 0; i < ftl_dev_num_bands(dev); ++i) { - dev->num_lbas += ftl_band_num_usable_lbks(&dev->bands[i]); - } - - dev->num_lbas = (dev->num_lbas * (100 - conf->lba_rsvd)) / 100; - - if (ftl_dev_l2p_alloc(dev)) { - SPDK_ERRLOG("Unable to init l2p table\n"); - return -1; - } - - if (ftl_init_bands_state(dev)) { - SPDK_ERRLOG("Unable to finish the initialization\n"); - return -1; - } - - ftl_init_complete(dev); - return 0; -} - struct ftl_init_fail_ctx { spdk_ftl_init_fn cb; void *arg; @@ -866,6 +844,115 @@ ftl_init_fail(struct spdk_ftl_dev *dev) } } +static void +ftl_write_nv_cache_md_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) +{ + struct spdk_ftl_dev *dev = cb_arg; + struct iovec *iovs = NULL; + int iov_cnt = 0; + + spdk_bdev_io_get_iovec(bdev_io, &iovs, &iov_cnt); + spdk_dma_free(iovs[0].iov_base); + spdk_bdev_free_io(bdev_io); + + if (spdk_unlikely(!success)) { + SPDK_ERRLOG("Writing non-volatile cache's metadata header failed\n"); + ftl_init_fail(dev); + return; + } + + ftl_init_complete(dev); +} + +static void +ftl_clear_nv_cache_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) +{ + struct spdk_ftl_dev *dev = cb_arg; + struct spdk_bdev *bdev; + struct ftl_nv_cache *nv_cache = &dev->nv_cache; + struct ftl_io_channel *ioch; + struct ftl_nv_cache_header *hdr; + + spdk_bdev_free_io(bdev_io); + + ioch = spdk_io_channel_get_ctx(dev->ioch); + bdev = spdk_bdev_desc_get_bdev(nv_cache->bdev_desc); + + if (spdk_unlikely(!success)) { + SPDK_ERRLOG("Unable to clear the non-volatile cache bdev\n"); + ftl_init_fail(dev); + return; + } + + assert(sizeof(*hdr) <= spdk_bdev_get_block_size(bdev)); + hdr = spdk_dma_zmalloc(spdk_bdev_get_block_size(bdev), spdk_bdev_get_buf_align(bdev), NULL); + if (spdk_unlikely(!hdr)) { + SPDK_ERRLOG("Memory allocation failure\n"); + ftl_init_fail(dev); + return; + } + + hdr->uuid = dev->uuid; + hdr->size = spdk_bdev_get_num_blocks(bdev); + hdr->version = FTL_NV_CACHE_HEADER_VERSION; + hdr->checksum = spdk_crc32c_update(hdr, offsetof(struct ftl_nv_cache_header, checksum), 0); + + if (spdk_bdev_write_blocks(nv_cache->bdev_desc, ioch->cache_ioch, hdr, 0, 1, + ftl_write_nv_cache_md_cb, dev)) { + SPDK_ERRLOG("Unable to write non-volatile cache metadata header\n"); + spdk_dma_free(hdr); + ftl_init_fail(dev); + } +} + +static int +ftl_setup_initial_state(struct spdk_ftl_dev *dev) +{ + struct spdk_ftl_conf *conf = &dev->conf; + struct ftl_nv_cache *nv_cache = &dev->nv_cache; + struct spdk_bdev *bdev; + struct ftl_io_channel *ioch; + size_t i; + int rc; + + spdk_uuid_generate(&dev->uuid); + + dev->num_lbas = 0; + for (i = 0; i < ftl_dev_num_bands(dev); ++i) { + dev->num_lbas += ftl_band_num_usable_lbks(&dev->bands[i]); + } + + dev->num_lbas = (dev->num_lbas * (100 - conf->lba_rsvd)) / 100; + + if (ftl_dev_l2p_alloc(dev)) { + SPDK_ERRLOG("Unable to init l2p table\n"); + return -1; + } + + if (ftl_init_bands_state(dev)) { + SPDK_ERRLOG("Unable to finish the initialization\n"); + return -1; + } + + if (!nv_cache->bdev_desc) { + ftl_init_complete(dev); + } else { + ioch = spdk_io_channel_get_ctx(dev->ioch); + bdev = spdk_bdev_desc_get_bdev(nv_cache->bdev_desc); + + rc = spdk_bdev_write_zeroes_blocks(nv_cache->bdev_desc, ioch->cache_ioch, + 1, spdk_bdev_get_num_blocks(bdev) - 1, + ftl_clear_nv_cache_cb, dev); + if (spdk_unlikely(rc != 0)) { + SPDK_ERRLOG("Unable to clear the non-volatile cache bdev: %s\n", + spdk_strerror(-rc)); + return -1; + } + } + + return 0; +} + static void ftl_restore_device_cb(struct spdk_ftl_dev *dev, struct ftl_restore *restore, int status) {