Commit 2d613454 authored by Mateusz Kozlowski's avatar Mateusz Kozlowski Committed by Tomasz Zawadzki
Browse files

lib/ftl: Handle trim requests without VSS



The trim flow and its metadata are upgraded. To assure trim atomicity
a new metadata layout region is added - trim log. The flow of the trim
is as follows:
1. Log the trim requests
2. Persist the trim metadata
3. Clear the trim log

If power loss occurs during the trim, the trim log is used to restore
the entire trim request.

Change-Id: I132c6bb2b04fbe68a94a38c495344ffd4b8f6d47
Signed-off-by: default avatarMariusz Barczak <Mariusz.Barczak@solidigmtechnology.com>
Signed-off-by: default avatarLukasz Lasek <lukasz.lasek@solidigmtechnology.com>
Signed-off-by: default avatarMateusz Kozlowski <mateusz.kozlowski@solidigm.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/19609


Community-CI: Mellanox Build Bot
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
Reviewed-by: default avatarBen Walker <ben@nvidia.com>
Reviewed-by: default avatarArtur Paszkiewicz <artur.paszkiewicz@intel.com>
parent 106ad379
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ C_SRCS += mngt/ftl_mngt_recovery.c mngt/ftl_mngt_upgrade.c
C_SRCS += utils/ftl_conf.c utils/ftl_md.c utils/ftl_mempool.c utils/ftl_bitmap.c utils/ftl_property.c
C_SRCS += utils/ftl_layout_tracker_bdev.c
C_SRCS += upgrade/ftl_layout_upgrade.c upgrade/ftl_sb_upgrade.c upgrade/ftl_p2l_upgrade.c
C_SRCS += upgrade/ftl_band_upgrade.c upgrade/ftl_chunk_upgrade.c
C_SRCS += upgrade/ftl_band_upgrade.c upgrade/ftl_chunk_upgrade.c upgrade/ftl_trim_upgrade.c
C_SRCS += upgrade/ftl_sb_v3.c upgrade/ftl_sb_v5.c
C_SRCS += nvc/ftl_nvc_dev.c nvc/ftl_nvc_bdev_vss.c
C_SRCS += base/ftl_base_dev.c base/ftl_base_bdev.c
+79 −20
Original line number Diff line number Diff line
@@ -551,14 +551,21 @@ ftl_process_io_channel(struct spdk_ftl_dev *dev, struct ftl_io_channel *ioch)
}

static void
ftl_process_trim_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
ftl_trim_log_clear(struct spdk_ftl_dev *dev)
{
	struct ftl_io *io = md->owner.cb_ctx;
	struct ftl_trim_log *log = ftl_md_get_buffer(dev->layout.md[FTL_LAYOUT_REGION_TYPE_TRIM_LOG]);

	memset(&log->hdr, 0, sizeof(log->hdr));
}

static void
ftl_trim_finish(struct ftl_io *io, int status)
{
	io->dev->trim_qd--;

	if (spdk_unlikely(status)) {
#ifdef SPDK_FTL_RETRY_ON_ERROR
		ftl_io_clear(io);
		TAILQ_INSERT_HEAD(&io->dev->trim_sq, io, queue_entry);
		return;
#else
@@ -569,41 +576,97 @@ ftl_process_trim_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
	ftl_io_complete(io);
}

static void
ftl_trim_log_close_cb(int status, void *cb_arg)
{
	struct ftl_io *io = cb_arg;

	ftl_trim_finish(io, status);
}

static void
ftl_trim_log_persist(struct ftl_io *io, ftl_md_io_entry_cb cb)
{
	struct spdk_ftl_dev *dev = io->dev;
	struct ftl_md *trim_log = dev->layout.md[FTL_LAYOUT_REGION_TYPE_TRIM_LOG];

	ftl_md_persist_entries(trim_log, 0, 1, ftl_md_get_buffer(trim_log), NULL,
			       cb, io, &dev->trim_md_io_entry_ctx);
}

static void
ftl_trim_md_cb(int status, void *cb_arg)
{
	struct ftl_io *io = cb_arg;
	struct spdk_ftl_dev *dev = io->dev;

	if (status) {
		io->status = status;
	}
	ftl_trim_log_clear(dev);
	ftl_trim_log_persist(io, ftl_trim_log_close_cb);
}

static void
ftl_trim_log_open_cb(int status, void *cb_arg)
{
	struct ftl_io *io = cb_arg;
	struct spdk_ftl_dev *dev = io->dev;
	struct ftl_md *trim_md = dev->layout.md[FTL_LAYOUT_REGION_TYPE_TRIM_MD];
	uint64_t first, entires;
	const uint64_t entires_in_block = FTL_BLOCK_SIZE / sizeof(uint64_t);
	char *buffer;

	if (status) {
		ftl_trim_finish(io, status);
		return;
	}

	/* Map trim space into L2P pages */
	first = io->lba / dev->layout.l2p.lbas_in_page;
	entires = io->num_blocks / dev->layout.l2p.lbas_in_page;
	/* Map pages into trim metadata location */
	first = first / entires_in_block;
	entires = spdk_divide_round_up(entires, entires_in_block);

	/* Get trim metadata buffer */
	buffer = (char *)ftl_md_get_buffer(trim_md) + (FTL_BLOCK_SIZE * first);

	/* Persist the trim metadata snippet which corresponds to the trim IO */
	ftl_md_persist_entries(trim_md, first, entires, buffer, NULL,
			       ftl_trim_md_cb, io, &dev->trim_md_io_entry_ctx);
}

void
ftl_set_trim_map(struct spdk_ftl_dev *dev, uint64_t lba, uint64_t num_blocks, uint64_t seq_id)
{
	uint64_t first_page, num_pages;
	uint64_t first_md_block, num_md_blocks, num_pages_in_block;
	uint32_t lbas_in_page = dev->layout.l2p.lbas_in_page;
	uint64_t lbas_in_page = dev->layout.l2p.lbas_in_page;
	struct ftl_md *md = dev->layout.md[FTL_LAYOUT_REGION_TYPE_TRIM_MD];
	uint64_t *page = ftl_md_get_buffer(md);
	union ftl_md_vss *page_vss;
	struct ftl_trim_log *log;
	size_t i;

	first_page = lba / lbas_in_page;
	num_pages = num_blocks / lbas_in_page;

	/* Fill trim metadata */
	for (i = first_page; i < first_page + num_pages; ++i) {
		ftl_bitmap_set(dev->trim_map, i);
		page[i] = seq_id;
	}

	num_pages_in_block = FTL_BLOCK_SIZE / sizeof(*page);
	first_md_block = first_page / num_pages_in_block;
	num_md_blocks = spdk_divide_round_up(num_pages, num_pages_in_block);
	page_vss = ftl_md_get_vss_buffer(md) + first_md_block;
	for (i = first_md_block; i < num_md_blocks; ++i, page_vss++) {
		page_vss->trim.start_lba = lba;
		page_vss->trim.num_blocks = num_blocks;
		page_vss->trim.seq_id = seq_id;
	}
	/* Fill trim log */
	log = ftl_md_get_buffer(dev->layout.md[FTL_LAYOUT_REGION_TYPE_TRIM_LOG]);
	log->hdr.trim.seq_id = seq_id;
	log->hdr.trim.num_blocks = num_blocks;
	log->hdr.trim.start_lba = lba;
}

static bool
ftl_process_trim(struct ftl_io *io)
{
	struct spdk_ftl_dev *dev = io->dev;
	struct ftl_md *md = dev->layout.md[FTL_LAYOUT_REGION_TYPE_TRIM_MD];
	uint64_t seq_id;

	seq_id = ftl_nv_cache_acquire_trim_seq_id(&dev->nv_cache);
@@ -622,11 +685,7 @@ ftl_process_trim(struct ftl_io *io)
	ftl_debug_inject_trim_error();
	dev->sb_shm->trim.in_progress = false;

	md->owner.cb_ctx = io;
	md->cb = ftl_process_trim_cb;

	ftl_md_persist(md);

	ftl_trim_log_persist(io, ftl_trim_log_open_cb);
	return true;
}

+1 −0
Original line number Diff line number Diff line
@@ -173,6 +173,7 @@ struct spdk_ftl_dev {
	struct ftl_md			*trim_map_md;
	size_t				trim_qd;
	bool				trim_in_progress;
	struct ftl_md_io_entry_ctx	trim_md_io_entry_ctx;

	/* Writer for user IOs */
	struct ftl_writer		writer_user;
+9 −0
Original line number Diff line number Diff line
@@ -124,6 +124,15 @@ SPDK_STATIC_ASSERT(sizeof(struct ftl_p2l_ckpt_page_no_vss) == FTL_BLOCK_SIZE,

#define FTL_NUM_P2L_ENTRIES_NO_VSS (SPDK_COUNTOF_MEMBER(struct ftl_p2l_ckpt_page_no_vss, map))

#define FTL_TRIM_LOG_VERSION_0		0
#define FTL_TRIM_LOG_VERSION_1		1
#define FTL_TRIM_LOG_VERSION_CURRENT	FTL_TRIM_LOG_VERSION_1

struct ftl_trim_log {
	union ftl_md_vss hdr;
	char reserved[FTL_BLOCK_SIZE - sizeof(union ftl_md_vss)];
};
SPDK_STATIC_ASSERT(sizeof(struct ftl_trim_log) == FTL_BLOCK_SIZE, "Invalid trim log page size");

struct ftl_p2l_ckpt;
struct ftl_band;
+39 −10
Original line number Diff line number Diff line
@@ -99,6 +99,8 @@ ftl_md_region_name(enum ftl_layout_region_type reg_type)
		[FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP_NEXT] = "p2l3",
		[FTL_LAYOUT_REGION_TYPE_TRIM_MD] = "trim_md",
		[FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR] = "trim_md_mirror",
		[FTL_LAYOUT_REGION_TYPE_TRIM_LOG] = "trim_log",
		[FTL_LAYOUT_REGION_TYPE_TRIM_LOG_MIRROR] = "trim_log_mirror"
	};
	const char *reg_name = md_region_name[reg_type];

@@ -120,6 +122,12 @@ dump_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
		      blocks2mib(region->current.blocks));
}

static bool
is_region_disabled(struct ftl_layout_region *region)
{
	return region->current.blocks == 0 && region->current.offset == FTL_ADDR_INVALID;
}

int
ftl_validate_regions(struct spdk_ftl_dev *dev, struct ftl_layout *layout)
{
@@ -129,14 +137,14 @@ ftl_validate_regions(struct spdk_ftl_dev *dev, struct ftl_layout *layout)
	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; i++) {
		struct ftl_layout_region *r1 = ftl_layout_region_get(dev, i);

		if (!r1) {
		if (!r1 || is_region_disabled(r1)) {
			continue;
		}

		for (j = 0; j < FTL_LAYOUT_REGION_TYPE_MAX; j++) {
			struct ftl_layout_region *r2 = ftl_layout_region_get(dev, j);

			if (!r2) {
			if (!r2 || is_region_disabled(r2)) {
				continue;
			}

@@ -369,6 +377,10 @@ layout_setup_legacy_default_nvc(struct spdk_ftl_dev *dev)
	}

	/* Here is the place to add necessary region placeholders for the creation of new regions */
	ftl_layout_upgrade_add_region_placeholder(dev, dev->nvc_layout_tracker,
			FTL_LAYOUT_REGION_TYPE_TRIM_LOG);
	ftl_layout_upgrade_add_region_placeholder(dev, dev->nvc_layout_tracker,
			FTL_LAYOUT_REGION_TYPE_TRIM_LOG_MIRROR);

	return 0;

@@ -414,12 +426,12 @@ static int
layout_setup_default_nvc(struct spdk_ftl_dev *dev)
{
	int region_type;
	uint64_t l2p_blocks;
	uint64_t blocks;
	struct ftl_layout *layout = &dev->layout;

	/* Initialize L2P region */
	l2p_blocks = ftl_md_region_blocks(dev, layout->l2p.addr_size * dev->num_lbas);
	if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_L2P, 0, FTL_BLOCK_SIZE, l2p_blocks)) {
	blocks = ftl_md_region_blocks(dev, layout->l2p.addr_size * dev->num_lbas);
	if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_L2P, 0, FTL_BLOCK_SIZE, blocks)) {
		goto error;
	}

@@ -451,19 +463,36 @@ layout_setup_default_nvc(struct spdk_ftl_dev *dev)
	/*
	 * Initialize trim metadata region
	 */
	l2p_blocks = layout->region[FTL_LAYOUT_REGION_TYPE_L2P].current.blocks;
	if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_MD, 0, sizeof(uint64_t),
				     l2p_blocks)) {
	blocks = layout->region[FTL_LAYOUT_REGION_TYPE_L2P].current.blocks;
	blocks = spdk_divide_round_up(blocks * sizeof(uint64_t), FTL_BLOCK_SIZE);
	if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_MD, 0, FTL_BLOCK_SIZE, blocks)) {
		goto error;
	}

	/* Initialize trim metadata mirror region */
	if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR, 0, sizeof(uint64_t),
				     l2p_blocks)) {
	if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR, 0, FTL_BLOCK_SIZE,
				     blocks)) {
		goto error;
	}
	layout->region[FTL_LAYOUT_REGION_TYPE_TRIM_MD].mirror_type = FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR;

	/*
	 * Initialize trim log region
	 */
	if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_LOG, FTL_TRIM_LOG_VERSION_CURRENT,
				     sizeof(struct ftl_trim_log), 1)) {
		goto error;
	}

	/* Initialize trim log mirror region */
	if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_LOG_MIRROR,
				     FTL_TRIM_LOG_VERSION_CURRENT,
				     sizeof(struct ftl_trim_log), 1)) {
		goto error;
	}
	layout->region[FTL_LAYOUT_REGION_TYPE_TRIM_LOG].mirror_type =
		FTL_LAYOUT_REGION_TYPE_TRIM_LOG_MIRROR;

	/*
	 * Initialize NV Cache metadata
	 */
Loading