16.4. 一些其他的細節(jié)

2018-02-24 15:50 更新

16.4.?一些其他的細節(jié)

本節(jié)涵蓋塊層的幾個其他的方面, 對于高級讀者可能有興趣. 對于編寫一個正確的驅(qū)動下面的內(nèi)容都不需要, 但是它們在某些情況下可能是有用的.

16.4.1.?命令預(yù)準備

塊層為驅(qū)動提供一個進制來檢查和預(yù)處理請求, 在它們被從 elv_next_request 返回前. 這個機制允許驅(qū)動提前設(shè)立真正的驅(qū)動器命令, 決定是否這個請求可被完全處理, 或者進行其他的維護工作.

如果你想使用這個特性, 創(chuàng)建一個命令準備函數(shù), 它要適應(yīng)這個原型:


typedef int (prep_rq_fn) (request_queue_t *queue, struct request *req); 

請求結(jié)構(gòu)包含一個成員 cmd, 它是一個 BLK_MAX_CDB 字節(jié)的數(shù)組; 這個數(shù)組可被這個準備函數(shù)用來存儲實際的硬件命令(或者任何其他的有用信息). 這個函數(shù)應(yīng)當返回一個下列的值:

BLKPREP_OK
命令準備正常進行, 并且這個請求可被傳遞給你的驅(qū)動的請求函數(shù).

BLKPREP_KILL
這個請求不能完成; 它帶有一個錯誤碼而失敗.

BLKPREP_DEFER
這個請求這次無法完成. 它位于隊列的前面, 但是不能傳遞給請求函數(shù).

準備函數(shù)被 elv_next_request 在請求返回到你的驅(qū)動之前立刻調(diào)用. 如果這個函數(shù)返回 BLKPREP_DEFER, 從 elv_next_request 返回給你的驅(qū)動的返回值是 NULL. 這個操作描述可能是有用的, 如果, 例如你的設(shè)備已達到它能夠等候的請求的最大數(shù)目.

為使塊層調(diào)用你的準備函數(shù), 傳遞它到:


void blk_queue_prep_rq(request_queue_t *queue, prep_rq_fn *func); 

缺省地, 請求隊列沒有準備函數(shù).

16.4.2.?被標識的命令排隊

可同時有多個請求被激活的硬件, 常常支持某種被標識的命令排隊(TCQ). TCQ 簡單地說是關(guān)聯(lián)一個整數(shù) "tag" 到每個請求的技術(shù), 注意當驅(qū)動器完成每個請求時, 他可告知驅(qū)動是哪一個. 在以前的內(nèi)核版本, 實現(xiàn) TCQ 的塊驅(qū)動不得不自己做所有的工作; 在2.6, 一個 TCQ 支持框架已經(jīng)被添加到塊層, 以給所有的驅(qū)動來使用.

如果你的驅(qū)動器進行標記命令排隊, 你應(yīng)當在初始化時通知內(nèi)核這個事實, 使用:


int blk_queue_init_tags(request_queue_t *queue, int depth, struct blk_queue_tag *tags);

這里, queue 是你的請求隊列, 而 depth 是你的設(shè)備能夠在任何時間擁有的等待的標記請求的數(shù)目. tags 是一個可選的指針指向一個 struct blk_queue_tag 結(jié)構(gòu)數(shù)組; 必須有 depth 個. 正常地, tags 可用 NULL, 并且 blk_queue_init_tags 分配這個 數(shù)組. 如果, 但是, 你需要和多個設(shè)備分享通用的 tags, 你可傳遞這個標記數(shù)組指針(存儲在 queue_tags 成員)從另一個請求隊列. 你應(yīng)當從不真正自己分配這個標記數(shù)組; 塊層需要初始化這個數(shù)組并且不輸出這個初始化函數(shù)給模塊.

因為 blk_queue_init_tags 分配內(nèi)存, 它可能失敗. 在那個情況下它返回一個負的錯誤碼給調(diào)用者.

如果你的設(shè)備可處理的標記的數(shù)目改變了, 你可通知內(nèi)核, 使用:


int blk_queue_resize_tags(request_queue_t *queue, int new_depth); 

這個隊列鎖必須在這個調(diào)用期間被持有. 這個調(diào)用可能失敗, 返回一個負錯誤碼.

一個標記和一個請求結(jié)構(gòu)的關(guān)聯(lián)被 blk_queue_start_tag 來完成, 它必須在成員隊列鎖被持有時調(diào)用:


int blk_queue_start_tag(request_queue_t *queue, struct request *req); 

如果一個 tag 可用, 這個函數(shù)分配它給這個請求, 存儲這個標識號在 req->tag, 并且返回 0. 它還從隊列中解除這個請求, 并且連接它到它自己的標識跟蹤結(jié)構(gòu), 因此你的驅(qū)動應(yīng)當小心不從隊列中解除這個請求, 如果在使用標識. 如果沒有標識可用, blk_queue_start_tag 將這個請求留在隊列并且返回一個非零值.

當一個給定的請求的所有的傳送都已完成, 你的驅(qū)動應(yīng)當返回標識, 使用:


void blk_queue_end_tag(request_queue_t *queue, struct request *req); 

再一次, 你必須持有隊列鎖, 在調(diào)用這個函數(shù)之前. 這個調(diào)用應(yīng)當在 end_that_request_first 返回 0 之后進行(意味著這個請求完成), 但要在調(diào)用 end_that_request_last 之前. 記住這個請求已經(jīng)從隊列中解除, 因此它對于你的驅(qū)動在此點這樣做可能是一個錯誤.

如果你需要找到關(guān)聯(lián)到一個給定標識上的請求(當驅(qū)動器報告完成, 例如), 使用 blk_queue_find_tag:


struct request *blk_queue_find_tag(request_queue_t *qeue, int tag);

返回值是關(guān)聯(lián)的請求結(jié)構(gòu), 除非有些事情已經(jīng)真的出錯了.

如果事情真地出錯了, 你的請求可能發(fā)現(xiàn)它自己不得不復(fù)位或者對其中一個它的設(shè)備進行一些其他的大動作. 在這種情況下, 任何等待中的標識命令將不會完成. 塊層提供一個函數(shù)可用幫助在這種情況下恢復(fù):


void blk_queue_invalidate_tags(request_queue_t *queue); 

這個函數(shù)返回所有的等待的標識給這個池, 并且將關(guān)聯(lián)的請求放回請求隊列. 你調(diào)用這個函數(shù)時必須持有隊列鎖.

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號