block bio queue request等

參考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_set

         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標誌

BIO_SEG_VALID:bi_phys_segments有了有效值後置這個標誌位。

BIO操做

         bio_flagged(bio,flag)用於檢測bio的bi_flags域是否與flag相等。

request

         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調度。

 

request標誌

         REQ_FLUSH:表示執行bio前進行fluash。REQ_FUA表示執行bio後進行flush。

         QUEUE_FLAG_NO_SG_MERGE:表示是否容許bio自己的bio_vec進行物理合並。

request_queue

這是通用塊層的請求隊列,這個隊列一個cpu一個。上層的數據請求首先生成bio,而後由bio生成request,而後添加到request_queue,而後request_queue會被執行。這個執行包括不少步驟,最重要的是電梯算法。每一個算法都會在全局的request_queue以外生成本身的隊列結構體elevator_queue。

 

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的極限

相關文章
相關標籤/搜索