Linux块设备层已逐步切换到multiqueue , Linux5.0以后单队列代码已被完全移除。multiqueue核心思路是为每个CPU分配一个软件队列,为存储设备的每个硬件队列分配一个硬件派发队列,再将软件队列与硬件派发队列做绑定,减少了IO请求过程中对锁的竞争,从而提高IO性能。网上已有很多针对block multiqueue架构的文章,推荐这篇《Linux Block IO: Introducing Multi-queue SSD Access on Multi-core Systems》。这一系列主要梳理一下block multiqueue的实现,基于Linux5.x。
block mltiqueue 架构,图片引用自《Linux Block IO: Introducing Multi-queue SSD Access on Multi-core Systems》
核心数据结构
bio
block层的IO最小单元,负责在block层以及其上下层之间传递数据。bio数据结构中除了包含IO的一些基础信息,操作类型(读/写/discard...),作用的磁盘/分区,IO优先级,状态等等。bio最重要的信息是描述了磁盘上一段连续数据与内存页的映射关系,主要由bi_vcnt,bi_io_vec,bi_iter 描述。
struct bio {
struct bio *bi_next; /* request queue link */ //一个request有多个bio时,以单向链表组织bio
struct gendisk *bi_disk;
...
struct bvec_iter bi_iter; //bvec迭代器
...
unsigned short bi_vcnt; /* how many bio_vec's */
/*
* Everything starting with bi_max_vecs will be preserved by bio_reset()
*/
unsigned short bi_max_vecs; /* max bvl_vecs we can hold */
atomic_t __bi_cnt; /* pin count */ //bio 引用计数
struct bio_vec *bi_io_vec; /* the actual vec list */
struct bio_set *bi_pool;
/*
* We can inline a number of vecs at the end of the bio, to avoid
* double allocations for a small number of bio_vecs. This member
* MUST obviously be kept at the very end of the bio.
*/
struct bio_vec bi_inline_vecs[0];
};
struct bio_vec :是描述磁盘数据与内存页映射的最小单元,每个bio_vec以offset & size形式描述一个内存页中的一段数据。每个bio 通常有多个bio_vec,bio_vec以数组的形式存储,*bi_io_vec 指向数组头,bi_vcnt记录了其个数。bi_inline_vecs是为每个bio预分配的bio_vec数组,包括4个bio_vec,若bio需要分配的bio_vec小于4可直接使用bi_inline_vecs,不需要再额外申请内存,此时bi_io_vec会指向bi_inline_vecs。
struct bio_vec {
struct page *bv_page;
unsigned int bv_len;
unsigned i