diff --git a/lib/ftl/ftl_band.c b/lib/ftl/ftl_band.c index 961930773..643d6d841 100644 --- a/lib/ftl/ftl_band.c +++ b/lib/ftl/ftl_band.c @@ -12,6 +12,7 @@ #include "ftl_band.h" #include "ftl_io.h" #include "ftl_core.h" +#include "ftl_debug.h" #include "ftl_internal.h" #include "utils/ftl_md.h" #include "utils/ftl_defs.h" @@ -90,10 +91,12 @@ _ftl_band_set_preparing(struct ftl_band *band) } static void -_ftl_band_set_closed(struct ftl_band *band) +_ftl_band_set_closed_cb(struct ftl_band *band, bool valid) { struct spdk_ftl_dev *dev = band->dev; + assert(valid == true); + /* Set the state as free_md() checks for that */ band->md->state = FTL_BAND_STATE_CLOSED; if (band->owner.state_change_fn) { @@ -107,6 +110,13 @@ _ftl_band_set_closed(struct ftl_band *band) TAILQ_INSERT_TAIL(&dev->shut_bands, band, queue_entry); } +static void +_ftl_band_set_closed(struct ftl_band *band) +{ + /* Verify that band's metadata is consistent with l2p */ + ftl_band_validate_md(band, _ftl_band_set_closed_cb); +} + ftl_addr ftl_band_tail_md_addr(struct ftl_band *band) { diff --git a/lib/ftl/ftl_band.h b/lib/ftl/ftl_band.h index bc66c8991..f49c450b5 100644 --- a/lib/ftl/ftl_band.h +++ b/lib/ftl/ftl_band.h @@ -43,6 +43,7 @@ enum ftl_band_state { typedef void (*ftl_band_state_change_fn)(struct ftl_band *band); typedef void (*ftl_band_ops_cb)(struct ftl_band *band, void *ctx, bool status); typedef void (*ftl_band_md_cb)(struct ftl_band *band, void *ctx, enum ftl_md_status status); +typedef void (*ftl_band_validate_md_cb)(struct ftl_band *band, bool valid); struct ftl_band_md { /* Band iterator for writing */ @@ -132,6 +133,9 @@ struct ftl_band { /* For writing metadata */ struct ftl_md_io_entry_ctx md_persist_entry_ctx; + + /* Callback function for validate md */ + ftl_band_validate_md_cb validate_cb; }; diff --git a/lib/ftl/ftl_debug.c b/lib/ftl/ftl_debug.c index c129090d9..426e24851 100644 --- a/lib/ftl/ftl_debug.c +++ b/lib/ftl/ftl_debug.c @@ -21,6 +21,138 @@ static const char *ftl_band_state_str[] = { "max" }; +struct ftl_band_validate_ctx { + struct ftl_band *band; + ftl_band_validate_md_cb cb; + int remaining; + uint64_t pin_cnt; + uint64_t current_offset; + struct ftl_l2p_pin_ctx l2p_pin_ctx[]; +}; + +static void ftl_band_validate_md_l2p_pin_cb(struct spdk_ftl_dev *dev, int status, + struct ftl_l2p_pin_ctx *pin_ctx); + +#define FTL_MD_VALIDATE_LBA_PER_ITERATION 128 + +static void +ftl_band_validate_md_pin(struct ftl_band_validate_ctx *ctx) +{ + struct ftl_band *band = ctx->band; + struct spdk_ftl_dev *dev = band->dev; + struct ftl_p2l_map *p2l_map = &band->p2l_map; + size_t i, size; + struct ftl_l2p_pin_ctx tmp_pin_ctx = { + .cb_ctx = ctx + }; + + /* Since the first L2P page may already be pinned, the ftl_band_validate_md_l2p_pin_cb could be prematurely + * triggered. Initializing to 1 and then triggering the callback again manually prevents the issue. + */ + ctx->remaining = 1; + size = spdk_min(FTL_MD_VALIDATE_LBA_PER_ITERATION, + ftl_get_num_blocks_in_band(dev) - ctx->current_offset); + + for (i = ctx->current_offset; i < ctx->current_offset + size; ++i) { + if (!ftl_bitmap_get(p2l_map->valid, i)) { + ctx->l2p_pin_ctx[i].lba = FTL_LBA_INVALID; + continue; + } + + assert(p2l_map->band_map[i] != FTL_LBA_INVALID); + ctx->remaining++; + ctx->pin_cnt++; + ftl_l2p_pin(dev, p2l_map->band_map[i], 1, ftl_band_validate_md_l2p_pin_cb, ctx, + &ctx->l2p_pin_ctx[i]); + } + + ftl_band_validate_md_l2p_pin_cb(dev, 0, &tmp_pin_ctx); +} + +static void +_ftl_band_validate_md(void *_ctx) +{ + struct ftl_band_validate_ctx *ctx = _ctx; + struct ftl_band *band = ctx->band; + struct spdk_ftl_dev *dev = band->dev; + ftl_addr addr_l2p; + size_t i, size; + bool valid = true; + uint64_t lba; + + size = spdk_min(FTL_MD_VALIDATE_LBA_PER_ITERATION, + ftl_get_num_blocks_in_band(dev) - ctx->current_offset); + + for (i = ctx->current_offset; i < ctx->current_offset + size; ++i) { + lba = ctx->l2p_pin_ctx[i].lba; + if (lba == FTL_LBA_INVALID) { + continue; + } + + if (ftl_bitmap_get(band->p2l_map.valid, i)) { + addr_l2p = ftl_l2p_get(dev, lba); + + if (addr_l2p != FTL_ADDR_INVALID && !ftl_addr_in_nvc(dev, addr_l2p) && + addr_l2p != ftl_band_addr_from_block_offset(band, i)) { + valid = false; + } + } + + ctx->pin_cnt--; + ftl_l2p_unpin(dev, lba, 1); + } + assert(ctx->pin_cnt == 0); + + ctx->current_offset += size; + + if (ctx->current_offset == ftl_get_num_blocks_in_band(dev)) { + ctx->cb(band, valid); + free(ctx); + return; + } + + ftl_band_validate_md_pin(ctx); +} + +static void +ftl_band_validate_md_l2p_pin_cb(struct spdk_ftl_dev *dev, int status, + struct ftl_l2p_pin_ctx *pin_ctx) +{ + struct ftl_band_validate_ctx *ctx = pin_ctx->cb_ctx; + + assert(status == 0); + + if (--ctx->remaining == 0) { + spdk_thread_send_msg(dev->core_thread, _ftl_band_validate_md, ctx); + } +} + +void +ftl_band_validate_md(struct ftl_band *band, ftl_band_validate_md_cb cb) +{ + struct ftl_band_validate_ctx *ctx; + size_t size; + + assert(cb); + + size = ftl_get_num_blocks_in_band(band->dev); + + ctx = malloc(sizeof(*ctx) + size * sizeof(*ctx->l2p_pin_ctx)); + + if (!ctx) { + FTL_ERRLOG(band->dev, "Failed to allocate memory for band validate context"); + cb(band, false); + return; + } + + ctx->band = band; + ctx->cb = cb; + ctx->pin_cnt = 0; + ctx->current_offset = 0; + + ftl_band_validate_md_pin(ctx); +} + void ftl_dev_dump_bands(struct spdk_ftl_dev *dev) { diff --git a/lib/ftl/ftl_debug.h b/lib/ftl/ftl_debug.h index 32083c221..f3a3396d4 100644 --- a/lib/ftl/ftl_debug.h +++ b/lib/ftl/ftl_debug.h @@ -10,11 +10,27 @@ #include "ftl_band.h" #include "ftl_core.h" -typedef void (*ftl_band_validate_md_cb)(struct ftl_band *band, bool valid); - #if defined(DEBUG) +void ftl_band_validate_md(struct ftl_band *band, ftl_band_validate_md_cb cb); void ftl_dev_dump_bands(struct spdk_ftl_dev *dev); #else + +static void +_validate_cb(void *ctx) +{ + struct ftl_band *band = ctx; + + band->validate_cb(band, true); +} + +static inline void +ftl_band_validate_md(struct ftl_band *band, ftl_band_validate_md_cb cb) +{ + /* For release builds this is a NOP operation, but should still be asynchronous to keep the behavior consistent */ + band->validate_cb = cb; + spdk_thread_send_msg(band->dev->core_thread, _validate_cb, band); +} + static inline void ftl_dev_dump_bands(struct spdk_ftl_dev *dev) { 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 ddc8ab667..de6e51666 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 @@ -27,6 +27,9 @@ struct base_bdev_geometry g_geo = { static struct spdk_ftl_dev *g_dev; static struct ftl_band *g_band; +#if defined(DEBUG) +DEFINE_STUB_V(ftl_band_validate_md, (struct ftl_band *band, ftl_band_validate_md_cb cb)); +#endif DEFINE_STUB_V(spdk_bdev_free_io, (struct spdk_bdev_io *bdev_io)); DEFINE_STUB(spdk_bdev_get_block_size, uint32_t, (const struct spdk_bdev *bdev), 512); DEFINE_STUB(spdk_bdev_get_name, const char *, (const struct spdk_bdev *bdev), "test");