溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何分析Linux內(nèi)核SCSI IO子系統(tǒng)

發(fā)布時間:2021-11-09 17:32:09 來源:億速云 閱讀:158 作者:柒染 欄目:系統(tǒng)運維

如何分析Linux內(nèi)核SCSI IO子系統(tǒng),針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

概述

LINUX 內(nèi)核中 SCSI 子系統(tǒng)由 SCSI 上層,中間層和底層驅(qū)動模塊 [1] 三部分組成,主要負責管理 SCSI 資源和處理其他子系統(tǒng),如文件系統(tǒng),提交到 SCSI 子系統(tǒng)中的 IO 請求。因此,理解 SCSI 子系統(tǒng)的 IO 處理機制對理解整個 SCSI 子系統(tǒng)就顯的十分重要,同時也有助于理解整個 LINUX 內(nèi)核的 IO 處理機制。本文從 SCSI 設備訪問請求的提交,SCSI 子系統(tǒng)對訪問請求的處理和 SCSI 子系統(tǒng)錯誤處理三個方面,闡述了 SCSI 子系統(tǒng)的 IO 處理機制。

SCSI設備訪問請求的提交

SCSI 設備訪問請求的提交分為兩個步驟:用戶空間提交訪問請求到通用塊層以及通用塊層提交塊訪問請求到 SCSI 子系統(tǒng)。

用戶空間提交訪問請求到通用塊層

在 LINUX 用戶空間,有三種方式提交對 SCSI 設備的訪問請求到通用塊層:

通過文件系統(tǒng)提供的文件訪問接口進行訪問。對建立在 SCSI 設備上的 LINUX 文件系統(tǒng)中的文件讀寫操作,就屬于這種訪問方式;RAW 設備訪問方式。這種訪問方式比較常見的應用就是dd命令。 RAW 設備訪問方式和通過文件系統(tǒng)提供的文件訪問接口進行訪問的***區(qū)別在于前者對 SCSI 設備直接進行線性地址訪問,不需要由文件系統(tǒng)進行地址映射;SCSI PASSTHROUGH 方式。通過 LINUX 提供的 SG 進行訪問,就屬于這種方式,用戶可以直接發(fā) CDB[2] 命令給 SCSI 設備。所以,通過該接口,用戶可以做一些 SCSI 管理操作,如 SES 管理等。

圖 1 顯示了 LINUX 內(nèi)核對于三種請求提交方式的處理過程。

如何分析Linux內(nèi)核SCSI IO子系統(tǒng)
圖 1. LINUX 內(nèi)核處理三種訪問請求的方式

經(jīng)由文件系統(tǒng)或 RAW 設備方式提交的請求,會通過底層塊設備訪問層(ll_rw_block()),由其生成塊 IO 請求(BIO),并提交給通用塊層 [3] ;而通過 SG 接口提交的訪問請求,會調(diào)用 SCSI 中間層提供的接口,將請求直接交由通用塊層進行處理。

通用塊層提交塊訪問請求到SCSI子系統(tǒng)

為什么要通過通用塊層呢?這是因為首先通用塊層會根據(jù)磁盤訪問的特性對請求進行優(yōu)化操作;其次,通用塊層提供了調(diào)度功能,能夠?qū)φ埱筮M行調(diào)度;再次,通用塊層可擴展的結構,使各種設備的塊驅(qū)動都能比較容易的和其集成。

當請求提交到通用塊層后,通用塊層需要完成準備,調(diào)度并交付塊訪問請求給 SCSI 中間層的操作。塊訪問請求可以理解為描述了塊訪問區(qū)域,訪問方式和關聯(lián)的 BIO 的請求,在內(nèi)核中用 'struct request'結構表示。塊設備會有對應的塊訪問請求設備隊列,用于記錄需要該設備處理的訪問請求,新生成的塊訪問請求會被加入到對應設備的塊訪問請求隊列中。 SCSI 子系統(tǒng)對 IO 的處理,實際上是處理塊訪問請求隊列上的塊訪問請求。

通用塊層提供了兩種方式調(diào)度處理塊訪問請求隊列:直接調(diào)度和通過 LINUX 內(nèi)核工作隊列機制調(diào)度執(zhí)行。兩種方式,***都會調(diào)用塊訪問請求隊列處理函數(shù)進行處理,而 SCSI 設備在初始化時會向通用塊層注冊 SCSI 子系統(tǒng)定義的塊訪問請求隊列處理函數(shù)。清單 1[4] 顯示了這個過程。這樣當通用塊層處理 SCSI 設備的塊訪問請求隊列時,調(diào)用的就是 SCSI 中間層定義的這些處理函數(shù)。通過這種方式,通用塊層就將塊訪問請求的處理交給了 SCSI 子系統(tǒng)。

清單 1. 處理函數(shù)

struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)

{……

q = blk_init_queue(scsi_request_fn, NULL);

//request generate block layer allocate a request queue

……

blk_queue_prep_rq(q, scsi_prep_fn); //Prepare a scsi request blk_queue_max_hw_segments(q, shost->sg_tablesize);

//define sg table size

……

blk_queue_softirq_done(q, scsi_softirq_done);

}

SCSI子系統(tǒng)處理塊訪問請求

當 SCSI 子系統(tǒng)的請求隊列處理函數(shù)被通用塊層調(diào)用后,SCSI 中間層會根據(jù)塊訪問請求的內(nèi)容,生成、初始并提交 SCSI 命令 (struct scsi_cmd) 到 SCSI TARGET 端。

SCSI 命令記錄了命令描述塊 (CDB),感測數(shù)據(jù)緩存 (SENSE BUFFER),IO 超時時間等 SCSI 相關的信息和 SCSI 子系統(tǒng)處理命令需要的一些其他信息,如回調(diào)函數(shù)等。清單 2 顯示了這個命令的主要結構。

清單 2. 主要結構

struct scsi_cmnd {

……

void (*done) (struct scsi_cmnd *); /* Mid-level done function */

……

int retries; /*retried time*/

int timeout_per_command; /*timeout define*/

……

enum dma_data_direction sc_data_direction; /*data transfer direction*/

……

unsigned char cmnd[MAX_COMMAND_SIZE]; /*cdb*/

void *request_buffer; /* Actual requested buffer */

struct request *request; /* The command we are working on */

……

unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];

/* obtained by REQUEST SENSE when

* CHECK CONDITION is received on original

* command (auto-sense) */

/* Low-level done function - can be used by */

/*low-level driver to point  to completion function. */

void (*scsi_done) (struct scsi_cmnd *);

……

};

初始化的過程首先按照電梯調(diào)度算法,從塊設備的請求隊列上取出一個塊訪問請求,根據(jù)塊訪問請求的信息,定義 SCSI 命令中數(shù)據(jù)傳輸?shù)姆较?,長度和地址。其次,定義 CDB,SCSI 中間層的回調(diào)函數(shù)等。

在完成初始化后,SCSI 中間層通過調(diào)用scsi_host_template[5]結構中定義的queuecommand函數(shù)將 SCSI 命令提交給 SCSI 底層驅(qū)動部分。queuecommand函數(shù),是一個 SCSI 命令隊列處理函數(shù),在 SCSI 底層驅(qū)動中,定義了queuecommand函數(shù)的具體實現(xiàn)。因此,SCSI 中間層,調(diào)用queuecommand函數(shù)實際上就是調(diào)用了底層驅(qū)動定義的queuecommand函數(shù)的處理實體,將 SCSI 命令提交給了各個廠家定義的 SCSI 底層驅(qū)動進行處理。這個過程和通用塊設備層調(diào)用 SCSI 中間層的處理函數(shù)進行塊請求處理的機制很相似,這也體現(xiàn)了 LINUX 內(nèi)核代碼具有很好的擴展性。底層驅(qū)動接受到請求后,就要開始處理 SCSI 命令了,這一層和硬件關系緊密,所以這塊代碼一般都是由各個廠家自己實現(xiàn)?;玖鞒炭筛爬椋簭牡讓域?qū)動維護的隊列中,取出一個 SCSI 命令,封裝成廠家自定義的請求格式,然后采用 DMA 或者其他方式,將請求提交給 SCSI TARGET 端,由 SCSI TARGET 端對請求處理,并返回執(zhí)行結果給 SCSI 底層驅(qū)動層。

SCSI命令執(zhí)行結果的處理

當 SCSI 底層驅(qū)動接受到 SCSI TARGET 端返回的命令執(zhí)行結果后,SCSI 子系統(tǒng)主要通過兩次回調(diào)過程完成對命令執(zhí)行結果的處理。 SCSI 底層驅(qū)動在接受到 SCSI TARGET 端返回的命令執(zhí)行結果后,會調(diào)用 SCSI 中間層定義的回調(diào)函數(shù),將處理結果交付給 SCSI 中間層進行處理,這是***次回調(diào)過程。 SCSI 中間層處理完成后,將調(diào)用 SCSI 上層定義的回調(diào)函數(shù),結束 IO 在整個 SCSI 子系統(tǒng)中的處理,這為第二次回調(diào)過程。

***次回調(diào):

SCSI 中間層在調(diào)用queuecommand函數(shù)將 SCSI 命令提交給 SCSI 底層驅(qū)動的同時,也將回調(diào)函數(shù)指針傳給了 SCSI 底層驅(qū)動。底層驅(qū)動接受到 SCSI TARGET 端返回的命令執(zhí)行結果后,會調(diào)用該回調(diào)函數(shù),產(chǎn)生一個中斷號為 BLOCK_SOFTIRQ 的軟中斷進行***次回調(diào)處理。在這次回調(diào)處理過程中,SCSI 中間層首先會根據(jù) SCSI 底層驅(qū)動處理的結果判斷請求處理是否成功。處理成功,并不意味著處理沒有錯誤,而是返回的信息,能夠讓 SCSI 中間層很明確的知道,對于這個命令,中間層已經(jīng)沒有必要繼續(xù)進行處理了。所以,對于處理成功的 SCSI 命令,SCSI 中間層會調(diào)用第二次回調(diào)函數(shù)進入到第二次回調(diào)過程。清單 3 顯示了 SCSI 中間層定義的該軟中斷的處理函數(shù)。

清單 3. 該軟中斷的處理函數(shù)

static void scsi_softirq_done(struct request *rq)

{

……

disposition = scsi_decide_disposition(cmd);

……

switch (disposition) {

case SUCCESS:

scsi_finish_command(cmd);

//enter to second callback process

break;

case NEEDS_RETRY:

scsi_retry_command(cmd);

break;

case ADD_TO_MLQUEUE:

scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);

break;

default:

if (!scsi_eh_scmd_add(cmd, 0))

scsi_finish_command(cmd);

}

}

第二次回調(diào):

不同的 SCSI 上層模塊會定義自己不同的第二次回調(diào)函數(shù),如 SD 模塊,會在sd_init_command函數(shù)中,定義自己的第二次回調(diào)函數(shù)sd_rw_intr,這個回調(diào)函數(shù)會根據(jù) SD 模塊的需要,對 SCSI 命令執(zhí)行的結果做進一步的處理。清單 4 顯示了 SD 模塊注冊第二次回調(diào)的代碼。雖然各個 SCSI 上層模塊可以定義自己的第二次回調(diào)函數(shù),但是這些回調(diào)函數(shù)最終都會結束 SCSI 子系統(tǒng)對這個塊訪問請求的處理。

清單 4. SD 模塊注冊第二次回調(diào)的代碼

static int sd_init_command(struct scsi_cmnd * SCpnt)

{

……

SCpnt->done = sd_rw_intr;

return 1;

}

SCSI子系統(tǒng)的錯誤處理

由于 SCSI 底層驅(qū)動是由廠商自己實現(xiàn)的,在此就不予討論。除此之外,SCSI 子系統(tǒng)的出錯處理,主要是由 SCSI 中間層完成。在***次回調(diào)過程中,SCSI 底層驅(qū)動將 SCSI 命令的處理結果以及獲取的 SCSI 狀態(tài)信息返回給 SCSI 中間層,SCSI 中間層先對 SCSI 底層驅(qū)動返回的 SCSI 命令執(zhí)行的結果進行判斷,若無法得到明確的結論,則對 SCSI 底層驅(qū)動返回的 SCSI 狀態(tài)、感測數(shù)據(jù)等進行判斷。對于判斷結論為處理成功的 SCSI 命令,SCSI 中間層會直接進行第二次回調(diào);對于判斷結論為需要重試的命令,則會被加入塊設備請求對列,重新被處理。這個過程可稱為 SCSI 中間層對 SCSI 命令執(zhí)行結果的基本判斷方法。

一切看起來似乎是這么簡單,但是實際上并非如此,有些錯誤是沒有明確的判斷依據(jù)的,如感測數(shù)據(jù)錯誤或 TIMEOUT 錯誤。為了解決這個問題,LINUX 內(nèi)核中 SCSI 子系統(tǒng)引入了一個專門進行錯誤處理的線程,對于無法判斷錯誤原因的 SCSI 命令,都會交由該線程進行處理。線程處理過程和兩個隊列密切相關,一個是錯誤處理隊列(eh_work_q),一個是錯誤處理完成隊列 (done_q) 。錯誤處理隊列記錄了需要進行錯誤處理的 SCSI 命令,錯誤處理完成隊列記錄了在錯誤處理過程中被處理完成的 SCSI 命令。清單 5 顯示了線程對錯誤處理隊列上記錄的命令進行錯誤處理的過程。

清單 5. 錯誤處理的過程

scsi_unjam_host{

……

if (!scsi_eh_get_sense(&eh_work_q, &eh_done_q))

//get sense data

if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q))

//abort command

scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);

//reset

scsi_eh_flush_done_q(&eh_done_q);

//complete error io on done_q

……

}

整個處理過程可歸納為四個階段:

感測數(shù)據(jù)查詢階段

通過查詢感測數(shù)據(jù),為處理 SCSI 命令重新提供判斷依據(jù),并按照前述基本判斷方法進行判斷。如果判斷結果為成功或者重試,則可將該命令從錯誤處理隊列移到錯誤處理完成隊列。若判斷失敗,則命令將會繼續(xù)保留在 SCSI 錯誤處理隊列中,錯誤處理進入到 ABORT 階段。

ABORT階段

在這個階段中,錯誤處理隊列上的 SCSI 命令會被主動 ABORT 掉。被 ABORT 的命令,會被加入到錯誤處理完成隊列。若 ABORT 過程結束,錯誤處理隊列上還存在未能被處理的命令,則需進入 START STOP UNIT 階段進行處理。

START STOP UNIT階段

在這個階段,START STOP UNIT[6] 命令會被發(fā)送到與錯誤處理隊列上的命令相關的 SCSI DEVICE 上,去試圖恢復 SCSI DEVICE,如果在 START STOP UNIT 階段結束后,依舊有命令在錯誤處理隊列上,則需要進入 RESET 階段進行處理。

RESET階段

RESET 階段的處理過程分三個層次:DEVICE RESET,BUS RESET 和 HOST RESET 。首先對與錯誤隊列上的命令相關的 SCSI DEVICE,進行 RESET 操作,如果 DEVICE RESET 后,SCSI 設備能處于正常狀態(tài),則和該設備相關的錯誤處理隊列上的錯誤命令,會被加入到錯誤處理完成隊列中。若通過 DEVICE RESET 不能處理所有的錯誤命令,則需進入到 BUS RESET 階段,BUS RESET 會對與錯誤處理隊列上的命令相關的 BUS,進行 RESET 操作。若 BUS RESET 還不能成功處理所有錯誤處理隊列上的 SCSI 命令,則會進入到 HOST RESET 階段,HOST RESET 會對與錯誤處理隊列上的命令相關的 HOST 進行 RESET 操作。當然,很有可能 HOST RESET 也不能成功處理所有錯誤命令,則只能認為錯誤處理隊列上錯誤命令相關的 SCSI 設備不能被使用了。這些不能被使用的設備會被標記為不能使用狀態(tài),同時相關的錯誤命令都會被加入到錯誤處理完成隊列中。

對于被加入到錯誤處理完成隊列上的請求,若是在設備狀態(tài)正確,命令重試次數(shù)小于允許次數(shù)的情況下,這些命令將被重新加入到塊訪問請求隊列中,進行重新處理;否則,直接進行第二次回調(diào)處理,完成 SCSI 子系統(tǒng)對塊訪問請求的處理。這樣,SCSI 子系統(tǒng)就完成了 SCSI 命令錯誤處理的整個過程。

關于如何分析Linux內(nèi)核SCSI IO子系統(tǒng)問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業(yè)資訊頻道了解更多相關知識。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI