iSCSI Initiator 是通过 SCSI Command PDU 向 Target 发出 SCSI 请求,Target 接收请求,执行 SCSI 命令,然后返回数据以及 SCSI 状态。在 SCSI 任务执行时,Initiator/Target 之间会涉及大量数据 I/O。RFC3720 中对这些 I/O 的组织有特别的规定,以下结合 RFC3720, 分析一下 iSCSI 中对 SCSI 请求的具体实现。
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
http://mixin125.m.blog.chinaunix.net/uid-7749031-id-2044705.html