https://www.yuanmas.com/info/pYzLl3Wpzv.html
linux内核dm thin pool分析
一、简介
Docker现在非常火热,作为docker存储引擎之一的dm thin pool用的比较多,thin-pool由一个metadata设备和data设备组成,thin的概念就是按需分配数据块,删除时也回收数据块,提高存储空间利用率。
a) Dm thin pool创建命令如下:
//清零metadata设备的第一个page, why??因为第一个page是thin-pool的superblock, thin pool 内核模块里是通过判断superbock是否全0来决定是重新格式化一个thin-pool还是打开已有的thin-pool
dd if=/dev/zero of=$metadata_dev bs=4096 count=1
//创建pool, --table的参数,0为起始扇区,20971520是扇区数,这里的扇区都是512字节来算的,data_block_size — 数据块的大小,单位还是扇区,512字节,data_block_size最小为128,最大为2097152
low_water_mark —当空闲数据块个数小于water_mark时, dm thin-pool内核模块会给用户态daemon发送通知事件。
dmsetup create pool \
b) --table "0 20971520 thin-pool $metadata_dev $data_dev \
$data_block_size $low_water_mark"
//调用pool设备的message接口,创建thin设备,其中0是thin设备的id, 因为一个pool可以创建N个thin设备,所以需要一个id来区别开这些thin设备。
dmsetup message /dev/mapper/pool 0 “create_thin 0”
//为上面的thin设备创建/dev/dm-x 设备文件。
dmsetup create thin --table “0 2097152 thin /dev/mapper/pool 0”
二、thin-pool磁盘布局
a) data设备
data设备是以data_block_size,如果是128就是128*512=64KB 划分为块来管理的,data设备存储的全是数据,没有任何元数据,所以磁盘布局略。
b) Meta设备
c) 如图, meta设备的磁盘布局如上,meta设备的块大小为4KB, 第一个块是superblock, 代码里定义如下:
/*
Little endian on-disk superblock and device details.
*/
struct thin_disk_superblock {
__le32 csum; /* Checksum of superblock except for this field. */
__le32 flags;
__le64 blocknr; /* This block number, dm_block_t. */
__u8 uuid[16];
__le64 magic;
__le32 version;
__le32 time;
__le64 trans_id;
/*
Root held by userspace transactions.
*/
__le64 held_root;
__u8 data_space_map_root[SPACE_MAP_ROOT_SIZE];
__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
/*
2-level btree mapping (dev_id, (dev block, time)) -> data block
*/
__le64 data_mapping_root;
/*
Device detail root mapping dev_id -> device_details
*/
__le64 device_details_root;
__le32 data_block_size; /* In 512-byte sectors. */
__le32 metadata_block_size; /* In 512-byte sectors. */
__le64 metadata_nr_blocks;
__le32 compat_flags;
__le32 compat_ro_flags;
__le32 incompat_flags;
} __packed;
d) 重要的字段:
l Magic: pool设备的magic: 27022010
l Data_space_map_root: 管理数据块设备使用空间(使用位图)的map_root结构
l Metadata_space_map_root: 管理元数据块设备使用空间(使用位图)的map_root结构
l Data_mapping_root: thin设备从虚拟块地址到数据块设备上真实地址的映射btree根块号
l Device_details_root: 所有thin设备的信息以btree的方式存到Device_details_root指向的根块上
l Data_block_size: 数据设备块大小 = data_block_size * 512B
l Meta_nr_blocks: 元数据设备总块数, 注元数据设备块大小为4KB
三、寻址
从thin设备的虚拟块地址到数据设备的实际块地址,thin-pool的寻址逻辑如下:
a) 先用thin设备的dev_id作为key, 从data_mapping_root指向的块所包含的btree根节点中,去查找thin设备所对应的数据映射btree的块号
b) 再用虚拟块号作为key从thin设备的数据映射btree里去查找实际的value, 查到的value就是数据块设备上对应的实际块号
c) 整个过程用伪代码如下:
block_t find_thin_block(dev_t dev_id, block_t block)
{
block_t thin_map_root;
//从data_map_root btree中以dev_id为key查找对应的值,结果为dev_id对应的thin设备的具体btree根节点所在的块
thin_map_root = btree_lookup(pool_data_map_root, dev_id);
//从meta设备上读出根节点
read_block(meta_dev, thin_map_root, &thin_map_tree);
//从btree中以thin的虚拟块block查找data设备对应的实际块
return btree_lookup(&thin_map_tree, block);
}
四、空间管理
a) 整个thin-pool模块里,所有的空间管理都是基于btree的。
b) 元数据块设备自身的空间管理: 这个是通过以Metadata_space_map_root为根块的btree来管理的,叶子节点存放的是管理空间的位图,与普通位图不同的是thin-pool的位图是一个单元2个位,一共能表示4个状态: 0为空闲,1为引用计数为1, 2位引用计数为2, 3表示引用计数>2, 需要从另一颗btree里去找实际的引用计数。
c) 数据块设备空间管理: 跟元数据块设备自身空间管理类似,数据块设备也是一颗btree, 叶子节点指向包含管理位图的块,当然这些块都是在元数据设备上的。
d) 空间释放:当引用bitmap里的引用计数变为0时, 块就变空闲了,这也就是thin设备的意义所在,不过,这需要上层文件系统discard的支持,thin-pool需要能知道哪些块上层不需要了,就可以减小位图的引用计数,释放块。
五、总结
thin-pool的存储格式还是比较清晰和简单的,总体看上面的布局结构图就已经很清晰了