typedef void (*ftl_md_cb)(struct spdk_ftl_dev *dev, struct ftl_md *md, int status);
enum ftl_md_ops {
FTL_MD_OP_RESTORE = 0,
FTL_MD_OP_PERSIST = 1,
FTL_MD_OP_CLEAR = 2
};
/* 用于 store/restore/recover 的元数据容器
* 与region一一对应,由ftl_md_create初始化
*/
struct ftl_md {
/* owner的上下文 (restore/persist/clear 操作的调用者 ) */
struct {
/* 元数据所有者的私有上下文 */
void *private;
/* owner的额外上下文 */
void *cb_ctx;
} owner;
/* 用于表明操作到结尾了的回调函数 */
ftl_md_cb cb;
struct spdk_ftl_dev *dev;
/* 设备上的哪个区域存储该元数据
* 在ftl_md_set_region中被初始化
*/
const struct ftl_layout_region *region;
/*
* 在ftl_md_create被初始化为和对应region的blocks相等的值
*/
uint64_t data_blocks;
/*
* 这两个指向buffer的区域,某些region需要buffer
* 会在内存中申请一个和region描述的空间等大小的buffer
* 目前所用到的region都没有申请buffer
* 1. 对于vss_data:
* 如果有buffer的话,
* 会在ftl_md_set_region中将vss_data指向的 data_blocks 个 block对应的vss都初始化
*/
void *data;
void *vss_data;
/* 单个entry 的 VSS 默认 DMA 缓冲区。由 ftl_md_persist_entry() 使用。
* 如果对应的region的entry_size不为0的话,这个会指向一块用spdk_malloc申请到的DMA内存
* 如果 region的entry_size不为0的话
* 会在ftl_md_set_region中将vss_data指向的 entry_size 个 entries 对应的vss都初始化
*/
void *entry_vss_dma_buf;
/* 执行 IO 所用的字段
* 由io_init初始化
* 会申请一块大小为4倍的dev->xfer_size作为io.data的指向
* 如果该 ftl_md 或者对应的 region 携带 vss 区域的话,
* 则要额外申请4倍的dev->xfer_size 块数量的FTL_MD_VSS_SZ大小的区域
* io.address = region.current.offset; 这个是将要写入/读取的设备地址
* io.remaining = region->current.blocks
* io.data_offset = 0;
* io.status = 0;
* io.op = FTL_MD_OP_PERSIST / FTL_MD_OP_RESTORE / FTL_MD_OP_CLEAR
* 在read_write_blocks_cb会更新上面的成员,每次读取/写入一比ftl_xsfer_size大小的数据
*/
struct {
void *data;
void *md;
uint64_t address;
uint64_t remaining;
uint64_t data_offset;
int status;
enum ftl_md_ops op;
struct spdk_bdev_io_wait_entry bdev_io_wait;
} io;
/*
* 在 ftl_md_set_region 中由setup_mirror初始化:
* 先为mirror申请内存空间
* 然后将mirror大部分的成员(dev, data_blocks, data, vss_data)设置为和当前这个ftl_md一样
* 并将mirror的region设置为当前这个ftl_md的region中的mirror_type所属的region
*/
struct ftl_md *mirror;
/* 在ftl_md_create中初始化,默认为true
* 判断一个md是否有mirror的时候,优先判断对应的region的mirror_type是否有效,再判断是否mirror_enabled
*/
bool mirror_enabled;
};
#define FTL_MD_VSS_SZ 64
union ftl_md_vss {
struct {
uint8_t unused[FTL_MD_VSS_SZ - sizeof(uint64_t)];
uint64_t md_version;
} version;
struct {
uint64_t start_lba;
uint64_t num_blocks;
} unmap;
struct {
uint64_t lba;
} nv_cache;
};
// 创建FTL元数据
struct ftl_md *ftl_md_create(struct spdk_ftl_dev *dev, uint64_t blocks,
uint64_t vss_blksz, const char *name, bool no_mem,
const struct ftl_layout_region *region);
// 设定设备上执行 persisting, restoring 或者 cleaning 的区域
int ftl_md_set_region(struct ftl_md *md,
const struct ftl_layout_region *region);
// 将所有的元数据 persists 到设定的区域上
// 首先会 persists mirror
// persists 的流程为:
// 1. io_init(md, FTL_MD_OP_PERSIST) 初始化这个md的io成员变量
// 2. io_submit(md):
// 对于FTL_MD_OP_PERSIST的操作,将md->data的数据memcpy到md->io.data中,一次最多进行ftl_md_xfer_blocks 个块大小
// 如果存在vss_data,则也将md->vss_data的数据拷贝到md->io.md
// 3. 调用read_write_blocks:将数据和元数据读/写到设备相应的地址上
// 4. 回调read_write_blocks_cb,将md.io的成员变量向前调整一段,然后继续执行io_submit(md)
void ftl_md_persist(struct ftl_md *md);
// 将给定的 entries 持久化到设定的区域上
void ftl_md_persist_entry(struct ftl_md *md, uint64_t start_entry, void *buffer, void *vss_buffer,
ftl_md_io_entry_cb cb, void *cb_arg,
struct ftl_md_io_entry_ctx *ctx);
// 从设定的区域上读取 entries
void ftl_md_read_entry(struct ftl_md *md, uint64_t start_entry, void *buffer, void *vss_buffer,
ftl_md_io_entry_cb cb, void *cb_arg, struct ftl_md_io_entry_ctx *ctx);