1.背景
1.1 block device 處理流程
-
VFS
VFS 將調(diào)用用戶系統(tǒng)調(diào)用API read() 處理轉(zhuǎn)換成對(duì)應(yīng)的內(nèi)核系統(tǒng)調(diào)用服務(wù)程序陡蝇,并將對(duì)應(yīng)的read 操作重定向到具體的文件系統(tǒng)的操作實(shí)現(xiàn)比驻。 -
FS
本質(zhì)上棋蚌,文件系統(tǒng)最小訪問單位為block, 對(duì)于文件系統(tǒng)來說,文件由一個(gè)個(gè)block 構(gòu)成璃诀,文件系統(tǒng)通過file block number定位數(shù)據(jù)在文件中的位置(相對(duì)于文件的起始位置)弧可。而磁盤同樣有一個(gè)個(gè)固定大小的block 構(gòu)成,文件系統(tǒng)通過logical block number(相對(duì)于磁盤開始位置)定位磁盤的位置劣欢。
1)FS根據(jù)文件系統(tǒng)最小可訪問單位block size棕诵,確定訪問的數(shù)據(jù)的內(nèi)容的 file block number(相對(duì)于文件起始位置);
2)根據(jù)1)要訪問的數(shù)據(jù)的file block number凿将,調(diào)用具體文件系統(tǒng)的函數(shù)校套,確定要訪問的數(shù)據(jù)映射在磁盤中的位置logical block number(相對(duì)于磁盤); -
Block layer
block layer 為所有類型的塊設(shè)備建立統(tǒng)一的模型,抽象底層HW 的細(xì)節(jié)牧抵,為塊設(shè)備提供一個(gè)通用的視角笛匙。block layer 接收上層(文件系統(tǒng))對(duì)塊設(shè)備的磁盤操作請(qǐng)求,最終發(fā)送IO 情況 -
device driver
通過發(fā)送對(duì)應(yīng)的command 給HW 以驅(qū)動(dòng)實(shí)際數(shù)據(jù)的傳輸犀变。
1.2 block layer
- block layer 可劃分為兩層:bio layer , request layer
image.png
2. block layer 工作原理
2.0 IO處理
i. 數(shù)據(jù)組織數(shù)據(jù)結(jié)構(gòu):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 的下發(fā)机隙;
2.1 核心數(shù)據(jù)結(jié)構(gòu) bio/bio_vec
bio :
i. main unit of I/O for the block layer and lower layers (ie drivers and stacking drivers)(官方解釋)
ii. bio結(jié)構(gòu)體用于表示IO 請(qǐng)求(1)數(shù)據(jù)在內(nèi)存和磁盤的位置, (2)數(shù)據(jù)大小以及(3)IO完成情況。
iii. bio 中重要的數(shù)據(jù)結(jié)構(gòu):bio_vec和bi_iter
bio_vec:描述了IO數(shù)據(jù)在內(nèi)存中的組織
bi_iter: 描述了IO數(shù)據(jù)在磁盤中位置以及當(dāng)前IO數(shù)據(jù)的完成情況
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 之間的關(guān)系
request:可由在硬盤位置連續(xù)的bio鏈接起來的
2.2 bio 的remap
i. 特定分區(qū)的bio 的bi_sector 會(huì)重映射remap 到 整個(gè)磁盤設(shè)備的 IO偏移;
ii. 另外DM 設(shè)備 mapped device 的bio 根據(jù)其映射規(guī)則需要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的切分:將一個(gè)bio分成兩個(gè)bio萨西;
bio的合并:將bio與IO請(qǐng)求request進(jìn)行合并。
2.3.2 切分的依據(jù)
(1)q->limits.max_segments
表示請(qǐng)求隊(duì)列request-queue中每個(gè)IO請(qǐng)求request的最大segment數(shù)目
(2)q->limits.max_segment_size
表示請(qǐng)求隊(duì)列request-queue中每個(gè)segment最大的數(shù)據(jù)大小
(3)q->limits.max_sectors
表示請(qǐng)求隊(duì)列支持一次傳輸request的最大扇區(qū)數(shù)目即IO請(qǐng)求最大size旭旭。
2.3.3 拆分圖示
2.3.4 合并類型
2.3.5 合并過程-兩種機(jī)制的合并
(1)與plug/unplug 機(jī)制的plug->mq_list中的request進(jìn)行合并谎脯。
(2)與 IO調(diào)度器機(jī)制的schedule list (定義IO調(diào)度器)或block mq的軟件queue機(jī)制ctx->rq_lists(沒有定義IO調(diào)度器)上request進(jìn)行合并。
2.4 bio 包裝成 request
經(jīng)過bio split 和bio merge持寄, bio 還在的話源梭,會(huì)將bio 封裝到request 中娱俺,后續(xù)操作的對(duì)象由bio 轉(zhuǎn)向 request。
2.5 request 的分配和獲取
2.5.1 request 的分配
i. request 分配: 在初始化階段就分配好
ii. request 申請(qǐng): runtime 時(shí)通過申請(qǐng)一個(gè)空閑的tag獲取一個(gè)空閑的request 废麻。
iii. tag 和 request 一一對(duì)應(yīng)荠卷。
xi. tag 管理:通過sbitmap_queue 管理
2.5.2 tag 管理-sbitmap_queue
sbitmap_queue 在bitmap 基礎(chǔ)之上增加
(1)bitmap分組管理的功能(sbitmap);
(2)等待功能烛愧,即在沒有無空閑bit時(shí)油宜,會(huì)讓隊(duì)列一直等待
2.5.3 request 分配
i. 初始化階段,根據(jù)硬件的queue 數(shù)量和隊(duì)列深度怜姿,分配nr_hw_queues 個(gè)hctx, 每個(gè)hctx 對(duì)應(yīng)一套blk_mq_tags,
ii. 每個(gè)HCTX慎冤,存在total_tags和reserved_tags兩個(gè)sbitmap,分別用于分配和釋放tags以及reserved_tags及與之對(duì)應(yīng)的request沧卢。
iii. static_rq指向提前分配的request(包括(nvme/scsi)command以及底層驅(qū)動(dòng)的私有結(jié)構(gòu))蚁堤。提前分配的靜態(tài)request數(shù)目為nr_hw_queues * queue_depth。
2.5.4 request 獲取
先申請(qǐng)空閑的tag,若沒有空閑的tag,則通過等待機(jī)制等空閑的tag, 然后找到對(duì)應(yīng)的request,
2.6 request 的 plug/unplug
i. plug/unplug機(jī)制但狭,通過request的延遲下發(fā)披诗,為后續(xù)的IO增加合并和排序操作的可能,以減少request 數(shù)量立磁。類似于蓄流和泄流呈队。
ii. 工作流程:在開啟機(jī)制后,request 會(huì)放置到plug list中(plug過程)息罗,當(dāng)達(dá)到某個(gè)條件時(shí)會(huì)將request 統(tǒng)一下發(fā)(unplug過程)掂咒。
iii. plug的時(shí)機(jī)
plug 通過block_start_plug()開啟的。對(duì)于每個(gè)線程描述符task_struct迈喉,存在成員blk_plug绍刮,若為空表示沒有使能PLUG,否則表示已使能PLUG挨摸。函數(shù)blk_start_plug()進(jìn)行成員初始化孩革,同時(shí)給task_struct->plug賦值。
在開啟BLOCK PLUG后得运,可以將通過函數(shù)blk_add_rq_to_plug()將IO請(qǐng)求加入到plug->mq_list中膝蜈,且判斷所包含的IO請(qǐng)求是否來自多個(gè)隊(duì)列(通過成員multiple_queues)。
xi. unplug的時(shí)機(jī)
unplug的時(shí)機(jī)有三種:
(1)所積攢的IO數(shù)目達(dá)到BLK_MAX_REQUEST_COUNT(16)
(2)或遇到的IO大小超過BLK_PLUG_FLUSH_SIZE (128K)時(shí)熔掺;
(3)使用blk_finish_plug()主動(dòng)沖刷饱搏;
2.7 IO 的下發(fā)路徑
IO 下發(fā)處理路徑
路徑一:使能了plug/unplug機(jī)制,此時(shí)會(huì)等待plug池中存取足夠的IO后統(tǒng)一往調(diào)度器插入IO置逻,并選取IO下發(fā)推沸;
路徑二:沒有使能plug/unplug機(jī)制,此時(shí)會(huì)將IO插入調(diào)度器中,并選取IO下發(fā)鬓催;
路徑三:跳過調(diào)度層肺素,直接下發(fā)IO;
參考block layer 系列文章:https://blog.csdn.net/flyingnosky/article/details/121341392