块IO子系统层级关系
块IO子系统的一般IO处理流程:虚拟文件系统层调用通用块层提供的接口(submit_bio)向块IO子系统提交IO请求,这些请求在IO调度层经过合并/排序等操作,转换成I/O请求派发到具体的块设备等待队列,由具体的块设备驱动去处理。
上述操作包括了两种IO请求:一种是通用块层的I/O请求,即bio;另一种是块设备驱动层的I/O请求,以request描述。
一、主要数据结构
第一个重要的数据结构就是struct bio,上一章介绍bdi回写的时候块设备bdi回写实现_叶落随风亦随雨的博客-CSDN博客
提到从回写最后调用do_writepages,再往后就是具体的文件系统内建立bio并调用submit_bio传递到通用块层,其中bio就是一个关键。
结构体定义如下:
struct bio {
struct bio *bi_next; /* request queue link */
struct gendisk *bi_disk;
unsigned int bi_opf; /* bottom bits req flags,
* top bits REQ_OP. Use
* accessors.
*/
unsigned short bi_flags; /* status, etc and bvec pool number */
unsigned short bi_ioprio;
unsigned short bi_write_hint;
blk_status_t bi_status;
u8 bi_partno;
atomic_t __bi_remaining;
struct bvec_iter bi_iter;
bio_end_io_t *bi_end_io;
void *bi_private;
#ifdef CONFIG_BLK_CGROUP
/*
* Represents the association of the css and request_queue for the bio.
* If a bio goes direct to device, it will not have a blkg as it will
* not have a request_queue associated with it. The reference is put
* on release of the bio.
*/
struct blkcg_gq *bi_blkg;
struct bio_issue bi_issue;
#ifdef CONFIG_BLK_CGROUP_IOCOST
u64 bi_iocost_cost;
#endif
#endif
#ifdef CONFIG_BLK_INLINE_ENCRYPTION
struct bio_crypt_ctx *bi_crypt_context;
#endif
union {
#if defined(CONFIG_BLK_DEV_INTEGRITY)
struct bio_integrity_payload *bi_integrity; /* data integrity */
#endif
};
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 */
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[];
};
其中gendisk指向具体的块设备;
bi_io_vec、bi_vcnt和__bi_cnt主要用来管理这一次IO操作涉及的内存数组,每个bio_vec结构都是一个形式为<page, offset, len>的向量,它描述的是一个特定的段:段所在的物理页、块在物理页中的偏移量、从给定偏移量开始的块长度。整个bio_io_vec结构体数组表示了一个完整的缓冲区。
/**
* struct bio_vec - a contiguous range of physical memory addresses
* @bv_page: First page associated with the address range.
* @bv_len: Number of bytes in the address range.
* @bv_offset: Start of the address range relative to the start of @bv_page.
*
* The following holds for a bvec if n * PAGE_SIZE < bv_offset + bv_len:
*
* nth_page(@bv_page, n) == @bv_page + n
*
* This holds because page_is_mergeable() checks the above property.
*/
struct bio_vec {
struct page *bv_page;
unsigned int bv_len;
unsigned int bv_offset;
};
bio_vec 则是组成 bio 数据的最小单位,他包含了一块数据所在的页,这块数据所在的页内偏移以及长度,通过这些信息就可以很清晰的描述数据具体位于内存的什么位置。
上述描述的是bio数据所在的内存位置,通过bio_vec数组可以看出一个bio的数据可以用多个不连续的内存段连接一起表示,那么这些数据对应的块设备地址范围是如何表示的?是通过另外一个结构体bi_iter表示的,它所表示的块设备地址范围则是一个连续的空间。
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 */
};
他们之间的关系如下图:
二、bio请求的生成和提交
主要介绍在文件系统层如何生成bio结构体,并调用submit_bio向通用块层提交。