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:
parent
57cfab6808
commit
8fad5718e1
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user