iSCSI 中 SCSI 讀寫請求處理分析

2021-08-17 00:10:21 字數 4444 閱讀 5567

iscsi initiator 是通過 scsi command pdu 向 target 發出 scsi 請求,target 接收請求,執行 scsi 命令,然後返回資料以及 scsi 狀態。在 scsi 任務執行時,initiator/target 之間會涉及大量資料 i/o。rfc3720 中對這些 i/o 的組織有特別的規定,以下結合 rfc3720, 分析一下 iscsi 中對 scsi 請求的具體實現。

scsi read 比較簡單,就先說說 scsi read 吧。initiator 發出 scsi read 請求以後,target 從裝置中讀取出資料,然後通過 datain 資料報返回給 initiator。如果資料長度太長,就要分成多個 datain 資料報。至於每個 datain 資料報最大資料段長度為多少由 login 時 initiator 給出的 maxrecvdatasegmentlength 值控制,不能超過該值。另外, 這些 datain 是否按順序傳送同則由 datasequenceinorder 以及 datapduinorder 控制。具體請參閱《iscsi幾個關鍵字協商與實現》一文。資料報互動流程大致如下:

scsi read (128)   --------> 

<--------     datain(flags=0, data_sn=0, data_offset=0)

<--------     datain(flags=0, data_sn=1, data_offset=8192)

<--------     datain(flags=0, data_sn=2, data_offset=16384)

<--------     datain(flags=0x81, data_sn=3, data_offset=32768)

# 結束包帶f標記以及status

作為 target 端來說,似乎想不出有什麼理由不按順序傳送 datain,但有一種情況就是 mc/s 時,有可能從各個 connection 中分發 datain(這樣可提高率能),如果各個鏈路走的路徑不一樣,那麼到達 initiator 端的 datain 包就有可能為非順序的了。別外,在協議相容性測試時,為了增加測試覆蓋率,也會想盡辦法產生這樣的 case。當然最簡單的處理方式為 target 端直接協商時就直接宣告按順序收發 datain,如果是這種情況,我想每乙個 scsi 任務,只能由乙個 connect 上進行收到資料報了,否則沒有辦法保證 datain 的順序性(至少理論上沒法保證吧)。

scsi write 實現與 scsi read 稍有不同,主要是受幾個協商關鍵字的影響。一般 scsi write 過程為:首先發 scsi 寫指令,target 端收到請求後會分配置相應緩衝區進行接收,然後返回 r2t 包要求 initiator 先發這個範 圍的資料,r2t 包中指定了initiator 要傳送的資料範圍。initiator 就是根據 r2t 劃定的範圍依次發出這個範圍的資料。如果整個資料沒有完全接收完,會再發出一下個 r2t 要求 initiator 繼續發資料,重複上面過程直到接收全部資料,最後返回 scsi response 告訴 initiator 任務執行完成。initiator 資料是通過 dataout 形式進行發出的,當然 dataout 順序與 datain 一樣,受協商關鍵字影響。由於這些dataout 中的資料是由 r2t 指定範圍,也就是說應答 r2t 的這些 dataout 資料報屬於請求類資料 (solicated),根據 r2t 的請求來傳送。當然還有一些屬於非請求數(unsolicated),也就是說在沒有 r2t 請求之前就已經發出去,也就是說從 scsi write 到第乙個 r2t 之間,initiator 所發了的資料都稱為非請求資料(包括立即資料以及最開始幾個 dataout 資料),非請求類資料位於整個資料的最前面。這段資料的長度由 firstburstlength 控制,另外,是否允許這些非請求類資料由 initialr2t,以及immediatedata 所控制。r2t 中的請求範圍值也是控制的,主要受 maxburstlength 值所限制。可以這樣去理解,immediatedata 是立即資料開關,initir2t 是非請求dataout 開關。比如:

1.immediatedata=yes, initialr2t=yes,這時只允許立即資料,不允許非請求類dataout:

scsi_write    ------------>

immediatedata

<-------------      r2t0(請求範圍<=maxburstlength)

dataout0     ------------->     # 單pdu資料段長度<= maxrecvdatasegmentlength

dataout1      ------------->

dataoutn      -------------> # dataout(0,1,2..n)總長<= maxburstlength

<-------------  r2t1(請求範圍<=maxburstlength)

dataout0      ------------->

dataout1      ------------->

dataoutn      -------------> 

<-------------  scsi_response

2.immediatedata=yes, initialr2t=no,這時即允許立即資料,也允許非請求類dataout:

scsi_write ------------>

immediatedata

dataout0  -------------> # 單pdu資料段長度<= maxrecvdatasegmentlength

dataout1  ------------->

dataoutn  -------------> # immediatedata + 幾個dataout總長 <= firstburstlength

<-------------  r2t0(請求範圍<=maxburstlength)

dataout0  -------------> # 單pdu資料段長度<= maxrecvdatasegmentlength

dataout1  ------------->

dataoutn  -------------> # dataout(0,1,2..n)總長<= maxburstlength

<-------------  r2t1(請求範圍<=maxburstlength)

dataout0  ------------->

dataout1  ------------->

dataoutn  -------------> 

<-------------  scsi_response

3.immediatedata=no, initialr2t=yes,這時不允許立即資料,也不允許非請求類dataout:

scsi_write------------>

<-------------  r2t0(請求範圍<=maxburstlength)

dataout0  -------------> # 單pdu資料段長度<= maxrecvdatasegmentlength

dataout1  ------------->

dataoutn  -------------> # dataout(0,1,2..n)總長<= maxburstlength

<-------------  r2t1(請求範圍<=maxburstlength)

dataout0  ------------->

dataout1  ------------->

dataoutn  -------------> 

<-------------  scsi_response

4.immediatedata=no, initialr2t=no,這時不允許立即資料,允許非請求類dataout:

scsi_write------------>

dataout0  -------------> # 單pdu資料段長度<= maxrecvdatasegmentlength

dataout1  ------------->

dataoutn  -------------> # immediatedata + 幾個dataout總長 <= firstburstlength

<-------------  r2t0(請求範圍<=maxburstlength)

dataout0  -------------> # 單pdu資料段長度<= maxrecvdatasegmentlength

dataout1  ------------->

dataoutn  -------------> # dataout(0,1,2..n)總長<= maxburstlength

<-------------  r2t1(請求範圍<=maxburstlength)

dataout0  ------------->

dataout1  ------------->

dataoutn  -------------> 

<-------------  scsi_response

scsi裝置的請求處理函式(request fn

每個塊裝置驅動程式的核心就是它的請求處理函式,即請求佇列中對應的request fn函式 struct request queue下面分析scsi裝置的請求處理函式 static struct scsi device scsi alloc sdev struct scsi target starge...

Glusterfs下讀寫請求的處理流程

glusterfs基於核心的fuse模組,fuse模組除了建立fuse檔案系統外,還提供了乙個字元裝置 dev fuse 通過這個字元裝置,glusterfs可以讀取請求,並傳送響應,並且可以傳送notify訊息。下面是在glusterfs下的乙個讀 寫請求的完整流程 藍實線表示乙個請求通過系統呼叫...

vue中async await請求處理

promise.all 用法示例 const wait ms new promise resolve,reject ms resolve ms const pa promise.all wait 3000 wait 1000 wait 2000 依次列印 wait 1000ms wait 2000m...