Spdk/lib/ftl/ftl_layout.c
Artur Paszkiewicz c6880a3974 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>
2022-08-02 19:00:42 +00:00

389 lines
11 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#include "spdk/bdev.h"
#include "ftl_core.h"
#include "ftl_utils.h"
#include "ftl_layout.h"
#include "ftl_sb.h"
static inline float
blocks2mib(uint64_t blocks)
{
float result;
result = blocks;
result *= FTL_BLOCK_SIZE;
result /= 1024UL;
result /= 1024UL;
return result;
}
/* TODO: This should be aligned to the write unit size of the device a given piece of md is on.
* The tricky part is to make sure interpreting old alignment values will still be valid...
*/
#define FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS 32ULL
#define FTL_LAYOUT_REGION_ALIGNMENT_BYTES (FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS * FTL_BLOCK_SIZE)
static inline uint64_t
blocks_region(uint64_t bytes)
{
const uint64_t alignment = FTL_LAYOUT_REGION_ALIGNMENT_BYTES;
uint64_t result;
result = spdk_divide_round_up(bytes, alignment);
result *= alignment;
result /= FTL_BLOCK_SIZE;
return result;
}
static void
dump_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
{
assert(!(region->current.offset % FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS));
assert(!(region->current.blocks % FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS));
FTL_NOTICELOG(dev, "Region %s\n", region->name);
FTL_NOTICELOG(dev, " offset: %.2f MiB\n",
blocks2mib(region->current.offset));
FTL_NOTICELOG(dev, " blocks: %.2f MiB\n",
blocks2mib(region->current.blocks));
}
int
ftl_validate_regions(struct spdk_ftl_dev *dev, struct ftl_layout *layout)
{
uint64_t i, j;
/* Validate if regions doesn't overlap each other */
/* TODO: major upgrades: keep track of and validate free_nvc/free_btm regions */
for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; i++) {
struct ftl_layout_region *r1 = &layout->region[i];
for (j = 0; j < FTL_LAYOUT_REGION_TYPE_MAX; j++) {
struct ftl_layout_region *r2 = &layout->region[j];
if (r1->bdev_desc != r2->bdev_desc) {
continue;
}
if (i == j) {
continue;
}
uint64_t r1_begin = r1->current.offset;
uint64_t r1_end = r1->current.offset + r1->current.blocks - 1;
uint64_t r2_begin = r2->current.offset;
uint64_t r2_end = r2->current.offset + r2->current.blocks - 1;
if (spdk_max(r1_begin, r2_begin) <= spdk_min(r1_end, r2_end)) {
FTL_ERRLOG(dev, "Layout initialization ERROR, two regions overlap, "
"%s and %s\n", r1->name, r2->name);
return -1;
}
}
}
return 0;
}
static uint64_t
get_num_user_lbas(struct spdk_ftl_dev *dev)
{
uint64_t blocks = 0;
blocks = dev->layout.base.total_blocks;
blocks = (blocks * (100 - dev->conf.overprovisioning)) / 100;
return blocks;
}
static void
set_region_bdev_nvc(struct ftl_layout_region *reg, struct spdk_ftl_dev *dev)
{
reg->bdev_desc = dev->cache_bdev_desc;
reg->ioch = dev->cache_ioch;
reg->vss_blksz = dev->cache_md_size;
}
static void
set_region_bdev_btm(struct ftl_layout_region *reg, struct spdk_ftl_dev *dev)
{
reg->bdev_desc = dev->base_bdev_desc;
reg->ioch = dev->base_ioch;
reg->vss_blksz = 0;
}
static int
setup_layout_nvc(struct spdk_ftl_dev *dev)
{
uint64_t offset = 0;
struct ftl_layout *layout = &dev->layout;
struct ftl_layout_region *region;
#ifdef SPDK_FTL_VSS_EMU
/* Skip the already init`d VSS region */
region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
offset += region->current.blocks;
if (offset >= layout->nvc.total_blocks) {
goto error;
}
#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";
region->current.version = region->prev.version = 0;
region->current.offset = offset;
region->current.blocks = layout->nvc.total_blocks - offset;
set_region_bdev_nvc(region, dev);
offset += region->current.blocks;
if (offset > layout->nvc.total_blocks) {
FTL_ERRLOG(dev, "Error when setup NV cache layout\n");
goto error;
}
return 0;
error:
FTL_ERRLOG(dev, "Insufficient NV Cache capacity to preserve metadata\n");
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 = 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;
}
int
ftl_layout_setup(struct spdk_ftl_dev *dev)
{
const struct spdk_bdev *bdev;
struct ftl_layout *layout = &dev->layout;
uint64_t i;
uint64_t num_lbas;
bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
layout->base.total_blocks = spdk_bdev_get_num_blocks(bdev);
bdev = spdk_bdev_desc_get_bdev(dev->cache_bdev_desc);
layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
/* 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;
}
/*
* Initialize L2P information
*/
num_lbas = get_num_user_lbas(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;
}
layout->l2p.addr_length = spdk_u64log2(layout->base.total_blocks + layout->nvc.total_blocks) + 1;
layout->l2p.addr_size = layout->l2p.addr_length > 32 ? 8 : 4;
layout->l2p.lbas_in_page = FTL_BLOCK_SIZE / layout->l2p.addr_size;
if (setup_layout_nvc(dev)) {
return -EINVAL;
}
if (setup_layout_base(dev)) {
return -EINVAL;
}
if (ftl_validate_regions(dev, layout)) {
return -EINVAL;
}
FTL_NOTICELOG(dev, "Base device capacity: %.2f MiB\n",
blocks2mib(layout->base.total_blocks));
FTL_NOTICELOG(dev, "NV cache device capacity: %.2f MiB\n",
blocks2mib(layout->nvc.total_blocks));
FTL_NOTICELOG(dev, "L2P entries: %"PRIu64"\n",
dev->num_lbas);
FTL_NOTICELOG(dev, "L2P address size: %"PRIu64"\n",
layout->l2p.addr_size);
return 0;
}
#ifdef SPDK_FTL_VSS_EMU
void
ftl_layout_setup_vss_emu(struct spdk_ftl_dev *dev)
{
const struct spdk_bdev *bdev;
struct ftl_layout *layout = &dev->layout;
struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
assert(layout->md[FTL_LAYOUT_REGION_TYPE_VSS] == NULL);
region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
region->type = FTL_LAYOUT_REGION_TYPE_VSS;
region->name = "vss";
region->current.version = region->prev.version = 0;
region->current.offset = 0;
bdev = spdk_bdev_desc_get_bdev(dev->cache_bdev_desc);
layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
region->current.blocks = blocks_region(dev->cache_md_size * layout->nvc.total_blocks);
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);
}
#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)
{
struct ftl_layout *layout = &dev->layout;
int i;
FTL_NOTICELOG(dev, "NV cache layout:\n");
for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
if (layout->region[i].bdev_desc == dev->cache_bdev_desc) {
dump_region(dev, &layout->region[i]);
}
}
FTL_NOTICELOG(dev, "Bottom device layout:\n");
for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
if (layout->region[i].bdev_desc == dev->base_bdev_desc) {
dump_region(dev, &layout->region[i]);
}
}
}