Commit 6448f336 authored by Artur Paszkiewicz's avatar Artur Paszkiewicz Committed by Jim Harris
Browse files

FTL: Add band structure and helper functions

parent 1bbefed6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ CFLAGS += -I.
FTL_SUBDIRS := mngt utils

C_SRCS = ftl_core.c ftl_init.c ftl_layout.c ftl_debug.c ftl_io.c ftl_sb.c ftl_l2p.c ftl_l2p_flat.c
C_SRCS += ftl_nv_cache.c
C_SRCS += ftl_nv_cache.c ftl_band.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 += utils/ftl_conf.c utils/ftl_md.c utils/ftl_mempool.c

lib/ftl/ftl_band.c

0 → 100644
+147 −0
Original line number Diff line number Diff line
/*   SPDX-License-Identifier: BSD-3-Clause
 *   Copyright (c) Intel Corporation.
 *   All rights reserved.
 */

#include "spdk/stdinc.h"
#include "spdk/crc32.h"
#include "spdk/likely.h"
#include "spdk/util.h"
#include "spdk/ftl.h"

#include "ftl_band.h"
#include "ftl_io.h"
#include "ftl_core.h"
#include "ftl_internal.h"
#include "utils/ftl_md.h"
#include "utils/ftl_defs.h"

static uint64_t
ftl_band_tail_md_offset(const struct ftl_band *band)
{
	return ftl_get_num_blocks_in_band(band->dev) -
	       ftl_tail_md_num_blocks(band->dev);
}

int
ftl_band_filled(struct ftl_band *band, size_t offset)
{
	return offset == ftl_band_tail_md_offset(band);
}

ftl_addr
ftl_band_tail_md_addr(struct ftl_band *band)
{
	ftl_addr addr;

	/* Metadata should be aligned to xfer size */
	assert(ftl_band_tail_md_offset(band) % band->dev->xfer_size == 0);

	addr = ftl_band_tail_md_offset(band) + band->start_addr;

	return addr;
}

void
ftl_band_set_type(struct ftl_band *band, enum ftl_band_type type)
{
	switch (type) {
	case FTL_BAND_TYPE_COMPACTION:
	case FTL_BAND_TYPE_GC:
		band->md->type = type;
		break;
	default:
		assert(false);
		break;
	}
}

size_t
ftl_band_user_blocks_left(const struct ftl_band *band, size_t offset)
{
	size_t tail_md_offset = ftl_band_tail_md_offset(band);

	if (spdk_unlikely(offset > tail_md_offset)) {
		return 0;
	}

	return tail_md_offset - offset;
}

struct ftl_band *
ftl_band_from_addr(struct spdk_ftl_dev *dev, ftl_addr addr)
{
	size_t band_id = ftl_addr_get_band(dev, addr);

	assert(band_id < ftl_get_num_bands(dev));
	return &dev->bands[band_id];
}

uint64_t
ftl_band_block_offset_from_addr(struct ftl_band *band, ftl_addr addr)
{
	assert(ftl_addr_get_band(band->dev, addr) == band->id);
	return addr % ftl_get_num_blocks_in_band(band->dev);
}

ftl_addr
ftl_band_next_xfer_addr(struct ftl_band *band, ftl_addr addr, size_t num_blocks)
{
	struct spdk_ftl_dev *dev = band->dev;
	size_t num_xfers;
	uint64_t offset;

	assert(ftl_addr_get_band(dev, addr) == band->id);

	offset = addr - band->start_addr;

	/* In case starting address wasn't aligned to xfer_size, we'll align for consistent calculation
	 * purposes - the unaligned value will be preserved at the end however.
	 */
	num_blocks += (offset % dev->xfer_size);
	offset -= (offset % dev->xfer_size);

	/* Calculate offset based on xfer_size aligned writes */
	num_xfers = (num_blocks / dev->xfer_size);
	offset += num_xfers * dev->xfer_size;
	num_blocks -= num_xfers * dev->xfer_size;

	if (offset > ftl_get_num_blocks_in_band(dev)) {
		return FTL_ADDR_INVALID;
	}

	/* If there's any unalignment (either starting addr value or num_blocks), reintroduce it to the final address
	 */
	if (num_blocks) {
		offset += num_blocks;
		if (offset > ftl_get_num_blocks_in_band(dev)) {
			return FTL_ADDR_INVALID;
		}
	}

	addr = band->start_addr + offset;
	return addr;
}

ftl_addr
ftl_band_addr_from_block_offset(struct ftl_band *band, uint64_t block_off)
{
	ftl_addr addr;

	addr = block_off + band->id * ftl_get_num_blocks_in_band(band->dev);
	return addr;
}

ftl_addr
ftl_band_next_addr(struct ftl_band *band, ftl_addr addr, size_t offset)
{
	uint64_t block_off = ftl_band_block_offset_from_addr(band, addr);

	return ftl_band_addr_from_block_offset(band, block_off + offset);
}

ftl_addr
ftl_band_p2l_map_addr(struct ftl_band *band)
{
	return band->tail_md_addr;
}

lib/ftl/ftl_band.h

0 → 100644
+202 −0
Original line number Diff line number Diff line
/*   SPDX-License-Identifier: BSD-3-Clause
 *   Copyright (c) Intel Corporation.
 *   All rights reserved.
 */

#ifndef FTL_BAND_H
#define FTL_BAND_H

#include "spdk/stdinc.h"
#include "spdk/bit_array.h"
#include "spdk/queue.h"
#include "spdk/crc32.h"

#include "ftl_io.h"
#include "ftl_internal.h"
#include "ftl_core.h"

#define FTL_MAX_OPEN_BANDS 4

#define FTL_BAND_VERSION_0	0
#define FTL_BAND_VERSION_1	1

#define FTL_BAND_VERSION_CURRENT FTL_BAND_VERSION_1

struct spdk_ftl_dev;
struct ftl_band;
struct ftl_rq;
struct ftl_basic_rq;

enum ftl_band_state {
	FTL_BAND_STATE_FREE,
	FTL_BAND_STATE_PREP,
	FTL_BAND_STATE_OPENING,
	FTL_BAND_STATE_OPEN,
	FTL_BAND_STATE_FULL,
	FTL_BAND_STATE_CLOSING,
	FTL_BAND_STATE_CLOSED,
	FTL_BAND_STATE_MAX
};

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

struct ftl_band_md {
	/* Band iterator for writing */
	struct {
		/* Current physical address of the write pointer */
		ftl_addr		addr;

		/* Offset from the band's start of the write pointer */
		uint64_t		offset;
	} iter;

	/* Band's state */
	enum ftl_band_state		state;

	/* Band type set during opening */
	enum ftl_band_type		type;

	/* Number of times band was fully written (ie. number of free -> closed state cycles) */
	uint64_t			wr_cnt;

	/* CRC32 checksum of the associated P2L map when band is in closed state */
	uint32_t			p2l_map_checksum;
} __attribute__((aligned(FTL_BLOCK_SIZE)));

SPDK_STATIC_ASSERT(sizeof(struct ftl_band_md) == FTL_BLOCK_SIZE, "Incorrect metadata size");

struct ftl_band {
	/* Device this band belongs to */
	struct spdk_ftl_dev		*dev;

	struct ftl_band_md		*md;

	/* IO queue depth (outstanding IOs) */
	uint64_t			queue_depth;

	/* Fields for owner of the band - compaction, or gc */
	struct {
		/* Callback context for the owner */
		void *priv;

		/* State change callback */
		ftl_band_state_change_fn state_change_fn;

		/* Callback for the owner */
		union {
			ftl_band_ops_cb ops_fn;
			ftl_band_md_cb md_fn;
		};

		/* Reference counter */
		uint64_t cnt;
	} owner;

	/* P2L map */
	struct ftl_p2l_map		p2l_map;

	/* Band's index */
	uint32_t			id;

	/* Band's NAND id - a group multiple bands may be part of the same physical band on base device
	 * This way the write access pattern will match the actual physical layout more closely, leading
	 * to lower overall write amplification factor
	 */
	uint32_t			phys_id;

	/* Band start addr */
	ftl_addr			start_addr;

	/* End metadata start addr */
	ftl_addr			tail_md_addr;

	/* Metadata request */
	struct ftl_basic_rq		metadata_rq;

	/* Free/shut bands' lists
	 * Open bands are kept and managed directly by the writer (either GC or compaction). Each writer only
	 * needs to keep two bands (one currently written to, and a pre-assigned reserve band to make sure flow
	 * of data is always ongoing as the current one is closing).
	 */
	TAILQ_ENTRY(ftl_band)		queue_entry;

	/* For writing metadata */
	struct ftl_md_io_entry_ctx	md_persist_entry_ctx;
};


uint64_t ftl_band_block_offset_from_addr(struct ftl_band *band, ftl_addr addr);
ftl_addr ftl_band_addr_from_block_offset(struct ftl_band *band, uint64_t block_off);
void ftl_band_set_type(struct ftl_band *band, enum ftl_band_type type);
void ftl_band_set_state(struct ftl_band *band, enum ftl_band_state state);
ftl_addr ftl_band_next_xfer_addr(struct ftl_band *band, ftl_addr addr, size_t num_blocks);
ftl_addr ftl_band_next_addr(struct ftl_band *band, ftl_addr addr, size_t offset);
size_t ftl_band_user_blocks_left(const struct ftl_band *band, size_t offset);
struct ftl_band *ftl_band_from_addr(struct spdk_ftl_dev *dev, ftl_addr addr);
ftl_addr ftl_band_tail_md_addr(struct ftl_band *band);
int ftl_band_filled(struct ftl_band *band, size_t offset);
ftl_addr ftl_band_p2l_map_addr(struct ftl_band *band);

static inline void
ftl_band_set_owner(struct ftl_band *band,
		   ftl_band_state_change_fn fn,
		   void *priv)
{
	assert(NULL == band->owner.priv);
	assert(NULL == band->owner.state_change_fn);

	band->owner.state_change_fn = fn;
	band->owner.priv = priv;
}

static inline void
ftl_band_clear_owner(struct ftl_band *band,
		     ftl_band_state_change_fn fn,
		     void *priv)
{
	assert(priv == band->owner.priv);
	assert(fn == band->owner.state_change_fn);

	band->owner.state_change_fn = NULL;
	band->owner.priv = NULL;
}

static inline int
ftl_band_empty(const struct ftl_band *band)
{
	return band->p2l_map.num_valid == 0;
}

static inline uint64_t
ftl_band_qd(const struct ftl_band *band)
{
	return band->queue_depth;
}

static inline void
ftl_band_iter_init(struct ftl_band *band)
{
	/* Initialize band iterator to begin state */
	band->md->iter.addr = band->start_addr;
	band->md->iter.offset = 0;
}

static inline void
ftl_band_iter_advance(struct ftl_band *band, uint64_t num_blocks)
{
	band->md->iter.offset += num_blocks;
	band->md->iter.addr = ftl_band_next_xfer_addr(band, band->md->iter.addr, num_blocks);
	assert(band->md->iter.addr != FTL_ADDR_INVALID);
}

static inline void
ftl_band_iter_set(struct ftl_band *band, uint64_t num_blocks)
{
	band->md->iter.offset = num_blocks;
	band->md->iter.addr = ftl_band_next_xfer_addr(band, band->md->iter.addr, num_blocks);
	assert(band->md->iter.addr != FTL_ADDR_INVALID);
}

#endif /* FTL_BAND_H */
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include "spdk/crc32.h"

#include "ftl_core.h"
#include "ftl_band.h"
#include "ftl_io.h"
#include "ftl_debug.h"
#include "ftl_internal.h"
+40 −0
Original line number Diff line number Diff line
@@ -23,6 +23,13 @@
#include "ftl_l2p.h"
#include "utils/ftl_log.h"

/*
 * We need to reserve at least 2 buffers for band close / open sequence
 * alone, plus additional (8) buffers for handling relocations.
 *
 */
#define P2L_MEMPOOL_SIZE (2 + 8)

/* When using VSS on nvcache, FTL sometimes doesn't require the contents of metadata.
 * Some devices have bugs when sending a NULL pointer as part of metadata when namespace
 * is formatted with VSS. This buffer is passed to such calls to avoid the bug. */
@@ -71,9 +78,21 @@ struct spdk_ftl_dev {
	   3. base bdev read/write */
	uint64_t			io_activity_total;

	/* Array of bands */
	struct ftl_band			*bands;

	/* Number of operational bands */
	uint64_t			num_bands;

	/* Next write band */
	struct ftl_band			*next_band;

	/* Free band list */
	TAILQ_HEAD(, ftl_band)		free_bands;

	/* Closed bands list */
	TAILQ_HEAD(, ftl_band)		shut_bands;

	/* Number of free bands */
	uint64_t			num_free;

@@ -127,6 +146,12 @@ ftl_get_num_blocks_in_band(const struct spdk_ftl_dev *dev)
	return dev->num_blocks_in_band;
}

static inline uint64_t
ftl_addr_get_band(const struct spdk_ftl_dev *dev, ftl_addr addr)
{
	return addr / ftl_get_num_blocks_in_band(dev);
}

static inline uint32_t
ftl_get_write_unit_size(struct spdk_bdev *bdev)
{
@@ -182,4 +207,19 @@ ftl_addr_from_nvc_offset(const struct spdk_ftl_dev *dev, uint64_t cache_offset)
	return cache_offset + dev->layout.base.total_blocks;
}

static inline size_t
ftl_p2l_map_num_blocks(const struct spdk_ftl_dev *dev)
{
	return spdk_divide_round_up(ftl_get_num_blocks_in_band(dev) * sizeof(uint64_t),
				    FTL_BLOCK_SIZE);
}

static inline size_t
ftl_tail_md_num_blocks(const struct spdk_ftl_dev *dev)
{
	return spdk_divide_round_up(
		       ftl_p2l_map_num_blocks(dev),
		       dev->xfer_size) * dev->xfer_size;
}

#endif /* FTL_CORE_H */
Loading