enum ftl_layout_region_type {
#ifdef SPDK_FTL_VSS_EMU
/** 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,
/* If using cached L2P, this region stores the serialized instance of it */
FTL_LAYOUT_REGION_TYPE_L2P,
/* State of chunks */
FTL_LAYOUT_REGION_TYPE_NVC_MD,
/* Mirrored instance of the state of chunks */
FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR,
/* nv_cache设备上的用户数据区域 */
FTL_LAYOUT_REGION_TYPE_DATA_NVC,
/* base设备上的用户数据区域 */
FTL_LAYOUT_REGION_TYPE_DATA_BASE,
FTL_LAYOUT_REGION_TYPE_MAX,
};
#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 {
uint64_t version;
/* 以FTL_BLOCK_SIZE为粒度的,该区域处于设备上的位置 */
uint64_t offset;
/* FTL_BLOCK_SIZE粒度的blocks的数量 */
uint64_t blocks;
};
/* 描述设备上数据或者元数据区域的抽象 */
struct ftl_layout_region {
const char *name;
enum ftl_layout_region_type type;
/* 可以对某个区域进行镜像以获得更高的耐用性 */
// 在ftl_layout_setup中被初始化为FTL_LAYOUT_REGION_TYPE_INVALID
enum ftl_layout_region_type mirror_type;
/* 最新/上一个区域的desc */
struct ftl_layout_region_descriptor current;
struct ftl_layout_region_descriptor prev;
/* 表示一个entry中,以FTL_BLOCK_SIZE为粒度的块数量
* 一个元数据区域可能会被划分成多个更小的entries
* 例如:有一个区域用来描述所有的bands,但是你可能需要能够访问其中一个的元数据
*/
uint64_t entry_size;
uint64_t num_entries;
/* VSS MD 大小,为0则表示不启用VSS */
uint64_t vss_blksz;
/* 区域所属的设备 */
struct spdk_bdev_desc *bdev_desc;
/* 区域所属的channel */
struct spdk_io_channel *ioch;
};
// 这个结构体描述了整个FTL的空间组织
struct ftl_layout {
struct {
// 在ftl_layout_setup中初始化,直接调用spdk的接口获取base设备的块数
uint64_t total_blocks;
// 在 setup_layout_nvc 中初始化,为 1GB 的 blocks 数量
uint64_t chunk_data_blocks;
// 在 setup_layout_nvc 中初始化,chunk元数据的大小,强行对齐成一个 FTL_BLOCK_SIZE
uint64_t chunk_meta_size;
// 在 setup_layout_nvc 中初始化,包含 1GiB 的数据和 2 份 nvc_chunk_md 算作一个 chunk,计算有多少个chunk
uint64_t chunk_count;
// 在 setup_layout_nvc 中初始化,chunk 尾部要 1GiB data blocks的l2p,计算所有chunk_data_blocks的l2p使用的blocks
uint64_t chunk_tail_md_num_blocks;
} nvc;
struct {
// 在ftl_layout_setup中初始化,直接调用spdk的接口获取nvc设备的块数
uint64_t total_blocks;
} base;
/* L2P表的信息
* 在ftl_layout_setup中被初始化
*/
struct {
/* bits粒度的地址长度 */
// 计算是base的total_blocks+nvc的total_blocks取对数
uint64_t addr_length;
/* bytes粒度的地址大小 */
// 计算是根据addr_length的大小来判断:> 32 ? 8 : 4
uint64_t addr_size;
/* 在一个内存页中的LBAs的数量 */
// 计算是FTL_BLOCK_SIZE / layout->l2p.addr_size
uint64_t lbas_in_page;
} l2p;
/*
* region[FTL_LAYOUT_REGION_TYPE_DATA_NVC] 和
* region[FTL_LAYOUT_REGION_TYPE_NVC_MD] 和 region[FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR]
* 在set_layout_nvc和set_region_bdev_nvc中被初始化
* region[FTL_LAYOUT_REGION_TYPE_SB] 在 ftl_layout_setup_superblock中被初始化,放在cache设备的最前面
* 在set_layout_nvc中会跳过superblock这部分
* current.version和prev.version被设为0,current.offset被设为0,current.blocks被设为layout->nvc.total_blocks
* bdev_desc 被设为 dev->cache_bdev_desc, ioch 被设为 dev->cache_ioch, vss_blksz被设为cache_md_size
*
* region[FTL_LAYOUT_REGION_TYPE_SB] 在 ftl_mngt_superblock_init 中被初始化
* region[FTL_LAYOUT_REGION_TYPE_SB] 的 mirror type 是 FTL_LAYOUT_REGION_TYPE_SB_BASE
* region[FTL_LAYOUT_REGION_TYPE_SB_BASE] 的 mirror type 是 FTL_LAYOUT_REGION_TYPE_MAX
* super block的size是 128 KiB,初始化的时候全部在current上,super block是放在cache的最开头
* super block mirror是放在base的最末尾
*
* region[FTL_LAYOUT_REGION_TYPE_L2P] 在 set_layout_nvc 中被初始化
* 其所用的 blocks 计算为 layout->l2p.addr_size * dev->num_lbas
*
* region[FTL_LAYOUT_REGION_TYPE_DATA_BASE]在set_layout_base和set_region_bdev_btm中被初始化
* 初始化的方式和nv_cache类似, vss_blksz被设为0
*/
struct ftl_layout_region region[FTL_LAYOUT_REGION_TYPE_MAX];
/*
* 对应区域的元数据对象
* FTL_LAYOUT_REGION_TYPE_NVC 和 FTL_LAYOUT_REGION_TYPE_BASE 和 FTL_LAYOUT_REGION_L2P
* 在 ftl_mngt_init_md 中由 ftl_md_create 初始化
* FTL_LAYOUT_REGION_TYPE_SB 在 ftl_mngt_superblock_init 中被初始化
*
*/
struct ftl_md *md[FTL_LAYOUT_REGION_TYPE_MAX];
};
int ftl_layout_setup(struct spdk_ftl_dev *dev);
void ftl_layout_dump(struct spdk_ftl_dev *dev);
// 要保证两个region互相不会覆盖
int ftl_validate_regions(struct spdk_ftl_dev *dev, struct ftl_layout *layout);