Linux kernel之Block IO系统
1.背景
1.1 block device 处理流程
image.png-
VFS
VFS 将调用用户系统调用API read() 处理转换成对应的内核系统调用服务程序,并将对应的read 操作重定向到具体的文件系统的操作实现。 -
FS
本质上,文件系统最小访问单位为block, 对于文件系统来说,文件由一个个block 构成,文件系统通过file block number定位数据在文件中的位置(相对于文件的起始位置)。而磁盘同样有一个个固定大小的block 构成,文件系统通过logical block number(相对于磁盘开始位置)定位磁盘的位置。
1)FS根据文件系统最小可访问单位block size,确定访问的数据的内容的 file block number(相对于文件起始位置);
2)根据1)要访问的数据的file block number,调用具体文件系统的函数,确定要访问的数据映射在磁盘中的位置logical block number(相对于磁盘); -
Block layer
block layer 为所有类型的块设备建立统一的模型,抽象底层HW 的细节,为块设备提供一个通用的视角。block layer 接收上层(文件系统)对块设备的磁盘操作请求,最终发送IO 情况 -
device driver
通过发送对应的command 给HW 以驱动实际数据的传输。
1.2 block layer
- block layer 可划分为两层:bio layer , request layer
image.png
2. block layer 工作原理
2.0 IO处理
i. 数据组织数据结构:bio/bio_vec
ii. bio 的处理
(1)bio 的 split 和 merge;
(2)bio 的bounce;
(3)bio 包装成 request;
iii. request 的处理
(1)request 的分配和获取;
(2)request 的 plug/unplug;
(3)request 的下发;
2.1 核心数据结构 bio/bio_vec
bio :
i. main unit of I/O for the block layer and lower layers (ie drivers and stacking drivers)(官方解释)
ii. bio结构体用于表示IO 请求(1)数据在内存和磁盘的位置, (2)数据大小以及(3)IO完成情况。
iii. bio 中重要的数据结构:bio_vec和bi_iter
bio_vec:描述了IO数据在内存中的组织
bi_iter: 描述了IO数据在磁盘中位置以及当前IO数据的完成情况
struct bio_vec {
struct page *bv_page;
unsigned int bv_len;
unsigned int bv_offset;
};
struct bvec_iter {
sector_t bi_sector; /* device address in 512 byte
sectors */
unsigned int bi_size; /* residual I/O count */
unsigned int bi_idx; /* current index into bvl_vec */
unsigned int bi_bvec_done; /* number of bytes completed in
current bvec */
};
iiii. bio 和request 之间的关系
request:可由在硬盘位置连续的bio链接起来的
2.2 bio 的remap
i. 特定分区的bio 的bi_sector 会重映射remap 到 整个磁盘设备的 IO偏移;
ii. 另外DM 设备 mapped device 的bio 根据其映射规则需要remap 到target device的 bio.
/*
* Remap block n of partition p to block n+start(p) of the disk.
*/
static int blk_partition_remap(struct bio *bio)
{
struct block_device *p = bio->bi_bdev;
if (unlikely(should_fail_request(p, bio->bi_iter.bi_size)))
return -EIO;
if (bio_sectors(bio)) {
bio->bi_iter.bi_sector += p->bd_start_sect;
trace_block_bio_remap(bio, p->bd_dev,
bio->bi_iter.bi_sector -
p->bd_start_sect);
}
bio_set_flag(bio, BIO_REMAPPED);
return 0;
}
2.3 bio 的 split 和 merge
2.3.1 基本概念
bio的切分:将一个bio分成两个bio;
bio的合并:将bio与IO请求request进行合并。
2.3.2 切分的依据
(1)q->limits.max_segments
表示请求队列request-queue中每个IO请求request的最大segment数目
(2)q->limits.max_segment_size
表示请求队列request-queue中每个segment最大的数据大小
(3)q->limits.max_sectors
表示请求队列支持一次传输request的最大扇区数目即IO请求最大size。
2.3.3 拆分图示
拆分后.png
2.3.4 合并类型
后向合并.png
2.3.5 合并过程-两种机制的合并
(1)与plug/unplug 机制的plug->mq_list中的request进行合并。
(2)与 IO调度器机制的schedule list (定义IO调度器)或block mq的软件queue机制ctx->rq_lists(没有定义IO调度器)上request进行合并。
2.4 bio 包装成 request
经过bio split 和bio merge, bio 还在的话,会将bio 封装到request 中,后续操作的对象由bio 转向 request。
2.5 request 的分配和获取
2.5.1 request 的分配
i. request 分配: 在初始化阶段就分配好
ii. request 申请: runtime 时通过申请一个空闲的tag获取一个空闲的request 。
iii. tag 和 request 一一对应。
xi. tag 管理:通过sbitmap_queue 管理
2.5.2 tag 管理-sbitmap_queue
sbitmap_queue 在bitmap 基础之上增加
(1)bitmap分组管理的功能(sbitmap);
(2)等待功能,即在没有无空闲bit时,会让队列一直等待
2.5.3 request 分配
i. 初始化阶段,根据硬件的queue 数量和队列深度,分配nr_hw_queues 个hctx, 每个hctx 对应一套blk_mq_tags,
ii. 每个HCTX,存在total_tags和reserved_tags两个sbitmap,分别用于分配和释放tags以及reserved_tags及与之对应的request。
iii. static_rq指向提前分配的request(包括(nvme/scsi)command以及底层驱动的私有结构)。提前分配的静态request数目为nr_hw_queues * queue_depth。
2.5.4 request 获取
先申请空闲的tag,若没有空闲的tag,则通过等待机制等空闲的tag, 然后找到对应的request,
2.6 request 的 plug/unplug
i. plug/unplug机制,通过request的延迟下发,为后续的IO增加合并和排序操作的可能,以减少request 数量。类似于蓄流和泄流。
ii. 工作流程:在开启机制后,request 会放置到plug list中(plug过程),当达到某个条件时会将request 统一下发(unplug过程)。
iii. plug的时机
plug 通过block_start_plug()开启的。对于每个线程描述符task_struct,存在成员blk_plug,若为空表示没有使能PLUG,否则表示已使能PLUG。函数blk_start_plug()进行成员初始化,同时给task_struct->plug赋值。
在开启BLOCK PLUG后,可以将通过函数blk_add_rq_to_plug()将IO请求加入到plug->mq_list中,且判断所包含的IO请求是否来自多个队列(通过成员multiple_queues)。
xi. unplug的时机
unplug的时机有三种:
(1)所积攒的IO数目达到BLK_MAX_REQUEST_COUNT(16)
(2)或遇到的IO大小超过BLK_PLUG_FLUSH_SIZE (128K)时;
(3)使用blk_finish_plug()主动冲刷;
2.7 IO 的下发路径
IO 下发处理路径
路径一:使能了plug/unplug机制,此时会等待plug池中存取足够的IO后统一往调度器插入IO,并选取IO下发;
路径二:没有使能plug/unplug机制,此时会将IO插入调度器中,并选取IO下发;
路径三:跳过调度层,直接下发IO;
参考block layer 系列文章:https://blog.csdn.net/flyingnosky/article/details/121341392