Commit d4b9f2c6 authored by Kozlowski Mateusz's avatar Kozlowski Mateusz Committed by Ben Walker
Browse files

FTL: Add metadata self test



Adds additional debugging functionality - ability to check the validity
of all L2P entries and valid map to check for inconsistencies after FTL
startup. Since this is a very time consuming process, it's controlled by
an environment variable and not executed during normal operations.

Signed-off-by: default avatarKozlowski Mateusz <mateusz.kozlowski@intel.com>
Signed-off-by: default avatarArtur Paszkiewicz <artur.paszkiewicz@intel.com>
Change-Id: I4766a1576c058f69fa047f45d2d8be6d0ad0b3cd
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13363


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 cbd7ae6d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ C_SRCS = ftl_core.c ftl_init.c ftl_layout.c ftl_debug.c ftl_io.c ftl_sb.c ftl_l2
C_SRCS += ftl_nv_cache.c ftl_band.c ftl_band_ops.c ftl_writer.c ftl_rq.c ftl_reloc.c ftl_l2p_cache.c
C_SRCS += mngt/ftl_mngt.c mngt/ftl_mngt_bdev.c mngt/ftl_mngt_shutdown.c mngt/ftl_mngt_startup.c
C_SRCS += mngt/ftl_mngt_md.c mngt/ftl_mngt_misc.c mngt/ftl_mngt_ioch.c mngt/ftl_mngt_l2p.c
C_SRCS += mngt/ftl_mngt_band.c
C_SRCS += mngt/ftl_mngt_band.c mngt/ftl_mngt_self_test.c
C_SRCS += utils/ftl_conf.c utils/ftl_md.c utils/ftl_mempool.c utils/ftl_bitmap.c

SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_ftl.map)
+211 −0
Original line number Diff line number Diff line
/*   SPDX-License-Identifier: BSD-3-Clause
 *   Copyright (c) Intel Corporation.
 *   All rights reserved.
 */

#include "ftl_mngt.h"
#include "ftl_mngt_steps.h"
#include "ftl_internal.h"
#include "ftl_core.h"
#include "ftl_band.h"

struct ftl_validate_ctx {
	struct {
		struct ftl_bitmap *bitmap;
		void *buffer;
		uint64_t buffer_size;
		uint64_t bit_count;
		uint64_t base_valid_count;
		uint64_t cache_valid_count;
	} valid_map;

	int status;
};

static void
ftl_mngt_test_prepare(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	struct ftl_validate_ctx *cntx = ftl_mngt_get_process_ctx(mngt);

	cntx->valid_map.bit_count = dev->layout.base.total_blocks +
				    dev->layout.nvc.total_blocks;
	cntx->valid_map.buffer_size = spdk_divide_round_up(cntx->valid_map.bit_count, 8);
	cntx->valid_map.buffer_size = SPDK_ALIGN_CEIL(cntx->valid_map.buffer_size,
				      ftl_bitmap_buffer_alignment);

	cntx->valid_map.buffer = calloc(cntx->valid_map.buffer_size, 1);
	if (!cntx->valid_map.buffer) {
		ftl_mngt_fail_step(mngt);
		return;
	}

	cntx->valid_map.bitmap = ftl_bitmap_create(cntx->valid_map.buffer,
				 cntx->valid_map.buffer_size);
	if (!cntx->valid_map.bitmap) {
		ftl_mngt_fail_step(mngt);
		return;
	}

	ftl_mngt_next_step(mngt);
}

static void
ftl_mngt_test_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	struct ftl_validate_ctx *cntx = ftl_mngt_get_process_ctx(mngt);

	ftl_bitmap_destroy(cntx->valid_map.bitmap);
	cntx->valid_map.bitmap = NULL;

	free(cntx->valid_map.buffer);
	cntx->valid_map.buffer = NULL;

	ftl_mngt_next_step(mngt);
}

static void
test_valid_map_pin_cb(struct spdk_ftl_dev *dev, int status,
		      struct ftl_l2p_pin_ctx *pin_ctx)
{
	struct ftl_mngt_process *mngt = pin_ctx->cb_ctx;
	struct ftl_validate_ctx *ctx = ftl_mngt_get_process_ctx(mngt);
	uint64_t lba, end;

	if (status) {
		FTL_ERRLOG(dev, "L2P pin ERROR when testing valid map\n");
		ftl_mngt_fail_step(mngt);
		return;
	}

	lba = pin_ctx->lba;
	end = pin_ctx->lba + pin_ctx->count;

	for (; lba < end; ++lba) {
		ftl_addr addr = ftl_l2p_get(dev, lba);
		bool valid;

		if (FTL_ADDR_INVALID == addr) {
			continue;
		}

		if (ftl_bitmap_get(ctx->valid_map.bitmap, addr)) {
			status = -EINVAL;
			FTL_ERRLOG(dev, "L2P mapping ERROR, double reference, "
				   "address 0x%.16"PRIX64"\n", addr);
			break;
		} else {
			ftl_bitmap_set(ctx->valid_map.bitmap, addr);
		}

		if (ftl_addr_in_nvc(dev, addr)) {
			ctx->valid_map.cache_valid_count++;
		} else {
			ctx->valid_map.base_valid_count++;
		}

		valid = ftl_bitmap_get(dev->valid_map, addr);
		if (!valid) {
			status = -EINVAL;
			FTL_ERRLOG(dev, "L2P and valid map mismatch"
				   ", LBA 0x%.16"PRIX64
				   ", address 0x%.16"PRIX64" unset\n",
				   lba, addr);
			break;
		}
	}

	ftl_l2p_unpin(dev, pin_ctx->lba, pin_ctx->count);
	pin_ctx->lba += pin_ctx->count;

	if (!status) {
		ftl_mngt_continue_step(mngt);
	} else {
		ftl_mngt_fail_step(mngt);
	}
}

static void
ftl_mngt_test_valid_map(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	struct ftl_l2p_pin_ctx *pin_ctx;
	struct ftl_validate_ctx *ctx = ftl_mngt_get_process_ctx(mngt);
	uint64_t left;

	pin_ctx = ftl_mngt_get_step_ctx(mngt);
	if (!pin_ctx) {
		if (ftl_mngt_alloc_step_ctx(mngt, sizeof(*pin_ctx))) {
			ftl_mngt_fail_step(mngt);
			return;
		}
		pin_ctx = ftl_mngt_get_step_ctx(mngt);
		assert(pin_ctx);

		pin_ctx->lba = 0;
		memset(ctx->valid_map.buffer, 0, ctx->valid_map.buffer_size);
	}

	left = dev->num_lbas - pin_ctx->lba;
	pin_ctx->count = spdk_min(left, 4096);

	if (pin_ctx->count) {
		ftl_l2p_pin(dev, pin_ctx->lba, pin_ctx->count,
			    test_valid_map_pin_cb, mngt, pin_ctx);
	} else {
		if (!ctx->status) {
			uint64_t valid = ctx->valid_map.base_valid_count +
					 ctx->valid_map.cache_valid_count;

			if (ftl_bitmap_count_set(dev->valid_map) != valid) {
				ctx->status = -EINVAL;
			}
		}

		/* All done */
		if (ctx->status) {
			ftl_mngt_fail_step(mngt);
		} else {
			ftl_mngt_next_step(mngt);
		}
	}
}

/*
 * Verifies the contents of L2P versus valid map. Makes sure any physical addresses in the L2P
 * have their corresponding valid bits set and that two different logical addresses don't point
 * to the same physical address.
 *
 * For debugging purposes only, directed via environment variable - whole L2P needs to be loaded in
 * and checked.
 */
static const struct ftl_mngt_process_desc desc_self_test = {
	.name = "[Test] Startup Test",
	.ctx_size = sizeof(struct ftl_validate_ctx),
	.steps = {
		{
			.name = "[TEST] Initialize selftest",

			.action = ftl_mngt_test_prepare,
			.cleanup = ftl_mngt_test_cleanup
		},
		{
			.name = "[TEST] Validate map and L2P consistency",
			.action = ftl_mngt_test_valid_map
		},
		{
			.name = "[TEST] Deinitialize cleanup",
			.action = ftl_mngt_test_cleanup
		},
		{}
	}
};

void
ftl_mngt_self_test(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
	if (getenv("FTL_SELF_TEST")) {
		ftl_mngt_call_process(mngt, &desc_self_test);
	} else {
		FTL_NOTICELOG(dev, "Self test skipped\n");
		ftl_mngt_next_step(mngt);
	}
}
+2 −0
Original line number Diff line number Diff line
@@ -110,6 +110,8 @@ void ftl_mngt_init_vld_map(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mn

void ftl_mngt_deinit_vld_map(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);

void ftl_mngt_self_test(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);

void ftl_mngt_persist_band_info_metadata(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);

void ftl_mngt_persist_nv_cache_metadata(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);