ftl: validate band metadata in debug mode

Adds a debug function, that scans the whole P2L of band, when
it's getting closed. The P2L is compared against both L2P and
valid map to check for any discrepancies.

Signed-off-by: Artur Paszkiewicz <artur.paszkiewicz@intel.com>
Signed-off-by: Kozlowski Mateusz <mateusz.kozlowski@intel.com>
Change-Id: Ia4d7be65415e6af3752d676de69b6fdcb73effb4
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13352
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
Artur Paszkiewicz 2022-06-01 16:08:59 +02:00 committed by Jim Harris
parent 57cfab6808
commit 8fad5718e1
5 changed files with 168 additions and 3 deletions

View File

@ -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)
{

View File

@ -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;
};

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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");