diff --git a/lib/ftl/Makefile b/lib/ftl/Makefile index af71bdf97..478b5f561 100644 --- a/lib/ftl/Makefile +++ b/lib/ftl/Makefile @@ -17,8 +17,9 @@ CFLAGS += -I. 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_md.c C_SRCS += utils/ftl_conf.c SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_ftl.map) diff --git a/lib/ftl/ftl_core.h b/lib/ftl/ftl_core.h index 1fc6da0e2..964cb34bf 100644 --- a/lib/ftl/ftl_core.h +++ b/lib/ftl/ftl_core.h @@ -17,12 +17,16 @@ #include "spdk/bdev_zone.h" #include "ftl_internal.h" +#include "ftl_layout.h" #include "utils/ftl_log.h" struct spdk_ftl_dev { /* Configuration */ struct spdk_ftl_conf conf; + /* FTL device layout */ + struct ftl_layout layout; + /* Underlying device */ struct spdk_bdev_desc *base_bdev_desc; diff --git a/lib/ftl/ftl_layout.c b/lib/ftl/ftl_layout.c new file mode 100644 index 000000000..d152790a4 --- /dev/null +++ b/lib/ftl/ftl_layout.c @@ -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]); + } + } +} diff --git a/lib/ftl/ftl_layout.h b/lib/ftl/ftl_layout.h new file mode 100644 index 000000000..9e14ff0af --- /dev/null +++ b/lib/ftl/ftl_layout.h @@ -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 */ diff --git a/lib/ftl/mngt/ftl_mngt_md.c b/lib/ftl/mngt/ftl_mngt_md.c new file mode 100644 index 000000000..e2697a318 --- /dev/null +++ b/lib/ftl/mngt/ftl_mngt_md.c @@ -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); + } +} diff --git a/lib/ftl/mngt/ftl_mngt_startup.c b/lib/ftl/mngt/ftl_mngt_startup.c index c471cbd88..8c1119d54 100644 --- a/lib/ftl/mngt/ftl_mngt_startup.c +++ b/lib/ftl/mngt/ftl_mngt_startup.c @@ -22,6 +22,10 @@ static const struct ftl_mngt_process_desc desc_startup = { .action = ftl_mngt_open_cache_bdev, .cleanup = ftl_mngt_close_cache_bdev }, + { + .name = "Initialize layout", + .action = ftl_mngt_init_layout + }, {} } }; diff --git a/lib/ftl/mngt/ftl_mngt_steps.h b/lib/ftl/mngt/ftl_mngt_steps.h index 8bda2069f..eade7e270 100644 --- a/lib/ftl/mngt/ftl_mngt_steps.h +++ b/lib/ftl/mngt/ftl_mngt_steps.h @@ -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_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); #endif /* FTL_MNGT_STEPS_H */