Commit 8fad5718 authored by Artur Paszkiewicz's avatar Artur Paszkiewicz Committed by Jim Harris
Browse files

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: default avatarArtur Paszkiewicz <artur.paszkiewicz@intel.com>
Signed-off-by: default avatarKozlowski Mateusz <mateusz.kozlowski@intel.com>
Change-Id: Ia4d7be65415e6af3752d676de69b6fdcb73effb4
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13352


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
parent 57cfab68
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -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)
{
+4 −0
Original line number Diff line number Diff line
@@ -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;
};


+132 −0
Original line number Diff line number Diff line
@@ -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)
{
+18 −2
Original line number Diff line number Diff line
@@ -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)
{
+3 −0
Original line number Diff line number Diff line
@@ -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");