SCSI命令集如何通过ioctl发送对应的命令
SCSI和相关的命令集
所有SCSI设备都应响应INQUIRY命令,其响应的⼀部分是所谓的外围设备类型。linux内核使⽤它来决定哪个上层驱动程序控制设备。还有⼀些设备属于使⽤SCSI命令集的其他(即不被视为SCSI)传输,其主要⽰例是(S-)ATAPI CD和DVD驱动器。并⾮所有外围设备类型都映射到上层驱动程序,并且这些类型的设备通常通过SCSI generic(sg)驱动程序访问
SG_IO ioctl概述
赋予SG_IO ioctl的第三个参数是指向sg_io_hdr结构实例的指针,该结构在<scsi / sg.h>头⽂件中定义。SG_IO ioctl的执⾏可视为分三个阶段:
1. 对sg_io_hdr实例中的元数据进⾏健全性检查; 读取输⼊字段和其中⼀些字段指向的数据; 构建SCSI命令并将其发送到设备
2. 等待来⾃设备的响应,超时命令或⽤户终⽌调⽤SG_IO ioctl的进程(或线程)
3. 写⼊输出字段,并在某些情况下将数据写⼊某些字段指向的位置,然后返回
只有阶段1返回ioctl错误(即返回值-1和在errno中设置的值)。在阶段2中,应谨慎使⽤命令超时,因为设备(以及同⼀互连上的其他⼀些设备)可能最终被重置。如果⽤户终⽌调⽤SG_IO ioctl的进程或线程,那么显然阶段3从未发⽣但命令执⾏运⾏完成(或超时)并且内
核“抛弃”结果。如果该命令产⽣CW状态为CHECK CONDITION(在字段“status”中),则在阶段3中写出感测数据。
现在我们假设SCSI命令涉及将⽤户数据传输到设备或从设备传输。SCSI⼦系统不⽀持向设备进⾏真正的双向数据传输。所有数据DMA传输(假设硬件⽀持DMA)都发⽣在阶段2.但是,如果正在使⽤间接IO(即既不是直接IO也不是mmap-ed传输),则:
数据从阶段1中的⽤户空间读⼊内核缓冲区,并在阶段2或者DMA中编辑到设备
在阶段2中将数据从设备读⼊内核缓冲区并在阶段3中写⼊⽤户空间
当使⽤直接IO或mmap-ed传输时,所有⽤户数据都在阶段2中移动。如果在此类数据传输期间终⽌进程,则内核会正常处理此问题(通过固定关联的内存页直到传输完成)。
sg_io_hdr结构有22个字段(成员),但通常只需要设置少量字段。以下代码⽚段显⽰了⼀个简单的TEST UNIT READY SCSI命令的设置,它没有相关的数据传输:
unsigned char sense_b [32];
unsigned char turCmbBlk [] = {TUR_CMD,0,0,0,0,0};
struct sg_io_hdr io_hdr;
memset(&io_hdr,0,sizeof(struct sg_io_hdr));
io_hdr.interface_id ='S';
d_len = sizeof(turCmbBlk);
_sb_len = sizeof(sense_b);
io_hdr.dxfer_direction = SG_DXFER_NONE;
dp = turCmbBlk;
io_hdr.sbp = sense_b;
io_hdr.timeout = DEF_TIMEOUT;
if(ioctl(fd,SG_IO,&io_hdr)<0){
memset()调⽤⾮常重要,将未使⽤的输⼊字段设置为安全值。将超时字段设置为零不是⼀个好主意; 对于⼤多数SCSI命
令,30,000(30秒)是合理的默认值。与往常⼀样,良好的错误处理会消耗更多代码。对于出现问题时产⽣“感知数据”的SCSI命令尤其如此。例如,如果在磁盘读取期间出现中等错误,则检测数据将包含故障的逻辑块地址(lba)。另⼀个错误处理⽰例是设备认为是“⾮法请求”的SCSI命令,感测数据可以显⽰它反对的命令块(通常称为“cdb”)中字段的字节和位位置。有关错误处理的⽰例,请参阅
sg3_utils包,其“
下⾯是⼀组重要的sg_io_hdr结构字段和简短的摘要:
命令块(以前称为“cdb”):
cmdp - 指向cdb的指针(SCSI命令块)
cmd_len - cdb的长度(以字节为单位)
数据传输:
dxferp - 指向⽤户数据的指针,⽤于开始读取或开始写⼊
dxfer_len - 要传输的字节数
dxfer_direction - 是从设备读取(到⽤户存储器)还是写⼊设备(从⽤户存储器)或不传输数据:分别为
DXFER_FROM_DEV,DXFER_TO_DEV或DXFER_NONE
resid - 要传输的请求字节数(即dxfer_len)减去传输的实际数量
错误指⽰:
status - 从设备返回的SCSI状态
host_status - 来⾃主机总线适配器的错误,包括启动器(端⼝)
driver_status - 驱动程序(中级或低级驱动程序)错误和建议掩码
感知数据(仅在'status'为CHECK CONDITION或(driver_status&DRIVER_SENSE)为真时使⽤):
sbp - 开始将感知数据写⼊的指针
mx_sb_len - 要写⼊sbp的最⼤字节数
sb_len_wr - 写⼊sbp的实际字节数
sg_io_hdr结构中的字段在 ⽂档中有更详细的定义 。
SG_IO ioctl在sg驱动程序中
Linux内核2.4.0是第⼀个⽣成内核,SG_IO ioctl出现在SCSI通⽤(sg)驱动程序中。sg驱动程序本⾝⾃1993年以来⼀直在linux中.sg驱动程序中sg_io_hdr结构的⼀个实例可以是:
由SG_IO ioctl的第三个参数指出
UNIX write()或read()系统调⽤的第⼆个参数指向的,它们将sg设备节点的⽂件描述符作为其第⼀个参数
在 ⽂档描述LK 2.4系列,包括其使⽤的SG_IO IOCTL的sg驱动程序。在lk 2.4系列之前,sg驱动程序只有sg_header结构。它被⽤作异步命令接⼝,其中命令,元数据和可选的⽤户数据是通过Unix write()系统调⽤发送的。通过Unix read()系统调⽤接收包括错误信息(例如,感测数据)或可选的⽤户数据的相应响应。在lk 2.4系列的开头,对sg驱动程序做了两个主要的补充:
新的元数据结构(sg_io_hdr)作为原始混合元数据和数据结构的替代(sg_header)
使⽤新元数据结构并且是同步的SG_IO ioctl:它发送了⼀个SCSI命令并等待其回复
sg_io_hdr仅包含元数据,因为它包含指向数据来源位置(命令或数据输⼊)或转到(检测数据或数据输出)的位置的指针。这些指针在混合的32/64位环境中引起了问题,尤其是当⽤户应⽤程序(例如cdrecord)构建为32位且内核为64位时。lk 2.6系列有⼀个兼容层,可以通过专⽤于SG_IO ioctl的代码来处理这个问题。不幸的是,在设计sg_io_hdr结构时没有预见到这个问题。
sg驱动程序中SG_IO ioctl的⼀个重要特性是它是⽤户可中断的。这意味着在发出命令(例如,像磁盘格式的长持续时间命令)和其响应到达之间,⽤户可以在相关应⽤程序上命中控制-C。内核将保持稳定,资源将在适当的时间清除。sg驱动程序不会尝试中⽌这样⼀个“在飞⾏中”的命令,它只是抛弃响应并清理。当然,⽤户没有直接的⽅法来查明中断的命令是否成功,可能存在间接⽅式。
这⾥也可以按顺序发出警告:诸如格式之类的长持续时间命令通常会被赋予长超时值。如果⽤户中断了发送format命令的应⽤程序,则设备可能仍然忙于执⾏格式化(特别是如果未设置IMMED位)。因此,如果⽤户随后发送了诸如TEST UNIT READY或REQUEST SENSE之类的短持续时间命令来查看设备正在执⾏的操作,则这些命令可能会超时。这将调⽤SCSI⼦系统错误处理程序,该处理程序最有可能发送设备重置,从⽽中⽌格式,以引起设备的注意。这可能不是⽤户想到的!
SG_IO ioctl的差异
在下表中,sg_io_hdr结构字段按它们在该结构中出现的顺序列出。基本上,“in”字段出现在结构的顶部并在阶段1中读取。后⾯的字段被称为“out”并且由阶段3中的SG_IO实现写⼊。
表1. sg_io_hdr结构摘要和实现差异
sg_io_hdr字段进出类型不同简要描述包括实现之间的差异
interface_id在INT守卫场。当前实现仅接受“(int)'S'”。如果未设置,则sg驱动程序将errno设置为ENOSYS,⽽块层
将其设置为EINVAL
dxfer_direction在(-ve)int次要数据传输⽅向。SG_DXFER_NONE和朋友被定义为负整数,因此sg驱动程序可以区分sg_io_hdr实例
和sg_header实例。这种细微差别与SG_IO的⾮sg驱动程序使⽤⽆关。见下⽂。
cmd_len在unsigned
char
将命令长度限制为255个字节。没有SCSI命令(即使OSD中的可变长度命令)这么长(还)
max_sb_len在unsigned
mmap格式怎么打开char
驱动程序可以通过sbp指针输出的最⼤感测数据字节数
iovec_count在未签约的短
⽚是如果不是sg驱动程序且⼤于零则则SG_IO ioctl失败,并且errno设置为EOPNOTSUPP; 当此字段⼤于零时,sg驱动程序将dxferp视为指向数组struct sg_iovec的指针
dxfer_len在unsigned
int 次要要传输到设备或从设备传输的数据的字节数。与/ sys / block / <device> / queue / max_sectors_kb相关的块设备的上限
dxferp in
[*in
or
*out]⽆效*次要指向(⽤户空间)数据的指针,⽤于传输(如果从设备读取)或传输(如果写⼊设备)。当iovec_count⼤于0时,sg驱动程序中的间接进⼀步级别。
cmdp⽆符号的字
符 *指向SCSI命令的指针。如果cmdp为NULL,则sg驱动器中的SG_IO ioctl将失败,并且errno设置为EMSGSIZE;如果cmdp为⽆效,则为EFAULT; 在两种情况下,块层都将errno设置为EFAULT。
sbp进出]⽆符号的字
符 *指向⽤户数据区的指针,如果SCSI状态为CHECK CONDITION,则不会写⼊来⾃设备的max_sb_len 字节的感测数据。
timeout在unsigned
int 是
(如
果=
0)
SCSI中级等待响应的时间(以毫秒为单位)。如果该计时器在命令完成之前到期,则可以中⽌该命令,可以根据错误处理程序设置重置设备(以及可能在同⼀互连上的其他设备)。危险的东西,SG_IO ioctl ⽆法控制(通过这个界⾯)究竟会发⽣什么。在sg驱动程序中,超时值0表⽰0毫秒,在块层(当前)中表⽰60秒。
flags在unsigned
int 是块层SG_IO ioctl忽略该字段; sg驱动程序使⽤它来请求直接IO或mmap-ed传输等。这有点⾯具。
pack_id in - >
out
INT unused(⽤于⽤户空间程序标记)
usr_ptr in - >
out
⽆效*unused(⽤于⽤户空间指针标记)
status out⽆符号的字
SCSI命令状态,零表⽰良好
masked_status out⽆符号的字
逻辑上:masked_status ==((状态&0x3e)>> 1)。旧的Linux SCSI⼦系统使⽤情况,已弃⽤。
msg_status out⽆符号的字
SCSI并⾏接⼝(SPI)消息状态(⾮常旧,已弃⽤)
sb_len_wr出unsigned
short
通过sbp指针输出的感测数据的实际长度(以字节为单位)。
HOST_STATUS出unsigned
short
启动器(端⼝)报告的错误。这些是scsi.h中的“DID_ *”错误代码
DRIVER_STATUS出unsigned
short
位掩码:低级驱动程序(LLD)报告的错误和建议。这些是scsi.h中的“DRIVER_ *”错误代码
resid出INT(dxfer_len - number_of_bytes_actually_transferred)。通常仅在设备缩短DMA传输时设置。不
⼀定是错误。较旧的LLD总是产⽣零。
duration出unsigned
int 从命令注⼊SCSI中间级别到调⽤相应的“完成”回调之间经过的毫秒数。⼤致是SCSI命令的持续时间(以毫秒为单位)。
info出unsigned
int 次要位掩码指⽰已完成(或未完成)以及是否检测到任何错误。如果检测到错误,则块层SG_IO ioctl仅设置SG_INFO_CHECK
open()考虑因素
打开设备节点时,各种驱动程序具有不同的特征。ioctl系统调⽤的⼀个问题是⽤户只需要读取权限来执⾏它,但是可以使⽤像SG_IO这样的ioctl写⼊设备(例如格式化它)。命令(操作代码)嗅探逻辑⽤于克服此安全问题。此外,SG_IO ioctl的⽤户在与sd,st或cdrom驱动程序“共享”设备时需要知道这些驱动程序中的状态机可能被欺骗。这可能是不可避免的,但SG_IO ioctl的⽤户应该采取适当的谨慎措施。
在标志为零的linux中打开⽂件意味着O_RDONLY标志,因此只读访问。所有open()系统调⽤都可以
产⽣ENOENT(没有这样的⽂件或⽬录); ENODEV(没有此类设备)如果⽂件存在但没有连接的设备和EACCES(权限被拒绝),如果⽤户没有适当的权限。
具有CAP_SYS_RAWIO功能的⽤户(通常与“root”⽤户关联)绕过所有命令嗅探和其他访问控制,否则将导致EACCES或EPERM错误。使⽤sg驱动程序,这样的⽤户可能仍然需要使⽤O_RDWR(⽽不是O_RDONLY)打开()设备节点以使⽤所有SCSI命令。
表2. SG_IO ioctl⽤法的open()标志
open()标志sg
明sd
笔记
st
笔记
cdrom
笔记
评论
<⽆>或
O_RDONLY 1,23,43,53,6最好添加O_NONBLOCK。对于具有可移动介质(例如磁带驱动器)的设备,取决于是否
正在访问驱动器或其介质。
O_RDONLY
| O_NONBLOCK
1,733,133建议将SCSI命令识别为从设备读取信息
O_RDWR24,8,95,8,96,8,9再次,可以更好地添加O_NONBLOCK
O_RDWR
| O_NONBLOCK
78,98,9,138,9建议在发送任意(包括特定于供应商的)SCSI命令时
<< interaction with O_EXCL>>10111211仅在确定没有其他应⽤程序可能要访问设备(或分区)时使⽤。令⼈惊讶的应⽤程序确
实“捅”了⼀些设备。
<< interaction with
O_DIRECT>>
- - >- - >要求数据传输的扇区对齐(由sg和st忽略)
备注:
1. 在后续的SG_IO ioctl调⽤中,sg驱动程序将仅允许其allow_ops数组中的SCSI命令,其他则导致errno中的EPERM(不允许操
作)。见 。
2. 如果此sg设备节点的先前open()仍保留O_EXCL,则此open()等待直到它清除。
3. 在后续的SG_IO ioctl调⽤中,块层将仅允许在drivers / block / scsi_ioctl.c⽂件的verify_command()函数中列
为“safe_for_read”的SCSI命令; 其他导致errno中的EPERM(不允许操作)。见。
4. 如果可移动介质并且不存在则产⽣ENOMEDIUM(未到介质)
5. 如果磁带不在驱动器中,则产⽣EIO(输⼊/输出错误),如果磁带“正在使⽤”,则产⽣EBUSY(资源忙)。每个st设备节点⼀次只
允许⼀个打开的⽂件描述符(尽管可以使⽤dup())。
6. 如果托盘关闭且介质不存在则产⽣ENOMEDIUM(未到介质); 如果托盘打开然后尝试关闭它,如果没有介质存在则产⽣
ENOMEDIUM
7. 如果此sg设备节点的先前open()仍保留O_EXCL,则产⽣EBUSY(资源忙)。
8. 在后续的SG_IO ioctl调⽤中,块层将允许列为“safe_for_read”或“safe_for_write”的SCSI命令。对于其他SCSI命令,⽤户需
要CAP_SYS_RAWIO功能(通常与“root”⽤户关联); 如果没有收益EPERM(不允许操作)。⾃启动以来的其他SCSI命令的第⼀个实例,向⽇志发送恼⼈的“scsi:unknown opcode”消息。
9. 如果媒体或驱动器被标记为不可写,则产⽣EROFS(只读⽂件系统)。
10. 如果sg设备节点已经有独占锁定,那么后续的打开尝试(O_EXCL)将等待,除⾮给出O_NONBLOCK,在这种情况下它会产⽣
EBUSY(资源忙)
11. 在块设备级别(它知道设备内的分区)实现。如果先前打开(O_EXCL)处于活动状态,则后续打开(O_EXCL)将产⽣EBUSY(资
源繁忙)。挂载的⽂件系统通常使⽤O_EXCL打开设备/分区; 只要使⽤SG_IO ioctl的应⽤程序也不尝试使⽤O_EXCL标志,那么它将被允许访问该设备。
12. st驱动程序不⽀持(即忽略)O_EXCL标志。但是,它只允许每个磁带设备有⼀个活动的open()这⼀事实具有类似的功能。
13. 如果磁带“正在使⽤”,则产⽣EBUSY(资源忙)。每个st设备节点⼀次只允许⼀个打开的⽂件描述符。
O_EXCL标志在sg驱动程序和块层中具有不同的效果。在sg驱动程序中,⼀旦O_EXCL保留在设备上,所有后续的open()尝试都将等待或产⽣EBUSY(⽆论它们是否尝试使⽤O_EXCL标志)。⼀旦在块层中成功打开分区/设备(使⽤sd或cdrom驱动程序),仅拒绝后续使⽤O_EXCL标志的open()尝试(使⽤EBUSY)。块层中的设备上保持的O_EXCL锁定对通过sg驱动程序访问同⼀设备没有影响(反之亦然)。
在具有可移动介质的sd或cdrom设备节点上⾸次成功打开将向设备发送PREVENT ALLOW MEDIUM REMOVAL(阻⽌)SCSI命令。如果成功,这将禁⽌随后的START STOP UNIT(弹出)SCSI命令并取消激活驱动器上的弹出按钮。在紧急情况下,可以使⽤SG_IO ioctl来阻⽌此操作,例如 实⽤程序,特别是“sdparm --command = unlock”。
open()标志O_NDELAY具有与O_NONBLOCK相同的值和含义。其他标志(如O_DIRECT,O_TRUNC和O_APPEND)对SG_IO ioctl 没有影响。
SCSI命令权限
在linux中,⽤户只需要对⽂件描述符具有读取权限即可执⾏ioctl()系统命令。在SG_IO ioctl的情况下,可以发送显然改变设备状态的SCSI命令(例如,写⼊磁盘)。因此,SG_IO ioctl的两个实现都需要多于某些命令的读取权限,特别是那些已知可以更改设备状态或具有某些未知操作的命令(例如供应商特定命令)。
这是⼀个SCSI命令表,不需要⽤户具有写权限(或者在某些情况下,CAP_SYS_RAWIO功能通常等同于“root”⽤户):
表3. SCSI命令最⼩权限要求
SCSI命令(草案)标准sg驱动程序
需要块层SG_IO
需要(除了st)
评论
BLANK(空⽩)MMC-4O_RDWR O_RDWR
CLOSE
TRACK/SESSION
(关闭跟踪/会话)
MMC-4O_RDWR O_RDWR
ERASE(擦除)MMC-4O_RDWR O_RDWR
FLUSH CACHE(检查FLUSH)SBC-
3,MMC-4
O_RDWR O_RDWR真的是SYNCHRONIZE CACHE命令
FORMAT UNIT (格式单位)SBC-
3,MMC-4
O_RDWR O_RDWR默认命令超时可能不够长
GET CONFIGURATION
(获取配置)
MMC-4O_RDWR O_RDONLY读取CD / DVD元数据
GET EVENT STATUS
NOTIFICATION
(获取事件状态通知)
MMC-4O_RDWR O_RDONLY

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。