ftl: device layout abstraction
Signed-off-by: Artur Paszkiewicz <artur.paszkiewicz@intel.com> Signed-off-by: Kozlowski Mateusz <mateusz.kozlowski@intel.com> Change-Id: I5db829ffb9044179cdf0807c3aeeb3a850a276d2 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13292 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Konrad Sztyber <konrad.sztyber@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
parent
e49ccfc820
commit
2b5bba569f
@ -17,8 +17,9 @@ CFLAGS += -I.
|
|||||||
|
|
||||||
FTL_SUBDIRS := mngt utils
|
FTL_SUBDIRS := mngt utils
|
||||||
|
|
||||||
C_SRCS = ftl_core.c ftl_init.c
|
C_SRCS = ftl_core.c ftl_init.c ftl_layout.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.c mngt/ftl_mngt_bdev.c mngt/ftl_mngt_shutdown.c mngt/ftl_mngt_startup.c
|
||||||
|
C_SRCS += mngt/ftl_mngt_md.c
|
||||||
C_SRCS += utils/ftl_conf.c
|
C_SRCS += utils/ftl_conf.c
|
||||||
|
|
||||||
SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_ftl.map)
|
SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_ftl.map)
|
||||||
|
@ -17,12 +17,16 @@
|
|||||||
#include "spdk/bdev_zone.h"
|
#include "spdk/bdev_zone.h"
|
||||||
|
|
||||||
#include "ftl_internal.h"
|
#include "ftl_internal.h"
|
||||||
|
#include "ftl_layout.h"
|
||||||
#include "utils/ftl_log.h"
|
#include "utils/ftl_log.h"
|
||||||
|
|
||||||
struct spdk_ftl_dev {
|
struct spdk_ftl_dev {
|
||||||
/* Configuration */
|
/* Configuration */
|
||||||
struct spdk_ftl_conf conf;
|
struct spdk_ftl_conf conf;
|
||||||
|
|
||||||
|
/* FTL device layout */
|
||||||
|
struct ftl_layout layout;
|
||||||
|
|
||||||
/* Underlying device */
|
/* Underlying device */
|
||||||
struct spdk_bdev_desc *base_bdev_desc;
|
struct spdk_bdev_desc *base_bdev_desc;
|
||||||
|
|
||||||
|
228
lib/ftl/ftl_layout.c
Normal file
228
lib/ftl/ftl_layout.c
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/* 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"
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
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 int
|
||||||
|
setup_layout_base(struct spdk_ftl_dev *dev)
|
||||||
|
{
|
||||||
|
struct ftl_layout *layout = &dev->layout;
|
||||||
|
struct ftl_layout_region *region;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
set_region_bdev_btm(region, dev);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
lib/ftl/ftl_layout.h
Normal file
114
lib/ftl/ftl_layout.h
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
* Copyright (c) Intel Corporation.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FTL_LAYOUT_H
|
||||||
|
#define FTL_LAYOUT_H
|
||||||
|
|
||||||
|
#include "spdk/stdinc.h"
|
||||||
|
|
||||||
|
struct spdk_ftl_dev;
|
||||||
|
struct ftl_md;
|
||||||
|
|
||||||
|
enum ftl_layout_region_type {
|
||||||
|
/* User data region on the nv cache device */
|
||||||
|
FTL_LAYOUT_REGION_TYPE_DATA_NVC,
|
||||||
|
|
||||||
|
/* User data region on the base device */
|
||||||
|
FTL_LAYOUT_REGION_TYPE_DATA_BASE,
|
||||||
|
|
||||||
|
FTL_LAYOUT_REGION_TYPE_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* last nvc/base region in terms of lba address space */
|
||||||
|
#define FTL_LAYOUT_REGION_LAST_NVC FTL_LAYOUT_REGION_TYPE_DATA_NVC
|
||||||
|
#define FTL_LAYOUT_REGION_LAST_BASE FTL_LAYOUT_REGION_TYPE_DATA_BASE
|
||||||
|
#define FTL_LAYOUT_REGION_TYPE_FREE_BASE (UINT32_MAX - 2)
|
||||||
|
#define FTL_LAYOUT_REGION_TYPE_FREE_NVC (UINT32_MAX - 1)
|
||||||
|
#define FTL_LAYOUT_REGION_TYPE_INVALID (UINT32_MAX)
|
||||||
|
|
||||||
|
struct ftl_layout_region_descriptor {
|
||||||
|
/* Current version of the region */
|
||||||
|
uint64_t version;
|
||||||
|
|
||||||
|
/* Offset in FTL_BLOCK_SIZE unit where the region exists on the device */
|
||||||
|
uint64_t offset;
|
||||||
|
|
||||||
|
/* Number of blocks in FTL_BLOCK_SIZE unit */
|
||||||
|
uint64_t blocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Data or metadata region on devices */
|
||||||
|
struct ftl_layout_region {
|
||||||
|
/* Name of the region */
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
/* Region type */
|
||||||
|
enum ftl_layout_region_type type;
|
||||||
|
|
||||||
|
/* Mirror region type - a region may be mirrored for higher durability */
|
||||||
|
enum ftl_layout_region_type mirror_type;
|
||||||
|
|
||||||
|
/* Latest region version */
|
||||||
|
struct ftl_layout_region_descriptor current;
|
||||||
|
|
||||||
|
/* Previous region version, if found */
|
||||||
|
struct ftl_layout_region_descriptor prev;
|
||||||
|
|
||||||
|
/* Number of blocks in FTL_BLOCK_SIZE unit of a single entry.
|
||||||
|
* A metadata region may be subdivided into multiple smaller entries.
|
||||||
|
* Eg. there's one region describing all bands, but you may be able to access
|
||||||
|
* metadata of a single one.
|
||||||
|
*/
|
||||||
|
uint64_t entry_size;
|
||||||
|
|
||||||
|
/* Number of entries */
|
||||||
|
uint64_t num_entries;
|
||||||
|
|
||||||
|
/* VSS MD size or 0:disable VSS MD */
|
||||||
|
uint64_t vss_blksz;
|
||||||
|
|
||||||
|
/* Device of region */
|
||||||
|
struct spdk_bdev_desc *bdev_desc;
|
||||||
|
|
||||||
|
/* IO channel of region */
|
||||||
|
struct spdk_io_channel *ioch;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure describes the geometry (space organization) of FTL
|
||||||
|
*/
|
||||||
|
struct ftl_layout {
|
||||||
|
/* Organization for base device */
|
||||||
|
struct {
|
||||||
|
uint64_t total_blocks;
|
||||||
|
} base;
|
||||||
|
|
||||||
|
/* Organization for NV cache */
|
||||||
|
struct {
|
||||||
|
uint64_t total_blocks;
|
||||||
|
} nvc;
|
||||||
|
|
||||||
|
/* Information corresponding to L2P */
|
||||||
|
struct {
|
||||||
|
/* Address length in bits */
|
||||||
|
uint64_t addr_length;
|
||||||
|
/* Address size in bytes */
|
||||||
|
uint64_t addr_size;
|
||||||
|
/* Number of LBAS in memory page */
|
||||||
|
uint64_t lbas_in_page;
|
||||||
|
} l2p;
|
||||||
|
|
||||||
|
struct ftl_layout_region region[FTL_LAYOUT_REGION_TYPE_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setup FTL layout
|
||||||
|
*/
|
||||||
|
int ftl_layout_setup(struct spdk_ftl_dev *dev);
|
||||||
|
|
||||||
|
void ftl_layout_dump(struct spdk_ftl_dev *dev);
|
||||||
|
int ftl_validate_regions(struct spdk_ftl_dev *dev, struct ftl_layout *layout);
|
||||||
|
|
||||||
|
#endif /* FTL_LAYOUT_H */
|
23
lib/ftl/mngt/ftl_mngt_md.c
Normal file
23
lib/ftl/mngt/ftl_mngt_md.c
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/* 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_internal.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);
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,10 @@ static const struct ftl_mngt_process_desc desc_startup = {
|
|||||||
.action = ftl_mngt_open_cache_bdev,
|
.action = ftl_mngt_open_cache_bdev,
|
||||||
.cleanup = ftl_mngt_close_cache_bdev
|
.cleanup = ftl_mngt_close_cache_bdev
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "Initialize layout",
|
||||||
|
.action = ftl_mngt_init_layout
|
||||||
|
},
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,8 @@ void ftl_mngt_open_cache_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process
|
|||||||
|
|
||||||
void ftl_mngt_close_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);
|
||||||
|
|
||||||
|
void ftl_mngt_init_layout(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
|
||||||
|
|
||||||
void ftl_mngt_rollback_device(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
|
void ftl_mngt_rollback_device(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
|
||||||
|
|
||||||
#endif /* FTL_MNGT_STEPS_H */
|
#endif /* FTL_MNGT_STEPS_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user