作者:casualfish
主页:
新浪微博:@casualfish
2012.11.18
(转载请保留此部分内容)
1 SCSI标准1.1 SCSI标准历史及组成
SCSI标准定义了计算机和外设进行数据传输的方式,包括传输命令、协议、电气和光物理接口特征。SCSI通常使用在硬盘和磁带设备中,但同时也能连接很多其他类型的设备,包括打印机、扫描仪、CD驱动器等等。
1981年Shuagart联合公司和NCR合作开发了SASI(Shugart Associates System Interface),起初这个协议仅作为这两个公司使用的私有协议,随后为了使SASI能更广发的被业界接受,SASI进行了改善并重新命名为SCSI,1986年ANSI宣布SCSI为工业标准,这就是SCSI-1。SCSI-1只支持单端传输和被动结束,使用了8位总线,最高传输速率为5MB/s。
为了解决SCSI-1协议的各种非标准实现引起的各种问题,ANSI定义和开发了SCSI-2。SCSI-2关注于提高性能和可靠性以及为SCSI-1增加新的功能,同时标准化了SCSI的命令。ANSI于1994年公布了SCSI-2标准:X3.131-1994,SCSI-2向下提供对SCSI-1的兼容。
与此同时,1993年ANSI也开始进行SCSI-3标准的起草工作,与SCSI-1、SCSI-2不同,SCSI-3是由一系列相联系的标准组成,而不是一个单独的大型标准。起草完成SCSI-1使用了3年时间,大概有200页,而完成SCSI-2花费了近8年时间,近600页。
SCSI-3标准仍然在不断的改进和完善之中,-3实际上是T10委员会在起草新的SCSI标准中遇到了命名的问题,为了保持之前的命名规则,进而命名为SCSI-3标准,实际上并没有一个所谓的SCSI-3,SCSI-3就是最后的SCSI标准,之后的修改都是针对每个单独的标准进行修订的。现行的SCSI-3标准组成如图1所示(来自T10官网),图中的每个框都是一个单独的标准。这些标准中最重要的就是SAM(SCSI Architecture Model),有人称它为“SCSI标准的标准”,它定义了什么是SCSI标准,SCSI标准是由哪些部分组成的等等,它抽取了各个SCSI子标准中的公用部分,SCSI各个子标准中都会引用到SAM。
图1 SCSI-3标准及子标准
SCSI标准体系主要由以下三个部分组成,
图2 SCSI-3标准体系组成部分
-
SCSI-3命令协议
包含了对于所有设备通用的命令和对于某类设备特定的命令。图1中黄线上半部分都属于SCSI的命令协议。对于某种类型的设备,需要实现对应的命令协议,例如对于磁带设备,需要实现SPC(SCSI Primary Commands),SSC(SCSI Stream Commands),SMC(SCSI Media Changer Commands),对于磁盘设备,则需要实现SPC(SCSI Primary Commands),SBC(SCSI Block Commands)。
-
传输层协议
类似于OSI标准的七层协议,SCSI协议也定义了自己的传输层的协议,图1中黄线下半部分都属于SCSI的传输层协议,SCSI除了使用自己的SCSI-3传输层协议之外,还租借了其他的协议,例如与FC结合形成了FCP,与IP协议结合形成了iSCSI,时下最新的SCSI over PCIE就是SCSI命令与PCIE总线传输协议结合的产物。
-
物理链路层协议
传输层的每一种协议都定义了自己的电气化特征,例如SCSI-3使用的并口,iSCSI使用的千兆/万兆网络等等。
1.2 SCSI的通信模式
SCSI协议采用了Client-Server的模式完成通信,由Client发送请求,Server完成请求并返回Client端状态及信息。在SCSI中将Client称为Initiator,Server端称为Target,每个Target可以有多个LUN(Logical Unit),每个LUN处理Initiator发送过来的请求,实现LUN所属的SCSI子协议定义的命令,每个LUN包含了两个部分:Device Server和Task Manager,分别完成对于Initiator指定的处理和管理功能,如图3所示。
图3 SCSI的Initiator-Target通信模式
SCSI为设备之间的通信定义了端口,例如有Initiator端口,Target端口,Initiator/Target端口以及多端口的Target设备,对应的也就有Initiator模式,Target模式,Combined模式,多端口target模式等等。处于Initiator模式下只能发送请求和处理请求结果,处于Target模式下只能处理请求,Combined模式则可以针对相应端口的模式设置完成发起请求和处理请求。
对应于OSI的七层标准协议,SAM中也规定了SCSI模式的对应层次,例如SAL(SCSI Application Layer)定义对应了OSI七层的上三层(应用层,表示层,会话层),STPL(SCSI Transport Layer)对应了OSI七层的传输层,网络层,Interconnect layer对应了OSI七层中的数据链路层和物理层。
SCSI协议按照(控制器,通道,SCSIID, LUNID)的形式进行寻址,控制器指的是Initiator设备,通常是连接到主板设备的南桥上或者是以PCIE插卡的形式存在,每个Initiator可能有多个通道,SCSIID指每个通道上连接的SCSI设备,在窄总线(8 bit)中,SCSI ID可能为0-7,SCSI ID为7的设备有最高的优先级,SCSI ID为0的设备优先级最低,在宽总线(16 bit)中,SCSIID可能为0-15,为了保持兼容,SCSI ID为7的设备仍然有最高的优先级,因此此时连接到同一个SCSI总线(通道)上的设备的优先级顺序为7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8。由于每个SCSI设备上可能有多个LUN,因此需要一个单独的LUN ID进行标识。在linux下使用lsscsi会看到类似以下的输出:
[0:0:0:1] disk Linux scsi_debug 0004 /dev/sda /dev/sg0
[0:0:0:49409]wlun Linux scsi_debug 0004 - /dev/sg1
开头的四元组对应的就是[控制器:通道:SCSI ID:LUN ID]。
1.3 SCSI命令模型
有了传输命令的途径,下来就该使用具体的命令进行数据的传输了,SCSI标准规定了一整套详细的命令规范,有些SCSI子标准在通用的SCSI Primary Commands上做了自己的调整,例如iSCSI和serial SCSI。SCSI命令模型以CDB(Command Descriptor Block)的形式定义。每个CDB的长度可以为6,10,12,16字节或者是变长。CDB由一个字节的操作码(operation code),紧跟命令特定的参数,并以一个字节的控制域结束(control filed)。如图4所示:
图4 CDB结构
操作码由group code和command code组成,group code为cdb的高三位,不同的数值定义如下表:
表1 CDB高三位与CDB长度的对应关系
command code是一个5 bit的标识,在每个长度的组内允许32种不同的命令码,一些常用的command code及解释如表2所示:
表2 SCSI Command Code示例
此处列出的command code是SPC中规定的,其他的命令字段在SCSI子标准中规定,例如针对block设备的SBC中规定了Force Unit Access命令。
控制域的定义如图5所示,其中NACA(Normal Auto Contingent Allegiance)表示在命令返回check condition状态时时候是否设置ACA标志,有些情况下一个命令的执行会以CHECK CONDITION状 态中止,表明在命令执行过程中出现了错误或异常。有些命令执行的错误或异常则可能导致命令组中的其他命令被异常中止,需要专门的命令对其做善后处理,并要 求存储设各在完成善后处理工作之前不再处理该用户的其他命令。为了让应用客户能够事先声明哪些命令执行的错误或异常需要善后处理,SCSI允许应用客户在CDB 的控制码中设置NACA位,请求设备在命令执行以“检查条件”状态中止时建立“自动跟随”条件(Condition),从而允许应用客户在随后的善后处理命令中把新的CDB的属性设置成自动跟随(Auto ContingentAllegiance,ACA),实际中这个功能极少使用。
图5 控制域定义
link位在实际中未使用,位3-5为保留位,6-7留给厂商自己定义。
命令结束之后,LUN会把命令执行状态发送给应用客户,SCSI的状态码如图6所示:
图6 SCSI状态码
2 linux kernel中的SCSI子模块2.1 SCSI子模块在IO栈中的位置
谈到SCSI子模块在IO栈的位置时,首先需要谈到linux kernel中io栈的组成,如图7所示:
图7 linux kernel IO栈
当用户调用了write、read等系统调用陷入内核之后,系统会首先针对关联的file对象找到对应的file system,此处针对后端是否有持久化存储可将众多的file system划分为两类,在此我们关心的是后端对应真实存储的file system,经过了file system层处理之后将所有的数据块划分为了一个个的bio,交给device mapper层处理,device mapper层主要完成逻辑设备到物理设备的映射工作,例如可以将bio按LBA地址分别映射到管理的多个设备上(linear,raid0),将bio映射到每个设备上(mirror, raid1),或者是提供快照和多路径的功能,再往下是具体的block层,此处会为每个设备提供一个queue,在queue之上使用各种io scheduler(noop/deadline/cfq)进行请求的合并,最后调用各种不同的设备驱动完成服务。SCSI子模块就是这众多驱动中的一种,其他的还有按照协议和总线划分还有USB,ATA等等。
到此主机的IO路径就结束了,但是在SCSI设备本身还有一系列的处理,如此长的IO路径会给一些数据库、OLTP之类的应用带来较大的延迟,因此时下正在兴起一股缩短IO路径的浪潮,例如将SCSI设备自己完成block层和device mapper层的工作,直接与file system相连,更有甚者仅通过file system管理元数据,而数据则是在通过系统调用直接操作SCSI设备获取和写入,如图7中所示。
2.2 SCSI子模块的组成
linux kernel中SCSI子模块主要由三层组成,如图8所示,其中upper layer主要负责抽象出一些具体类型的设备,例如scsi disk(sd)、scsi tape(st)、scsi cdrom(sr)等等。mid layer主要负责提供一些公用的函数,连接upper layer和lower layer,此处负责lower layer中各种不同物理设备驱动的注册及SCSI相关错误处理,同时mid layer也负责了将上层传来的请求转译成为SCSI请求的工作。lower layer是不同物理设备的驱动层,因此这层的代码也最多,包括了QLogic,Emulex,LSI等等厂家的驱动,lower layer中使用了mid layer提供的回调函数用以通知请求的完成。
图8 linux kernel中scsi子模块的三层