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的存储格式还是比较清晰和简单的,总体看上面的布局结构图就已经很清晰了

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐