概述
当我们讲到scsi命令这个概念时,需要根据上下文去理解。可能指代两个概念:
- 一种含义指SCSI协议规划中定义的SCSI 命令描述块(Command descriptor block (CDB))。此描述块定义了需要target处理的具体的操作。遵循特定的数据格式,其格式根据操作的不同而不同。SCSI规范中针对每个命令的CDB都有详细的说明。描述块会经过传输层完整的传送到scsi target用于解析并执行。
- 另外一种含义指内核中定义的scsi_cmnd结构。该结构定义于linux/include/scsi/scsi_cmnd.h
中,描述了请求在SCSI子系统执行所需要的数据结构。每个请求都会创建一个scsi_cmnd数据结构。它创建于请求的初始化,结束于命令被正确的执行或者在操作中出现错误。其存储的信息不仅仅包括了最终发送给scsi target的数据,也包括了在scsi子系统和scsi底层驱动执行中需要的运行时信息。
CBD格式
随着技术的发展 ,存储设备的容量持续增长。原有的SCSI指令的寻址空间不能满足新的要求,于是就定了新的可支持更大空间的CBD格式。考虑到向前兼容,SCSI协议没有取消原有的指令,而是重新定义操作码。于是,针对一种服务(如读、写)定义了多种指令格式。
CBD格式分为定长格式和变长格式。
定长格式的CBD包括6、10、12、16四种格式,数字表示CBD的长度(单位:字节)。所有定长指令的第一个字节均为操作码,最后一个字节为控制块。其它域取决于指令。
变长格式,其长度取决于具体的指令。如READ(32)、WRITE(32)分别为32字节。变长格式第一个字节为操作码,固定为操作码(7Fh),其具体服务(如读、写)放在SERVICE ACTION
域,即CBD的第8、第9字节。变长格式的前10个字节是固定的。
无论是定长格式还是变长格式,都包括固定部分,即所有的指令都有的。这样有利于程序设计。系统可以将CBD前几个字节进行统一的解析,再进一步的根据操作码判断后面是否有数据段需要进一步解析。
CDB在内核中表现为一个连续的内存空间。通常它的存储空间在请求初始化时被创建,随着请求完成被释放。请求初始化过程中给cmnd分配了16个字节。
当CDB长度大于32时,驱动程序它初始化时创建的专用缓存池sd_cdb_pool
上申请。
读命令分析
定长读命令
READ读命令可以分成两种格式。READ(10)、READ(12)、READ(16)格式一致,归为第一类。READ32归为第二类。
READ(10)、READ(12)、READ(16)所包括的属性一致。两者别在于LBA与数据传输字节数两个字段的长度,这两个字段长度决定了SCSI指令所能支持的最大容量和一次IO最大的数据长度。自SCSI-3始,规范建议实现方将READ(10-12)迁移到READ16上。
READ(16) CBD格式如下图:
各个字段含义:
字段 | 含义 | 实现 |
---|---|---|
OPERATION CODE | 指令操作码|调用sd_setup_read_write_cmnd 设定 |
|
RDPROTECT | DIF支持相关属性 | 调用sd_setup_read_write_cmnd 设定;设定值取决于磁盘DIF保护方式 |
DPO | 禁用页面缓存(Disable Page Out)。 设定1意为执行此操作时,不进行缓存替换 设定为0意为读取时同时在设备缓存中保留此数据 |
Linux永远设为0 |
FUA | 强制访问介质。 如果设定为1,意味着target强制从介质中读取数据,而非从缓存中 |
在构造IO请求(struct request )时根据应用要求设置; |
RARC | 重构辅助模式。 | scsi层未处理 |
LOGICAL BLOCK ADDRESS | 读命令的起始地址 | 与请求的起始地址一致。以扇区(512字节)为单位 |
TRANSFER LENGTH | 读写数据长度 | 与请求的读写长度一致 |
CONTROL | 命令控制块 | SCSI子系统未设定 |
变长读命令
变长由自第12字节始的8字节表示LBA,由第28字节始的4个字节表示数据长度。其支持的最大容量与最大扇区数与READ16相同。其它变量的含义与READ(16),不详细解释。
比定长CBD多出来的字节主要是用于数据校验。T10是用于解决静默错误的一种机制。
数据在读写环境中要经过多个部件、多个传输渠道和多层次的软件系统,考虑到性能,系统设计过程中并非每层次都对数据进行检测。如此,最终固化的到磁盘上的数据可能是不正确的。为避免此类错误存在,ANSI T10 定义了一种通过对每个数据块加入保护信息(PI:Protection Information)也曾被称作数据完整性域(DIF:Data Integrity Field)的方法来保护数据完整性。只有开启T10信息检验且采用type2 保护信息时,才使用READ(10)命令格式。
P10在企业Oracle数据库应用比较广泛,且是存储一大亮点,所以主流的存储厂商都支持该特性,如EMC VNX系列支持自定义PI、VMAX支持标准的T10 PI,且支持DIX;HDS HUS系列支持自定义PI、HDS VSP支持PI;IBM DS8000支持标准的T10 PI,DS5000某些特定型号支持PI;HP P10000支持标准的T10 PI,华为 OceanStor 18000和V3全系列。 1
写命令分析
写命令与读命令在格式上非常相似,也分为多种格式。
不同的写操作对应的服务是一致的。在Target端可对应于相同的实现。以WRITE(10)为例。其操作码为24A。同时,也包括了用于DIF处理的WRPROTECT域,用于缓存控制的DPO、FUA域。
scsi_cmnd
scsi_cmnd结构体记录了scsi_cmnd需要处理的所有的上下文。系统在初始化request
时就为scsi_cmnd申请空间。当request
在子系统处理完成,scsi上层驱动根据request
创建scsi_cmnd
。面向IO的scsi_cmnd
与request
是一对一的关系。 scsi_cmnd
包含了scsi指令处理所需要的信息,包括CBD、数据、sense data等域。除此之外,还包括一些控制域。 scsi_cmnd
包含了scsi设备的相关信息,用于确定交scsi_cmnd针对于哪个设备。在 scsi_cmnd
流转时,设备类型相关的逻辑处理完成,系统更加关注指令和数据在通信链路上的流转。即关注总线、主机适配器、传输层等信息。 scsi_cmnd
生命周期中可能被挂接到多个不同的队列,代表不同的控制面。大多数据情况,scsi_cmnd
链接到主机适配器的命令处理队列中;在处理过程发生故障时,它又被错误处理相关的队列中暂存,一旦故障恢复,再进行继续处理;如果因为设备忙等原因,主机适配器不能接收scsi命令时,scsi_cmnd
将放到延时处理链表中,等合适的时机再行处理。