參考linux API https://www.kernel.org/doc/htmldocs/kernel-api/html
1. queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, ns->queue);linux
不容許對隊列的request進行merge操做ios
2. blk_queue_virt_boundary(ns->queue, dev->page_size - 1);算法
to ensure there are no 'holes' in the presented
sg list (all segments in the middle of the list need to be of PAGE_SIZE).Setting virt_boundary_mask to PAGE_SIZE - 1 guarantees we'll never see
such holesapi
The block layer can reliably guarantee that SG lists won't
contain gaps (page unaligned) if a driver set the queue
virt_boundary.
With this setting the block layer will:
- refuse merges if bios are not aligned to the virtual boundary
- split bios/requests that are not aligned to the virtual boundary
- or, bounce buffer SG_IOs that are not aligned to the virtual boundary 緩存
理解是block層保證request bio中的數據是page對齊的,不對齊的話會分紅多個request性能優化
對磁盤的抽象genhd.c和對分區的抽象:partition-generic.c和partitions目錄下的文件數據結構
l 上層文件系統會把對文件訪問轉變爲對多個sector的訪問,這些sector極可能在內存中是分離的。因此須要一種數據表示方法,用來表示要讀寫的數據內容。這這個數據結構叫作bio(很奇怪的是這裏爲何不直接使用scsi使用的scatterlist?)dom
l scsi相關異步
n 新的scsi標準有DIF/DIX的數據保護機制,不管對於讀仍是寫的數據,都須要一個數據完整性的校驗,因爲在通用塊層存儲數據的結構體是bio,因此對其進行校驗的文件叫作bio-integraty.c。這個文件完成的是與內存相關的設置,真正的算法在blk-integraty.c中定義的一系列鉤子函數。不一樣的硬件會註冊不一樣的計算方法供本層調用。也就是說,這裏實際實現的是DIX協議。
n 本層要知道scsi的接口,本層定義了bsg(blockSCSI generic device)的v4接口,在bsg.c和bsg-lib.c
n t10保護的支持算法t0-pi.c,scsi的ioctl:scsi_ioctl.c
l 鏈接本層各個功能組件的核心程序:blk-core.c,還包括一些輔助文件實現些特定的周邊。這一部分包括
n 內核執行這部分代碼不是阻塞的,而是使用內核線程完成的,使用的是kblockd,其定義和相關功能位於blk-core.c中
n request的處理(bio只是數據的存儲結構,可是一個命令請求不僅有數據,還須要有其餘控制和狀態信息,這些信息和bio一塊兒被組織到request中),可是要注意的是request和bio都只是本層的數據結構,request服務於電梯算法,bio用於盛放用戶傳進內核的數據
u 將用戶數據映射到bio結構體的blk-map.c
u 將request中的bio數據映射到下層(scsi)使用的scatterlist結構體的處理程序blk-merge.c
u request若是超過了必定的時間須要被time out掉,代碼在blk-timeout.c
u request請求到的數據在本層須要有緩衝,能夠從中提取提交到上層上層所須要的數據,而丟棄或者緩存一部分上層沒有要到的數據。這種行爲叫作bounce,功能定義在bounce.c中
n 隊列(queue)處理(對於塊設備的一系列命令,須要隊列緩存,而且這一層最重要的,隊列中的各個命令有可能能夠合併爲一個,例如讀取連續的數據的兩個命令,因爲每次存取數據的量越大,越節省時間,因此這一步是提升效率的關鍵)
u linux的設計者將對queue的插入執行操做單獨的提取出來放到blk-exe.c中
u 對隊列的屬性進行設置的blk-settings.c
u 對隊列中的request添加ID(tag),能夠經過該tag直接找到該request,實如今blk-tag.c
u 凡是通訊管道都要考慮流量控制問題。queue能夠有多個來源,若是某個來源瞬間提交了過多的bio,那麼其餘來源的bio就可能飢餓。防止這種現象發生須要給隊列針對某一個來源添加一個閾值,這個閾值的控制在blk-throttle.c
n 電梯算法接口。上一條說的合併多個request的操做,須要有合併的算法,合併的算法有不少,可是核心部分要爲這些算法提供調用的接口函數
n 提交請求。當電梯算法被執行完,多個request和其對應的bio被合併,這個bio就須要被提交到下層(scsi的上層)去實際的執行發送。發送完畢還要執行回調。這部分代碼也在這裏提供。
l 電梯算法:電梯算法在queue上執行合併操做,是性能優化的關鍵。代碼位於elevator.c,deadline-iosched.c,cfq-iosched.c,noop-iosched.c,還有提供優先級的ioprio.c
l 對於IO上下文的處理。IO上下文是在request上層的數據結構,若是說通用塊層處理的request級別的數據結構,文件系統就是處理的IO上下文。而文件系統層次包括同步和異步兩種數據模式,這裏的IO上下文(io_context)主要是用在異步,異步IO在提交IO請求前必需要初始化一個IO上下文,一個IO上下文會包含多個request。通用塊層對IO上下文的處理函數放在blk-ioc.c。
l 正常的邏輯是發送了IO命令,命令請求完畢後會調用回調函數。可是,通用塊層容許poll操做,就是沒有回調函數,請求執行完後須要用戶手動查詢和處理。這部分代碼在blk-iopoll.c
l 對本層命令隊列的處理能夠有一個CPU,也能夠有多個。若是多個,就須要對隊列進行特殊的優化,叫mq,相關代碼位於blk-mq.c、blk-mq-cpu.c、blk-mq-cpumap.c、blk-mq.h、blk-mq-sysfs.c、blk-mq-tag.c、blk-mq-tag.h中
l 內核處理命令的返回結果,在通用塊層不多是使用硬中斷,因此這裏的回調使用的是軟中斷,定義在blk-softirq.c
l 實現sysfs接口,定義在blk-sysfs.c,實現cgroup子系統的blk-cgroup.c
l 其餘的輔助功能組件:將內容flush進磁盤的blk-flush.c、輔助函數blk-lib.c、用來解析磁盤信息返回值的cmdline-parser.c、提供ioctl接口的compat_ioctl.c,ioctl.c
l
從以上能夠看出,這一部分的關鍵組件是:request、queue、bio、elevator和磁盤與分區的抽象。
若是要對bio進行數據完整性校驗,須要調用bio_integraity_alloc給bio分配對應的空間,以後經過bio_integraty_add_page給bio添加額外的空間,用bio_free就會自動刪除掉分配的空間。
具體的計算bip(dif)的算法由具體的驅動提供,驅動調用的是blk_integraty_register來註冊本身的計算函數。
在文件系統中,能夠經過/sys/block/<bdev>/integraty/目錄下的write_generate和read_verify來控制是否執行讀寫校驗。
大部分狀況下,數據完整性對於文件系統是透明的,但上層的文件系統仍能夠顯示的使用DIX。在bio_integraty_enabled爲1的狀況下,上層調用bio_integrity_prep爲bio準備bip。
磁盤設備在註冊是能夠生成blk_integrity結構體,體面就是存放具體的讀寫校驗函數和tag的大小。
linux的通用快層對磁盤的抽象是gendisk結構體,該層如下的各類設備都是這個結構體的一種。例如scsi磁盤設備scsi_disk就是gendisk的一種。
對於分區的抽象是structpartition。
block_device_operations
對設備進行驅動
對設備進行指令操做的結構體是struct request,鏈接通用快層和下層設備指令操做的數據結構是bio,bio在request中,被上層識別,也被下層識別。
這部分描述當發現插入了磁盤或者是刪除了磁盤時,內核是如何反應的。
BIO是通用塊層表達數據的方式,其將用戶傳遞進來的數據轉換爲bio存儲,bio又包含進了request。多個bio能夠組成連接,bio中內生提供鏈表結構。
struct bio { struct bio *bi_next; /*BIO 鏈表*/ struct block_device *bi_bdev; //文件系統層的塊設備抽象 unsigned long bi_flags; /* status, command, etc */ unsigned long bi_rw; /* 標示是讀仍是寫的標誌位 */
struct bvec_iter bi_iter; unsigned int bi_phys_segments;
/* * To keep track of the max segment size, we account for the * sizes of the first and last mergeable segments in this bio. */ unsigned int bi_seg_front_size; unsigned int bi_seg_back_size;
atomic_t bi_remaining;
bio_end_io_t *bi_end_io; //BIO所有執行結束的回調函數
void *bi_private; unsigned short bi_vcnt; /* how many bio_vec's */ 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[0]; }; |
內核裏一個bio有多個bio_vec,一個bio_vec叫一個segment。因爲上層提交來的bio中的bio_vec,因此bio自己也是能夠合併的。可是每一個queue能夠有標誌位QUEUE_FLAG_NO_SG_MERGE控制是否容許bio的合併。如此,bio就有了兩種統計口徑:bi_vcnt表示bio沒有通過自身合併的bio_vec數目,bi_phys_segments表示將物理連續的bio_vec算成一個後統計出來的段總數。這裏須要注意的是:bio的段總數並非單個bio的段的數目,而由於bio天生是個鏈表,因此段的數目老是統計的是鏈表中段的總數。
BIO_SEG_VALID:bi_phys_segments有了有效值後置這個標誌位。
bio_flagged(bio,flag)用於檢測bio的bi_flags域是否與flag相等。
request中包含了bio和其餘參數,例如代表攜帶數據總大小的__data_len。用雙下劃線的域通常是不直接使用,而是要使用輔助函數調用,典型的是blk_rq_bytes(const struct request *rq)函數返回這個值,而
static inline unsigned intblk_rq_sectors(const struct request *rq)
{
returnblk_rq_bytes(rq) >> 9;
}
又能夠返回這個request攜帶的sector的數目。
struct request {
struct list_head queuelist;
union {
struct call_single_data csd;
unsigned long fifo_time;
};
...}
結構體的定義都是和功能相關的。因爲bio能夠被合併進一個request,因此request要爲這種功能提供支持。bio合併進request能夠在原bio的前面合併也可能在後面。若是在前面,那麼確定是在最前面,此時直接利用bio自己的鏈表結構插入到最前面便可。若是在後面,也確定是在最後面,可是此時沒有使用bio自己的鏈表結構,而是使用了一個額外的域,叫biotail來盛放要合併進入的bio。由於這個域自己的定義就是用來放最後一個bio。向前合併最後一個bio不變,而向後合併最後一個bio要變化。
request中的域分爲3類,分別用在3個不一樣的地方:驅動、通用塊層、IO調度。
REQ_FLUSH:表示執行bio前進行fluash。REQ_FUA表示執行bio後進行flush。
QUEUE_FLAG_NO_SG_MERGE:表示是否容許bio自己的bio_vec進行物理合並。
這是通用塊層的請求隊列,這個隊列一個cpu一個。上層的數據請求首先生成bio,而後由bio生成request,而後添加到request_queue,而後request_queue會被執行。這個執行包括不少步驟,最重要的是電梯算法。每一個算法都會在全局的request_queue以外生成本身的隊列結構體elevator_queue。
Queue的標誌
#define QUEUE_FLAG_QUEUED 1 /*uses generic tag queueing */
#define QUEUE_FLAG_STOPPED 2 /*queue is stopped */
#define QUEUE_FLAG_SYNCFULL 3 /*read queue has been filled */
#define QUEUE_FLAG_ASYNCFULL 4 /*write queue has been filled */
#define QUEUE_FLAG_DYING 5 /*queue being torn down */
#define QUEUE_FLAG_BYPASS 6 /*act as dumb FIFO queue */
#define QUEUE_FLAG_BIDI 7 /*queue supports bidi requests */
QUEUE_FLAG_NOMERGES:直接不容許對隊列的request進行merge操做
#define QUEUE_FLAG_SAME_COMP 9 /*complete on same CPU-group */
#define QUEUE_FLAG_FAIL_IO 10 /*fake timeout */
#define QUEUE_FLAG_STACKABLE 11 /*supports request stacking */
#define QUEUE_FLAG_NONROT 12 /*non-rotational device (SSD) */
#define QUEUE_FLAG_VIRT QUEUE_FLAG_NONROT /* paravirt device */
#define QUEUE_FLAG_IO_STAT 13 /*do IO stats */
#define QUEUE_FLAG_DISCARD 14 /*supports DISCARD */
#define QUEUE_FLAG_NOXMERGES 15 /*No extended merges */
#define QUEUE_FLAG_ADD_RANDOM 16 /*Contributes to random pool */
#define QUEUE_FLAG_SECDISCARD 17 /*supports SECDISCARD */
#define QUEUE_FLAG_SAME_FORCE 18 /*force complete on same CPU */
#define QUEUE_FLAG_DEAD 19 /*queue tear-down finished */
#define QUEUE_FLAG_INIT_DONE 20 /*queue is initialized */
#define QUEUE_FLAG_NO_SG_MERGE 21 /* don't attempt to merge SG segments*/
#define QUEUE_FLAG_SG_GAPS 22 /*queue doesn't support SG gaps */
#define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \
(1 << QUEUE_FLAG_STACKABLE) | \
(1 << QUEUE_FLAG_SAME_COMP) | \
(1 << QUEUE_FLAG_ADD_RANDOM))
#define QUEUE_FLAG_MQ_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \
(1 << QUEUE_FLAG_SAME_COMP))
queue的極限