Spdk/lib/ftl/mngt/ftl_mngt_md.c
Kozlowski Mateusz 0e33da4974 ftl: fast shutdown
Adds API for fast shutdown - the ability for FTL to skip most
of the metadata persists made during clean shutdown, and relying
on their representation in shared memory instead. This allows for
faster update of SPDK (or just FTL, assuming no metadata changes),
with downtime reduction from 2-5 seconds to 500-1000 ms (for
14TiB+800GiB base and cache drives).

Signed-off-by: Artur Paszkiewicz <artur.paszkiewicz@intel.com>
Signed-off-by: Kozlowski Mateusz <mateusz.kozlowski@intel.com>
Change-Id: I5999d31698a81512db8d5893eabee7b505c80d06
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13348
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>
2022-08-30 14:48:50 +00:00

333 lines
8.2 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#include "spdk/thread.h"
#include "spdk/crc32.h"
#include "ftl_core.h"
#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"
void
ftl_mngt_init_layout(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
if (ftl_layout_setup(dev)) {
ftl_mngt_fail_step(mngt);
} else {
ftl_mngt_next_step(mngt);
}
}
static bool
is_buffer_needed(enum ftl_layout_region_type type)
{
switch (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:
return true;
}
}
void
ftl_mngt_init_md(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
struct ftl_layout *layout = &dev->layout;
struct ftl_layout_region *region = layout->region;
uint64_t i;
int md_flags;
for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; i++, region++) {
if (layout->md[i]) {
/*
* Some metadata objects are initialized by other FTL
* components. At the moment it's only used by superblock (and its mirror) -
* during load time we need to read it earlier in order to get the layout for the
* other regions.
*/
continue;
}
md_flags = is_buffer_needed(i) ? ftl_md_create_region_flags(dev,
region->type) : FTL_MD_CREATE_NO_MEM;
layout->md[i] = ftl_md_create(dev, region->current.blocks, region->vss_blksz, region->name,
md_flags, region);
if (NULL == layout->md[i]) {
ftl_mngt_fail_step(mngt);
return;
}
}
ftl_mngt_next_step(mngt);
}
void
ftl_mngt_deinit_md(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
struct ftl_layout *layout = &dev->layout;
struct ftl_layout_region *region = layout->region;
uint64_t i;
for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; i++, region++) {
if (layout->md[i]) {
ftl_md_destroy(layout->md[i], ftl_md_destroy_region_flags(dev, layout->region[i].type));
layout->md[i] = NULL;
}
}
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;
assert(type < FTL_LAYOUT_REGION_TYPE_MAX);
md = layout->md[type];
if (!md) {
ftl_mngt_fail_step(mngt);
return;
}
md->owner.cb_ctx = mngt;
md->cb = persist_cb;
ftl_md_persist(md);
}
void
ftl_mngt_persist_nv_cache_metadata(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
if (ftl_nv_cache_save_state(&dev->nv_cache)) {
ftl_mngt_fail_step(mngt);
return;
}
persist(dev, mngt, FTL_LAYOUT_REGION_TYPE_NVC_MD);
}
void
ftl_mngt_persist_band_info_metadata(
struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
persist(dev, mngt, FTL_LAYOUT_REGION_TYPE_BAND_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;
dev->sb_shm->shm_clean = false;
/* Max 16 IO depth per band relocate */
sb->max_reloc_qdepth = 16;
sb->overprovisioning = dev->conf.overprovisioning;
ftl_band_init_gc_iter(dev);
/* 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;
dev->sb_shm->shm_clean = false;
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)
{
struct ftl_layout *layout = &dev->layout;
struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
/* Initialize VSS layout */
ftl_layout_setup_vss_emu(dev);
/* Allocate md buf */
layout->md[FTL_LAYOUT_REGION_TYPE_VSS] = ftl_md_create(dev, region->current.blocks,
region->vss_blksz, NULL, 0, region);
if (NULL == layout->md[FTL_LAYOUT_REGION_TYPE_VSS]) {
ftl_mngt_fail_step(mngt);
return;
}
ftl_mngt_next_step(mngt);
}
void
ftl_mngt_md_deinit_vss_emu(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
struct ftl_layout *layout = &dev->layout;
if (layout->md[FTL_LAYOUT_REGION_TYPE_VSS]) {
ftl_md_destroy(layout->md[FTL_LAYOUT_REGION_TYPE_VSS], 0);
layout->md[FTL_LAYOUT_REGION_TYPE_VSS] = NULL;
}
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];
int md_create_flags = FTL_MD_CREATE_SHM | FTL_MD_CREATE_SHM_NEW;
/* 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);
}
/* Allocate md buf */
dev->sb_shm = NULL;
dev->sb_shm_md = ftl_md_create(dev, spdk_divide_round_up(sizeof(*dev->sb_shm), FTL_BLOCK_SIZE),
0, "sb_shm",
md_create_flags, NULL);
if (dev->sb_shm_md == NULL) {
ftl_mngt_fail_step(mngt);
return;
}
dev->sb_shm = ftl_md_get_buffer(dev->sb_shm_md);
/* 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,
md_create_flags, 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, FTL_MD_CREATE_HEAP, 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],
ftl_md_destroy_region_flags(dev, 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], 0);
layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE] = NULL;
}
ftl_md_destroy(dev->sb_shm_md, ftl_md_destroy_shm_flags(dev));
dev->sb_shm_md = NULL;
dev->sb_shm = NULL;
ftl_mngt_next_step(mngt);
}