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);