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
阅读(1236) | 评论(0) | 转发(0) |