2010-3-7 sbull代碼閱讀

昨晚,我把sbull的代碼,大體上看了一下,因爲ldd這一章已經看過,此外對Linux驅動程序的結構有一定了解,所以基本上看得懂,但是也遇到了一些疑惑,今天上午,我再次閱讀了ldd中的相關內容,主要如下:

1、弄清楚了以下幾個概念:
請求函數(請求處理函數):負責執行塊設備的讀寫請求,blk_init_queue函數負責分配請求隊列,並綁定自旋鎖和請求函數
在sbull的代碼中,RM_SIMPLE模式和RM_FULL的不同就在於請求函數的不同。
請求完成函數:通知塊設備子系統,設備已完成在一個I/O請求中的部分或全部扇區
主要函數有end_that_requset_first和end_that_request_end
“構造請求”的函數:在不使用請求隊列的時候會用到,使用來對請求直接進行傳輸,或者把請求定向到其他設備
通過blk_queue_t_make_request()函數通知塊設備子系統,驅動程序使用定製的函數。
2、request結構中的buffer成員的理解(現在仍不太理解)
sbull程序有三種模式:
enum {
RM_SIMPLE = 0, /* The extra-simple request function */
RM_FULL = 1, /* The full-blown version */
RM_NOQUEUE = 2, /* Use make_request */
};
其中RM_SIMPLE對應的是使用簡單的請求處理函數,RM_FULL則使用了bio方面的內容,通過閱讀兩者的代碼,我對buffer(request結構中的成員)產生了很大的疑惑。
ldd中寫到“從本質上講,一個request結構式作爲一個bio結構的鏈表實現的”,request中的bio指針就負責指向bio鏈表,而bio結構則描述I/O請求。
RM_FULL對應的請求處理函數就是直接對bio進行操作完成I/O請求的,函數調用鏈爲:
sbull_full_request--->sbull_xfer_request--->sbull_xfer_bio,sbull_xfer_bio的代碼如下:

static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;

/* Do each segment independently. */

/*bio_for_each_segment 只是簡單的在bi_io_vec數組中遍歷每個沒有被處理的入口*/
bio_for_each_segment(bvec, bio, i) {
/*__bio_kmap_atomic函數用來爲緩衝區返回內核虛擬地址*/
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
sbull_transfer(dev, sector, bio_cur_sectors(bio),buffer, bio_data_dir(bio) == WRITE);
sector += bio_cur_sectors(bio);
/*__bio_kunmap_atomic用於取消緩衝區映射*/
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0; /* Always "succeed" */
}

從代碼中,可以看到,在使用sbull_transfer傳輸數據前需要進行虛擬地址映射,之後還有取消映射,
但是RM_SIMPLE模式使用的請求處理函數只是直接使用request結構中的buffer成員就搞定了,我就不知道buffer到地指向什麼。
首先,我在想,是不是request結構中的bio鏈表中各個bio的緩存區域都已經映射到有buffer指向的同一個虛存地址了呢?
在ldd的470頁寫到buffer是“要傳輸或者要接受數據的緩衝區指針。該指針在內核虛擬地址中,如果有需要,驅動程序可以直接引用它。”
因此,我覺得我的想法可能說對了,但是在478頁又提到“這個成員不過是在當前bio中調用的bio_data的結果”,
#define bio_data(bio) (page_address(bio_page((bio))) + bio_offset((bio)))
#define bio_page(bio) bio_iovec((bio))->bv_page
如果是這樣的話,buffer則只是一個bio對應的虛存地址,於是我又陷入了疑惑:
難道request鏈表只有一個bio?或者在sbull中的RM_SIMPLE模式是這樣的?
或者塊設備子系統有什麼機制,將bio鏈表中每個bio對應的緩存映射到buffer?
這估計還是得請教學長。
3、end_that_request_last函數
以前我編譯sbull程序時,報錯end_that_request_last的參數少了(2.6.18的內核中該函數有兩個參數,而ldd中該函數只有一個參數),
由於ldd中提到“如果有必要的話,還使用end_that_request函數”,我誤以爲該函數可要可不要於是直接註釋掉了,後來發現該函數的作用是:
“通知任何等待已經完成請求的對象,並重複利用該request結構”,之所以編譯後能正常使用是因爲默認是RM_SIMPLE,根本不用改函數。

那麼第二個參數到底寫什麼呢?
閱讀源代碼發現在end_that_request_last函數中,第二個參數只用到一次,就是用來設置error,如下所示:
if (end_io_error(uptodate))
error = !uptodate ? -EIO : uptodate;
於是,我覺得如果成功給個1就行了,end_that_request_last()也是用到了該函數,它在失敗是傳的是0(對於非文件系統操作),成功則傳1。
發佈了20 篇原創文章 · 獲贊 1 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章