ftl: superblock

Signed-off-by: Artur Paszkiewicz <artur.paszkiewicz@intel.com>
Signed-off-by: Kozlowski Mateusz <mateusz.kozlowski@intel.com>
Change-Id: Ic8ca0cd3bf3621ad5604e83ed24c0fa59a83f124
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13313
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
Artur Paszkiewicz 2022-06-21 15:05:02 +02:00 committed by Jim Harris
parent f725ca81cf
commit c6880a3974
13 changed files with 462 additions and 5 deletions

View File

@ -21,7 +21,7 @@ CFLAGS += -I.
FTL_SUBDIRS := mngt utils
C_SRCS = ftl_core.c ftl_init.c ftl_layout.c ftl_debug.c ftl_io.c
C_SRCS = ftl_core.c ftl_init.c ftl_layout.c ftl_debug.c ftl_io.c ftl_sb.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
C_SRCS += utils/ftl_conf.c utils/ftl_md.c utils/ftl_mempool.c

View File

@ -19,6 +19,7 @@
#include "ftl_internal.h"
#include "ftl_io.h"
#include "ftl_layout.h"
#include "ftl_sb.h"
#include "utils/ftl_log.h"
/* When using VSS on nvcache, FTL sometimes doesn't require the contents of metadata.
@ -35,6 +36,9 @@ struct spdk_ftl_dev {
/* FTL device layout */
struct ftl_layout layout;
/* FTL superblock */
struct ftl_superblock *sb;
/* Queue of registered IO channels */
TAILQ_HEAD(, ftl_io_channel) ioch_queue;

View File

@ -8,6 +8,7 @@
#include "ftl_core.h"
#include "ftl_utils.h"
#include "ftl_layout.h"
#include "ftl_sb.h"
static inline float
blocks2mib(uint64_t blocks)
@ -27,7 +28,6 @@ blocks2mib(uint64_t blocks)
#define FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS 32ULL
#define FTL_LAYOUT_REGION_ALIGNMENT_BYTES (FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS * FTL_BLOCK_SIZE)
#ifdef SPDK_FTL_VSS_EMU
static inline uint64_t
blocks_region(uint64_t bytes)
{
@ -40,7 +40,6 @@ blocks_region(uint64_t bytes)
return result;
}
#endif
static void
dump_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
@ -136,6 +135,14 @@ setup_layout_nvc(struct spdk_ftl_dev *dev)
}
#endif
/* Skip the superblock region. Already init`d in ftl_layout_setup_superblock */
region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
offset += region->current.blocks;
if (offset >= layout->nvc.total_blocks) {
goto error;
}
region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_NVC];
region->type = FTL_LAYOUT_REGION_TYPE_DATA_NVC;
region->name = "data_nvc";
@ -157,21 +164,55 @@ error:
return -1;
}
static ftl_addr
layout_base_offset(struct spdk_ftl_dev *dev)
{
ftl_addr addr;
addr = dev->num_bands * ftl_get_num_blocks_in_band(dev);
return addr;
}
static int
setup_layout_base(struct spdk_ftl_dev *dev)
{
uint64_t left, offset;
struct ftl_layout *layout = &dev->layout;
struct ftl_layout_region *region;
/* Base device layout is following:
* - data
* - superblock
* - valid map
*
* Superblock has been already configured, its offset marks the end of the data region
*/
offset = layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.offset;
/* Setup data region on base device */
region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_BASE];
region->type = FTL_LAYOUT_REGION_TYPE_DATA_BASE;
region->name = "data_btm";
region->current.version = region->prev.version = 0;
region->current.offset = 0;
region->current.blocks = layout->base.total_blocks;
region->current.blocks = offset;
set_region_bdev_btm(region, dev);
/* Move offset after base superblock */
offset += layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.blocks;
/* Checking for underflow */
left = layout->base.total_blocks - offset;
if (left > layout->base.total_blocks) {
FTL_ERRLOG(dev, "Error when setup base device layout\n");
return -1;
}
if (offset > layout->base.total_blocks) {
FTL_ERRLOG(dev, "Error when setup base device layout\n");
return -1;
}
return 0;
}
@ -191,6 +232,11 @@ ftl_layout_setup(struct spdk_ftl_dev *dev)
/* Initialize mirrors types */
for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
if (i == FTL_LAYOUT_REGION_TYPE_SB) {
/* Super block has been already initialized */
continue;
}
layout->region[i].mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
}
@ -201,6 +247,7 @@ ftl_layout_setup(struct spdk_ftl_dev *dev)
if (dev->num_lbas == 0) {
assert(dev->conf.mode & SPDK_FTL_MODE_CREATE);
dev->num_lbas = num_lbas;
dev->sb->lba_cnt = num_lbas;
} else if (dev->num_lbas != num_lbas) {
FTL_ERRLOG(dev, "Mismatched FTL num_lbas\n");
return -EINVAL;
@ -262,6 +309,64 @@ ftl_layout_setup_vss_emu(struct spdk_ftl_dev *dev)
}
#endif
int
ftl_layout_setup_superblock(struct spdk_ftl_dev *dev)
{
struct ftl_layout *layout = &dev->layout;
struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
uint64_t total_blocks, offset, left;
assert(layout->md[FTL_LAYOUT_REGION_TYPE_SB] == NULL);
/* Initialize superblock region */
region->type = FTL_LAYOUT_REGION_TYPE_SB;
region->mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
region->name = "sb";
region->current.version = FTL_METADATA_VERSION_CURRENT;
region->prev.version = FTL_METADATA_VERSION_CURRENT;
region->current.offset = 0;
/*
* VSS region must go first in case SB to make calculating its relative size easier
*/
#ifdef SPDK_FTL_VSS_EMU
region->current.offset = layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.offset +
layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.blocks;
#endif
region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE);
region->vss_blksz = 0;
region->bdev_desc = dev->cache_bdev_desc;
region->ioch = dev->cache_ioch;
assert(region->bdev_desc != NULL);
assert(region->ioch != NULL);
region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE];
region->type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
region->mirror_type = FTL_LAYOUT_REGION_TYPE_MAX;
region->name = "sb_mirror";
region->current.version = FTL_METADATA_VERSION_CURRENT;
region->prev.version = FTL_METADATA_VERSION_CURRENT;
/* TODO: This should really be at offset 0 - think how best to upgrade between the two layouts
* This is an issue if some other metadata appears at block 0 of base device (most likely GPT or blobstore)
*/
region->current.offset = layout_base_offset(dev);
region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE);
set_region_bdev_btm(region, dev);
/* Check if SB can be stored at the end of base device */
total_blocks = spdk_bdev_get_num_blocks(
spdk_bdev_desc_get_bdev(dev->base_bdev_desc));
offset = region->current.offset + region->current.blocks;
left = total_blocks - offset;
if ((left > total_blocks) || (offset > total_blocks)) {
FTL_ERRLOG(dev, "Error when setup base device super block\n");
return -1;
}
return 0;
}
void
ftl_layout_dump(struct spdk_ftl_dev *dev)

View File

@ -16,6 +16,10 @@ enum ftl_layout_region_type {
/** VSS region for NV cache VSS emulation */
FTL_LAYOUT_REGION_TYPE_VSS,
#endif
/* Superblock describing the basic FTL information */
FTL_LAYOUT_REGION_TYPE_SB,
/* Mirrored instance of the superblock on the base device */
FTL_LAYOUT_REGION_TYPE_SB_BASE,
/* User data region on the nv cache device */
FTL_LAYOUT_REGION_TYPE_DATA_NVC,
@ -115,6 +119,11 @@ struct ftl_layout {
*/
int ftl_layout_setup(struct spdk_ftl_dev *dev);
/**
* @brief Setup FTL layout of a superblock
*/
int ftl_layout_setup_superblock(struct spdk_ftl_dev *dev);
#ifdef SPDK_FTL_VSS_EMU
/**
* @brief Setup FTL layout of VSS emu

14
lib/ftl/ftl_sb.c Normal file
View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#include "ftl_sb.h"
#include "ftl_core.h"
#include "ftl_layout.h"
bool
ftl_superblock_check_magic(struct ftl_superblock *sb)
{
return sb->header.magic == FTL_SUPERBLOCK_MAGIC;
}

17
lib/ftl/ftl_sb.h Normal file
View File

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#ifndef FTL_SB_H
#define FTL_SB_H
#include "spdk/uuid.h"
#include "ftl_sb_common.h"
#include "ftl_sb_current.h"
struct spdk_ftl_dev;
bool ftl_superblock_check_magic(struct ftl_superblock *sb);
#endif /* FTL_SB_H */

50
lib/ftl/ftl_sb_common.h Normal file
View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#ifndef FTL_SB_COMMON_H
#define FTL_SB_COMMON_H
#include "spdk/stdinc.h"
#include "utils/ftl_defs.h"
/* Size of superblock on NV cache, make it bigger for future fields */
#define FTL_SUPERBLOCK_SIZE (128ULL * KiB)
#define FTL_MAGIC(a, b, c, d) \
((UINT64_C(a) << 48) | (UINT64_C(b) << 32) | (UINT64_C(c) << 16) | \
UINT64_C(d))
/**
* Magic number identifies FTL superblock
*/
#define FTL_SUPERBLOCK_MAGIC FTL_MAGIC(0x1410, 0x1683, 0x1920, 0x1989)
struct ftl_superblock_gc_info {
/* High priority band; if there's no free bands after dirty shutdown, don't restart GC from same id, or phys_id -
* pick actual lowest validity band to avoid being stuck and try to write it to the open band.
*/
uint64_t band_id_high_prio;
/* Currently relocated band (note it's just id, not seq_id ie. its actual location on disk) */
uint64_t current_band_id;
/* Bands are grouped together into larger reclaim units; this is the band id translated to those units */
uint64_t band_phys_id;
/* May be updating multiple fields at the same time, clearing/setting this marks the transaction */
uint64_t is_valid;
};
struct ftl_superblock_header {
uint64_t magic;
uint64_t crc;
uint64_t version;
};
struct ftl_superblock_md_region {
uint32_t type;
uint32_t version;
uint64_t blk_offs;
uint64_t blk_sz;
};
#endif /* FTL_SB_COMMON_H */

48
lib/ftl/ftl_sb_current.h Normal file
View File

@ -0,0 +1,48 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#ifndef FTL_SB_CURRENT_H
#define FTL_SB_CURRENT_H
#include "spdk/uuid.h"
#include "ftl_sb_common.h"
#define FTL_METADATA_VERSION_4 4
#define FTL_METADATA_VERSION_CURRENT FTL_METADATA_VERSION_4
struct ftl_superblock {
struct ftl_superblock_header header;
struct spdk_uuid uuid;
/* Flag describing clean shutdown */
uint64_t clean;
/* Number of surfaced LBAs */
uint64_t lba_cnt;
/* Percentage of base device blocks not exposed to the user */
uint64_t overprovisioning;
/* Maximum IO depth per band relocate */
uint64_t max_reloc_qdepth;
/* Reserved field */
uint64_t reserved;
uint32_t reserved2;
struct ftl_superblock_gc_info gc_info;
struct ftl_superblock_md_region md_layout_head;
};
SPDK_STATIC_ASSERT(offsetof(struct ftl_superblock, header) == 0,
"Invalid placement of header");
SPDK_STATIC_ASSERT(FTL_SUPERBLOCK_SIZE >= sizeof(struct ftl_superblock),
"FTL SB metadata size is invalid");
#endif /* FTL_SB_CURRENT_H */

View File

@ -11,6 +11,7 @@
#include "ftl_mngt_steps.h"
#include "ftl_utils.h"
#include "ftl_internal.h"
#include "ftl_sb.h"
void
ftl_mngt_init_layout(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
@ -29,6 +30,8 @@ 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:
return false;
@ -83,6 +86,103 @@ ftl_mngt_deinit_md(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
ftl_mngt_next_step(mngt);
}
static void
persist_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
{
struct ftl_mngt_process *mngt = md->owner.cb_ctx;
if (status) {
ftl_mngt_fail_step(mngt);
} else {
ftl_mngt_next_step(mngt);
}
}
static void
persist(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt,
enum ftl_layout_region_type type)
{
struct ftl_layout *layout = &dev->layout;
struct ftl_md *md = layout->md[type];
assert(type < FTL_LAYOUT_REGION_TYPE_MAX);
if (!md) {
ftl_mngt_fail_step(mngt);
return;
}
md->owner.cb_ctx = mngt;
md->cb = persist_cb;
ftl_md_persist(md);
}
static uint32_t
get_sb_crc(struct ftl_superblock *sb)
{
uint32_t crc = 0;
/* Calculate CRC excluding CRC field in superblock */
void *buffer = sb;
size_t offset = offsetof(struct ftl_superblock, header.crc);
size_t size = offset;
crc = spdk_crc32c_update(buffer, size, crc);
buffer += offset + sizeof(sb->header.crc);
size = FTL_SUPERBLOCK_SIZE - offset - sizeof(sb->header.crc);
crc = spdk_crc32c_update(buffer, size, crc);
return crc;
}
void
ftl_mngt_init_default_sb(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
struct ftl_superblock *sb = dev->sb;
sb->header.magic = FTL_SUPERBLOCK_MAGIC;
sb->header.version = FTL_METADATA_VERSION_CURRENT;
sb->uuid = dev->conf.uuid;
sb->clean = 0;
/* Max 16 IO depth per band relocate */
sb->max_reloc_qdepth = 16;
sb->overprovisioning = dev->conf.overprovisioning;
/* md layout isn't initialized yet.
* empty region list => all regions in the default location */
sb->md_layout_head.type = FTL_LAYOUT_REGION_TYPE_INVALID;
sb->header.crc = get_sb_crc(sb);
ftl_mngt_next_step(mngt);
}
void
ftl_mngt_set_dirty(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
struct ftl_superblock *sb = dev->sb;
sb->clean = 0;
sb->header.crc = get_sb_crc(sb);
persist(dev, mngt, FTL_LAYOUT_REGION_TYPE_SB);
}
/*
* Initializes the superblock fields during first startup of FTL
*/
static const struct ftl_mngt_process_desc desc_init_sb = {
.name = "SB initialize",
.steps = {
{
.name = "Default-initialize superblock",
.action = ftl_mngt_init_default_sb,
},
{}
}
};
#ifdef SPDK_FTL_VSS_EMU
void
ftl_mngt_md_init_vss_emu(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
@ -116,3 +216,69 @@ ftl_mngt_md_deinit_vss_emu(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mn
ftl_mngt_next_step(mngt);
}
#endif
void
ftl_mngt_superblock_init(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
struct ftl_layout *layout = &dev->layout;
struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
char uuid[SPDK_UUID_STRING_LEN];
/* Must generate UUID before MD create on SHM for the SB */
if (dev->conf.mode & SPDK_FTL_MODE_CREATE) {
spdk_uuid_generate(&dev->conf.uuid);
spdk_uuid_fmt_lower(uuid, sizeof(uuid), &dev->conf.uuid);
FTL_NOTICELOG(dev, "Create new FTL, UUID %s\n", uuid);
}
/* Setup the layout of a superblock */
if (ftl_layout_setup_superblock(dev)) {
ftl_mngt_fail_step(mngt);
return;
}
/* Allocate md buf */
layout->md[FTL_LAYOUT_REGION_TYPE_SB] = ftl_md_create(dev, region->current.blocks,
region->vss_blksz, region->name, false, region);
if (NULL == layout->md[FTL_LAYOUT_REGION_TYPE_SB]) {
ftl_mngt_fail_step(mngt);
return;
}
/* Link the md buf to the device */
dev->sb = ftl_md_get_buffer(layout->md[FTL_LAYOUT_REGION_TYPE_SB]);
/* Setup superblock mirror to QLC */
region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE];
layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE] = ftl_md_create(dev, region->current.blocks,
region->vss_blksz, NULL, false, region);
if (NULL == layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE]) {
ftl_mngt_fail_step(mngt);
return;
}
/* Initialize the superblock */
if (dev->conf.mode & SPDK_FTL_MODE_CREATE) {
ftl_mngt_call_process(mngt, &desc_init_sb);
} else {
ftl_mngt_fail_step(mngt);
}
}
void
ftl_mngt_superblock_deinit(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
struct ftl_layout *layout = &dev->layout;
if (layout->md[FTL_LAYOUT_REGION_TYPE_SB]) {
ftl_md_destroy(layout->md[FTL_LAYOUT_REGION_TYPE_SB]);
layout->md[FTL_LAYOUT_REGION_TYPE_SB] = NULL;
}
if (layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE]) {
ftl_md_destroy(layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE]);
layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE] = NULL;
}
ftl_mngt_next_step(mngt);
}

View File

@ -49,6 +49,11 @@ static const struct ftl_mngt_process_desc desc_startup = {
.cleanup = ftl_mngt_md_deinit_vss_emu
},
#endif
{
.name = "Initialize superblock",
.action = ftl_mngt_superblock_init,
.cleanup = ftl_mngt_superblock_deinit
},
{
.name = "Register IO device",
.action = ftl_mngt_register_io_device,
@ -88,6 +93,10 @@ static const struct ftl_mngt_process_desc desc_first_start = {
.name = "Scrub NV cache",
.action = ftl_mngt_scrub_nv_cache,
},
{
.name = "Set FTL dirty state",
.action = ftl_mngt_set_dirty,
},
{
.name = "Start core poller",
.action = ftl_mngt_start_core_poller,

View File

@ -20,6 +20,10 @@ void ftl_mngt_md_init_vss_emu(struct spdk_ftl_dev *dev, struct ftl_mngt_process
void ftl_mngt_md_deinit_vss_emu(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
#endif
void ftl_mngt_superblock_init(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
void ftl_mngt_superblock_deinit(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
void ftl_mngt_open_cache_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
void ftl_mngt_close_cache_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
@ -50,4 +54,8 @@ void ftl_mngt_rollback_device(struct spdk_ftl_dev *dev, struct ftl_mngt_process
void ftl_mngt_dump_stats(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
void ftl_mngt_init_default_sb(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
void ftl_mngt_set_dirty(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
#endif /* FTL_MNGT_STEPS_H */

View File

@ -9,7 +9,6 @@
#include "ftl_core.h"
#include "ftl_md.h"
#include "ftl_nv_cache_io.h"
#include "ftl_utils.h"
struct ftl_md;
static void io_submit(struct ftl_md *md);
@ -703,6 +702,21 @@ restore_mirror_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
}
}
static void
restore_sync_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
{
struct ftl_md *primary = md->owner.private;
if (status) {
/* Cannot sync the object from the primary to the mirror, mark error and fail */
primary->io.status = -EIO;
io_done(primary);
} else {
primary->cb(dev, primary, primary->io.status);
io_cleanup(primary);
}
}
static int
restore_done(struct ftl_md *md)
{
@ -729,6 +743,18 @@ restore_done(struct ftl_md *md)
} else {
return -EIO;
}
} else if (0 == md->io.status && false == md->dev->sb->clean) {
if (has_mirror(md)) {
/* There was a dirty shutdown, synchronize primary to mirror */
/* Set callback and context in the mirror */
md->mirror->cb = restore_sync_cb;
md->mirror->owner.private = md;
/* First persist the mirror */
ftl_md_persist(md->mirror);
return -EAGAIN;
}
}
return md->io.status;

View File

@ -161,6 +161,7 @@ free_device(struct spdk_ftl_dev *dev)
free_threads();
free(dev->ioch);
free(dev->sb);
free(dev);
}