linux dm
最近想学习Linux IO子系统, 找了flashcache代码, 它通过内核提供的Device Mapper机制, 将一块SSD和一块普通磁盘虚拟为一个块设备, 其中SSD作为cache, 数据最终落地到普通磁盘. 这种混合存储的策略, 向上层应用(如mysql)屏蔽了底层的实现, 上层应用看到的只是一个挂载到虚拟块设备上的某种文件系统, 使用常见的文件系统接口即可读写数据, 一方面保持兼容, 一方面获得不错的性能. flashcache的代码只有几千行, 从commit log中可以看到版本迭代比较频繁, 也因此引入了较多我个人不关心的新特性. flashcache源码中作者写到借鉴了dm-cache的代码, 所以查了下资料, 竟是国人出品, sloc不足两千, 一晚上就可以看完, 正合胃口. dm-cache的使用可以参考flashcache文档, 原理见flashcache原理.
内存结构
dm-cache思路非常简单, 它把SSD作为cache, 将数据持久化到普通磁盘. 其中, SSD cache组织方式为set-associative map, 这和CPU cache的组织非常相像, 只是这里的key是cacheblock编号. cacheblock是dm-cache为了方便存取数据引入的单位, 粒度在磁盘block之上. 在通过dmsetup创建dm-cache块设备时时可以指定cacheblock的大小, 默认为8个连续的磁盘block组成一个cacheblock, 即4k字节. 上层的IO请求由Device Mapper框架切割为cacheblock大小(且对齐)的bio, 然后交由dm-cache处理. 也就是说, 不管是对SSD, 还是普通磁盘, dm-cache处理IO的单位都是cacheblock. 它在内存中的metadata为:
117/*Cache block metadata structure*/
118struct cacheblock {
119 spinlock_t lock; /*Lock to protect operations on the bio list*/
120 sector_t block; /*Sector number of the cached block*/
121 unsigned short state; /*State of a block*/
122 unsigned long counter; /*Logical timestamp of the block’s last access*/
123 struct bio_list bios; /*List of pending bios*/
124};
其中, block字段表示当前cacheblock的起始扇区编号. 既然SSD作为cache, 针对写请求必定会有writeback和writethrough等多种选择. writeback即数据先写到SSD, 然后由后台线程在合适的时间写回磁盘. writethrough指数据同时写入磁盘和SSD. (flashcache在这基础上又增加了writearound的方式, 意思是绕过SSD cache, 数据直接写入磁盘, 在处理读请求时更新到SSD.) 不管是writeback, 还是writethrough, 数据写入磁盘(或者由磁盘读取数据更新至cache)都不可能一蹴而就, 所以每个cacheblock必定会有一个状态(state字段). 另外, cache有淘汰的概念, dm-cache支持FIFO或LRU淘汰, 所以需要为每个cacheblock保存其最后访问时间(counter字段). 最后, 为了互斥同时请求同一个cacheblock, 每个cacheblock还对应一个spinlock. 被互斥的后发请求记录在bios链表中. 在当前cacheblock上的操作完成后, dm-cache将重新提交bios链表上的bio.
接下来看下dm-cache的总控结构体cache_c:
80/*
81* Cache context
82*/
83struct cache_c {
84 struct dm_dev *src_dev; /*Source device*/
85 struct dm_dev *cache_dev; /*Cache device*/
86 struct dm_kcopyd_client *kcp_client; /*Kcopyd client for writing back data*/
87
88 struct cacheblock *cache; /*Hash table for cache blocks*/
89 sector_t size; /*Cache size*/
90 unsigned int bits; /*Cache size in bits*/
91 unsigned int assoc; /*Cache associativity*/
92 unsigned int block_size; /*Cache block size*/
93 unsigned int block_shift; /*Cache block size in bits*/
94 unsigned int block_mask; /*Cache block mask*/
95 unsigned int consecutive_shift; /*Consecutive blocks size in bits*/
96 unsigned long counter; /*Logical timestamp of last access*/
97 unsigned int write_policy; /*Cache write policy*/
98 sector_t dirty_blocks; /*Number of dirty blocks*/
99
100 spinlock_t lock; /*Lock to protect page allocation/deallocation*/
101 struct page_list *pages; /*Pages for I/O*/
102 unsigned int nr_pages; /*Number of pages*/
103 unsigned int nr_free_pages; /*Number of free pages*/
104 wait_queue_head_t destroyq; /*Wait queue for I/O completion*/
105 atomic_t nr_jobs; /*Number of I/O jobs*/
106 struct dm_io_client *io_client; /*Client memory pool*/
107
108 /*Stats*/
109 unsigned long reads; /*Number of reads*/
110 unsigned long writes; /*Number of writes*/
111 unsigned long cache_hits; /*Number of cache hits*/
112 unsigned long replace; /*Number of cache replacements*/
113 unsigned long writeback; /*Number of replaced dirty blocks*/
114 unsigned long dirty; /*Number of submitted dirty blocks*/
115};
其中, src_dev和cache_dev分别为磁盘和SSD在DM框架的抽象. cache字段为连续的cacheblock数组, 元素个数即size字段. 其余字段顾名思义, 不再赘述.
初始化
dm-cache的初始化代码相对简单, DM框架获取dmsetup参数, 传递给cache_ctr(), dm-cache通过该函数构造一个cache_c对象, 保存在dm_target.private中. dm_target结构中另一重要字段为split_io, 这个字段表示DM框架分割bio的粒度, cache_ctr()函数指定其为cacheblock大小.
上层的读写请求在IO内核路径上表示为bio, 针对Device Mapper框架虚拟出来的块设备的bio请求, DM框架通过bio的block编号找到所属的dm_targets(一个bio的请求可能横跨多个dm_target), 逐个回调dm_target.type->map, 该字段为函数指针, 在dm-cache模块加载到内核时, 由该模块的初始化函数dm_cache_init()注册为cache_map(). 也就是说, 读写请求的入口都是cache_map().
请求处理
如上所述, 读写请求的入口都是cache_map(), 其实现如下:
1202/*
1203* Decide the mapping and perform necessary cache operations for a bio request.
1204*/
1205static int cache_map(struct dm_target *ti, struct bio *bio,
1206 union map_info *map_context)
1207{
1208 struct cache_c *dmc = (struct cache_c *) ti->private;
<
发布评论