FTL: Add band structure and helper functions

Signed-off-by: Kozlowski Mateusz <mateusz.kozlowski@intel.com>
Signed-off-by: Artur Paszkiewicz <artur.paszkiewicz@intel.com>
Change-Id: I986746a008e716705304906ab4f2bdabce0a84c3
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13324
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
Artur Paszkiewicz 2022-06-21 15:05:28 +02:00 committed by Jim Harris
parent 1bbefed63b
commit 6448f33672
17 changed files with 480 additions and 4 deletions

View File

@ -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

147
lib/ftl/ftl_band.c Normal file
View File

@ -0,0 +1,147 @@
/* 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;
}

202
lib/ftl/ftl_band.h Normal file
View File

@ -0,0 +1,202 @@
/* 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 */

View File

@ -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"

View File

@ -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 */

View File

@ -16,6 +16,7 @@
#include "ftl_core.h"
#include "ftl_io.h"
#include "ftl_band.h"
#include "ftl_debug.h"
#include "ftl_nv_cache.h"
#include "ftl_utils.h"

View File

@ -24,6 +24,30 @@
*/
typedef uint64_t ftl_addr;
enum ftl_md_type {
FTL_MD_TYPE_BAND,
FTL_MD_TYPE_CHUNK
};
enum ftl_band_type {
FTL_BAND_TYPE_GC = 1,
FTL_BAND_TYPE_COMPACTION
};
enum ftl_md_status {
FTL_MD_SUCCESS,
/* Metadata read failure */
FTL_MD_IO_FAILURE,
/* Invalid version */
FTL_MD_INVALID_VER,
/* UUID doesn't match */
FTL_MD_NO_MD,
/* UUID and version matches but CRC doesn't */
FTL_MD_INVALID_CRC,
/* Vld or p2l map size doesn't match */
FTL_MD_INVALID_SIZE
};
/* Number of LBAs that could be stored in a single block */
#define FTL_NUM_LBA_IN_BLOCK (FTL_BLOCK_SIZE / sizeof(uint64_t))

View File

@ -17,6 +17,7 @@
#include "utils/ftl_md.h"
struct spdk_ftl_dev;
struct ftl_band;
struct ftl_io;
typedef void (*ftl_io_fn)(struct ftl_io *, void *, int);
@ -147,6 +148,11 @@ struct ftl_rq_entry {
void *priv;
} owner;
/* If request issued in iterative way, it contains IO information */
struct {
struct ftl_band *band;
} io;
/* For l2p pinning */
struct ftl_l2p_pin_ctx l2p_pin_ctx;
@ -194,6 +200,10 @@ struct ftl_rq {
/* End request callback */
void (*cb)(struct ftl_rq *rq);
/* IO error request callback */
void (*error)(struct ftl_rq *rq, struct ftl_band *band,
uint64_t idx, uint64_t count);
/* Owner context */
void *priv;
@ -219,6 +229,9 @@ struct ftl_rq {
/* Request physical address, on IO completion set for append device */
ftl_addr addr;
/* Band to which IO is issued */
struct ftl_band *band;
struct spdk_bdev_io_wait_entry bdev_io_wait;
} io;
@ -228,7 +241,7 @@ struct ftl_rq {
struct ftl_rq_entry entries[];
};
/* Used for reading/writing LBA map during runtime and recovery */
/* Used for reading/writing P2L map during runtime and recovery */
struct ftl_basic_rq {
struct spdk_ftl_dev *dev;
@ -258,6 +271,9 @@ struct ftl_basic_rq {
/* Request physical address, on IO completion set for append device */
ftl_addr addr;
/* Band to which IO is issued */
struct ftl_band *band;
/* Chunk to which IO is issued */
struct ftl_nv_cache_chunk *chunk;

View File

@ -4,9 +4,9 @@
*/
#include "ftl_l2p.h"
#include "ftl_band.h"
#include "ftl_nv_cache.h"
#include "ftl_l2p_flat.h"
#include "ftl_core.h"
/* TODO: Verify why function pointers had worse performance than compile time constants */

View File

@ -5,6 +5,7 @@
#include "ftl_l2p.h"
#include "ftl_core.h"
#include "ftl_band.h"
#include "ftl_utils.h"
#include "ftl_l2p_flat.h"
#include "utils/ftl_addr_utils.h"

View File

@ -7,6 +7,7 @@
#include "ftl_core.h"
#include "ftl_utils.h"
#include "ftl_band.h"
#include "ftl_layout.h"
#include "ftl_nv_cache.h"
#include "ftl_sb.h"
@ -158,6 +159,34 @@ setup_layout_nvc(struct spdk_ftl_dev *dev)
set_region_bdev_nvc(region, dev);
offset += region->current.blocks;
/* Initialize band info metadata */
if (offset >= layout->nvc.total_blocks) {
goto error;
}
region = &layout->region[FTL_LAYOUT_REGION_TYPE_BAND_MD];
region->type = FTL_LAYOUT_REGION_TYPE_BAND_MD;
region->mirror_type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR;
region->name = "band_md";
region->current.version = region->prev.version = FTL_BAND_VERSION_CURRENT;
region->current.offset = offset;
region->current.blocks = blocks_region(ftl_get_num_bands(dev) * sizeof(struct ftl_band_md));
region->entry_size = sizeof(struct ftl_band_md) / FTL_BLOCK_SIZE;
region->num_entries = ftl_get_num_bands(dev);
set_region_bdev_nvc(region, dev);
offset += region->current.blocks;
/* Initialize band info metadata mirror */
if (offset >= layout->nvc.total_blocks) {
goto error;
}
mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR];
*mirror = *region;
mirror->type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR;
mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
mirror->name = "band_md_mirror";
mirror->current.offset += region->current.blocks;
offset += mirror->current.blocks;
if (offset >= layout->nvc.total_blocks) {
goto error;
}

View File

@ -23,6 +23,11 @@ enum ftl_layout_region_type {
/* If using cached L2P, this region stores the serialized instance of it */
FTL_LAYOUT_REGION_TYPE_L2P,
/* State of bands */
FTL_LAYOUT_REGION_TYPE_BAND_MD,
/* Mirrored instance of bands state */
FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR,
/* State of chunks */
FTL_LAYOUT_REGION_TYPE_NVC_MD,
/* Mirrored instance of the state of chunks */
@ -99,6 +104,8 @@ struct ftl_layout {
/* Organization for base device */
struct {
uint64_t total_blocks;
uint64_t num_usable_blocks;
uint64_t user_blocks;
} base;
/* Organization for NV cache */

View File

@ -12,6 +12,7 @@
#include "ftl_nv_cache.h"
#include "ftl_nv_cache_io.h"
#include "ftl_core.h"
#include "ftl_band.h"
#include "utils/ftl_addr_utils.h"
#include "mngt/ftl_mngt.h"

View File

@ -8,6 +8,7 @@
#include "ftl_core.h"
#include "ftl_mngt.h"
#include "ftl_mngt_steps.h"
#include "ftl_band.h"
struct ftl_io_channel_ctx {
struct ftl_io_channel *ioch;

View File

@ -8,6 +8,7 @@
#include "ftl_core.h"
#include "ftl_mngt.h"
#include "ftl_mngt_steps.h"
#include "ftl_band.h"
#include "ftl_l2p.h"
static void

View File

@ -10,6 +10,7 @@
#include "ftl_mngt.h"
#include "ftl_mngt_steps.h"
#include "ftl_utils.h"
#include "ftl_band.h"
#include "ftl_internal.h"
#include "ftl_sb.h"
@ -30,12 +31,12 @@ is_buffer_needed(enum ftl_layout_region_type type)
#ifdef SPDK_FTL_VSS_EMU
case FTL_LAYOUT_REGION_TYPE_VSS:
#endif
case FTL_LAYOUT_REGION_TYPE_SB:
case FTL_LAYOUT_REGION_TYPE_SB_BASE:
case FTL_LAYOUT_REGION_TYPE_DATA_NVC:
case FTL_LAYOUT_REGION_TYPE_DATA_BASE:
case FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR:
case FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR:
return false;
default:

View File

@ -37,4 +37,8 @@
} \
} while (0)
#define FTL_INVALID_VALUE ((uint64_t)-1)
#define FTL_BAND_ID_INVALID FTL_INVALID_VALUE
#define FTL_BAND_PHYS_ID_INVALID FTL_INVALID_VALUE
#endif /* FTL_DEFS_H */