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'; 
    io_hdr.cmd_len = sizeof(turCmbBlk);
    io_hdr.mx_sb_len = sizeof(sense_b); 
    io_hdr.dxfer_direction = SG_DXFER_NONE; 
    io_hdr.cmdp = 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结构中的字段在SCSI-Generic-HOWTO 文档中有更详细的定义 。

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设备节点的文件描述符作为其第一个参数

SCSI-通用-HOWTO 文档描述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_idINT 守卫场。当前实现仅接受“(int)'S'”。如果未设置,则sg驱动程序将errno设置为ENOSYS,而块层将其设置为EINVAL
dxfer_direction(-ve)int次要数据传输方向。SG_DXFER_NONE和朋友被定义为负整数,因此sg驱动程序可以区分sg_io_hdr实例和sg_header实例。这种细微差别与SG_IO的非sg驱动程序使用无关。见下文。
cmd_lenunsigned char 将命令长度限制为255个字节。没有SCSI命令(即使OSD中的可变长度命令)这么长(还)
max_sb_lenunsigned 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相关的块设备的上限
dxferpin [*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字节的感测数据。 
timeoutunsigned int
(如果= 0)
SCSI中级等待响应的时间(以毫秒为单位)。如果该计时器在命令完成之前到期,则可以中止该命令,可以根据错误处理程序设置重置设备(以及可能在同一互连上的其他设备)。危险的东西,SG_IO ioctl无法控制(通过这个界面)究竟会发生什么。在sg驱动程序中,超时值0表示0毫秒,在块层(当前)中表示60秒。
flagsunsigned int块层SG_IO ioctl忽略该字段; sg驱动程序使用它来请求直接IO或mmap-ed传输等特殊服务。这有点面具。
pack_idin - > outINT unused(用于用户空间程序标记)
usr_ptrin - > out无效* unused(用于用户空间指针标记)
statusout无符号的字符 SCSI命令状态,零表示良好
masked_statusout无符号的字符 逻辑上:masked_status ==((状态&0x3e)>> 1)。旧的Linux SCSI子系统使用情况,已弃用。
msg_statusout无符号的字符 SCSI并行接口(SPI)消息状态(非常旧,已弃用)
sb_len_wr

unsigned

short

 通过sbp指针输出的感测数据的实际长度(以字节为单位)。
HOST_STATUS

unsigned

short

 启动器(端口)报告的错误。这些是scsi.h中的“DID_ *”错误代码
DRIVER_STATUS

unsigned

short

 位掩码:低级驱动程序(LLD)报告的错误和建议。这些是scsi.h中的“DRIVER_ *”错误代码
residINT (dxfer_len - number_of_bytes_actually_transferred)。通常仅在设备缩短DMA传输时设置。不一定是错误。较旧的LLD总是产生零。
durationunsigned int 从命令注入SCSI中间级别到调用相应的“完成”回调之间经过的毫秒数。大致是SCSI命令的持续时间(以毫秒为单位)。
infounsigned 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_NONBLOCK1,733,133建议将SCSI命令识别为从设备读取信息
O_RDWR24,8,95,8,96,8,9再次,可以更好地添加O_NONBLOCK
O_RDWR | O_NONBLOCK78,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 实用程序,特别是“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_RDWRO_RDWR 

CLOSE

TRACK/SESSION

(关闭跟踪/会话)

MMC-4O_RDWRO_RDWR 
ERASE(擦除)MMC-4O_RDWRO_RDWR 
FLUSH CACHE(检查FLUSH)SBC-3,MMC-4O_RDWRO_RDWR真的是SYNCHRONIZE CACHE命令

FORMAT UNIT

(格式单位)

SBC-3,MMC-4O_RDWRO_RDWR默认命令超时可能不够长

GET CONFIGURATION

(获取配置)

MMC-4O_RDWRO_RDONLY读取CD / DVD元数据

GET EVENT STATUS NOTIFICATION

(获取事件状态通知)

MMC-4O_RDWRO_RDONLY 

GET PERFORMANCE

(获得效能)

MMC-4O_RDWRO_RDONLY 

INQUIRY

(查询)

SPC-4O_RDONLYO_RDONLY所有SCSI设备都应响应此命令

LOAD UNLOAD MEDIUM

(加载卸载介质)

MMC-4O_RDWRO_RDWRMEDIUM可能被CD,DVD或其他任何东西取代

LOG SELECT

(日志选择)

SPC-4O_RDWRO_RDWR用于更改日志记录或清除记录的数据

LOG SENSE

(LOG 检测)

SPC-4O_RDONLYO_RDONLY用于获取记录的数据

MAINTENANCE COMMAND IN

(维护指挥)

SPC-4O_RDONLYCAP_SYS_RAWIO
 
各种“REPORT ...”命令,例如REPORT SUPPORTED OPERATION CODES

MODE SELECT (6+10)

模式选择(6 + 10)

SPC-4O_RDWRO_RDWR用于更改SCSI设备元数据

MODE SENSE (6+10)

模式感应(6 + 10)

SPC-4O_RDONLYO_RDONLY用于读取SCSI设备元数据

PAUSE RESUME

暂停恢复

MMC-4O_RDWRO_RDONLY 

PLAY AUDIO (10)

播放音频(10)

MMC-4O_RDWRO_RDONLY 

PLAY AUDIO MSF

播放音频 MSF

MMC-4O_RDWRO_RDONLY 

PLAY AUDIO TI

播放音频TI

??O_RDWRO_RDONLY操作码0x48,未分配给SPC-4中的任何规范

PLAY CD

播放CD

MMC-2O_RDWRO_RDONLY旧的,现在备用SPC-4

PREVENT ALLOW MEDIUM REMOVAL

防止允许中度去除

SPC-4,MMC-4O_RDWRO_RDWRsd,st和cdrom驱动程序在内部使用它

READ (6+10+12+16)

读(6 + 10 + 12 + 16)

SBC-3O_RDONLYO_RDONLYREAD(16)在lk2.6.11之前需要带有sg驱动程序的O_RDWR

READ BUFFER

读BUFFER

SPC-4O_RDONLYO_RDONLY 

READ BUFFER CAPACITY

读缓冲容量

MMC-4O_RDWRO_RDONLY 

READ CAPACITY(10)

读能力(10)

SBC-3,MMC-4O_RDONLYO_RDONLY 

READ CAPACITY(16)

读能力(16)

SBC-3,
MMC-4
O_RDONLYCAP_SYS_RAWIO在SERVICE ACTION IN命令中。需要大于2 TB的RAID

READ CD

读CD

MMC-4O_RDWRO_RDONLY 

READ CD MSF

读CD MSF

MMC-4O_RDWRO_RDONLY 

READ CDVD CAPACITY

读CDVD容量

SBC-3,MMC-4O_RDONLYO_RDONLY来自cdrom.h的奇怪(旧?)名称。实际上是READ CAPACITY。

READ DEFECT (10)

读缺陷(10)

SBC-3O_RDWRO_RDONLY 

READ DISC INFO

读DISC信息

MMC-4O_RDWRO_RDONLY 

READ DVD STRUCTURE

读DVD结构

MMC-4O_RDWRO_RDONLY 

READ FORMAT CAPACITIES

读格式容量

MMC-4O_RDWRO_RDONLY 

READ HEADER

读标题

MMC-2O_RDWRO_RDONLY 

READ LONG (10)

SBC-3O_RDONLYO_RDONLY但不读长(16)

READ SUB-CHANNEL

读子通道

MMC-4O_RDWRO_RDONLY 

READ TOC/PMA/ATIP

读TOC / PMA / ATIP

MMC-4O_RDWRO_RDONLY 
READ TRACK(RZONE)INFOMMC-4O_RDWRO_RDONLY在MMC-4中称为READ TRACK INFO

RECEIVE DIAGNOSTIC

接受诊断

SPC-4O_RDONLYCAP_SYS_RAWIOSES命令集大量使用此命令。只能通过sg设备节点访问SES设备

REPAIR (RZONE) TRACK

修理(RZONE)轨道

MMC-4O_RDWRO_RDWR 

REPORT KEY

报告键

MMC-4O_RDWRO_RDONLY 

REPORT LUNS

报告LUN

SPC-4O_RDONLYCAP_SYS_RAWIO自SPC-3起必须遵守

REQUEST SENSE

请求扫描

SPC-4O_RDONLYO_RDONLY除了那些因自动感应而流离失所的人以外的用途
RESERVE(RZONE)TRACKMMC-4O_RDWRO_RDWR 
SCANMMC-4O_RDWRO_RDONLY 
SEEKMMC-4O_RDWRO_RDONLY 
SEND CUE SHEETMMC-4O_RDWRO_RDWR 
SEND DVD STRUCTUREMMC-4O_RDWRO_RDWR 
[SEND EVENT]MMC-2 O_RDWRcdrom.h关联操作码0xa2但MMC-2使用操作码0x5d ??
SEND KEYMMC-4O_RDWRO_RDWR 
SEND OPC INFORMATIONMMC-4O_RDWRO_RDWR 
SERVICE ACTION INSPC-4,SBC-3O_RDONLYCAP_SYS_RAWIO在此处阅读容量(16)服务操作
SET CD SPEEDMMC-4O_RDWRO_RDWRcdrom.h调用此SET SPEED
SET STREAMINGMMC-4O_RDWRO_RDWR 
START STOP UNITSBC-3,MMC-4O_RDWRO_RDONLY
STOP PLAY/SCANMMC-4O_RDWRO_RDONLY 
SYNCHRONIZE CACHESBC-3,MMC-4O_RDWRO_RDWRcdrom.h调用此FLUSH CACHE
TEST UNIT READYSPC-4O_RDONLYO_RDONLY所有SCSI设备都应响应此命令
VERIFY (10+16)SBC-3,MMC-4O_RDWRO_RDONLY 

WRITE (6+10+12+16)

写(6 + 10 + 12 + 16)

SBC-3O_RDWRO_RDWR 
WRITE LONG (10+16)SBC-3O_RDWRO_RDWR 
WRITE VERIFY (10+16)SBC-3,MMC-4O_RDWRO_RDWR只有WRITE VERIFY(10)在MMC-4中


没有为sg驱动程序提及的任何其他SCSI命令(操作码)需要O_RDWR。对于块层SG_IO ioctl未提及的任何其他SCSI命令(操作码)需要具有CAP_SYS_RAWIO能力的用户。在st设备节点上的所有“块”SG_IO ioctl调用都需要具有CAP_SYS_RAWIO能力的用户。如果用户没有足够的权限通过SG_IO ioctl执行SCSI命令,则系统调用失败(即没有发送SCSI命令),并且errno设置为EPERM(不允许操作)。

sg驱动程序和块层SG_IO代码都使用内部表来强制执行上表中显示的权限(allow_ops和cmd_type分别为[safe_for_read和safe_for_write])。该技术不能很好地扩展,因为更高级的命令集(例如OSD)使用服务动作(和一个操作码:在OSD的情况下为0x7f)。在命令集之间的操作码使用中也可能存在重叠,例如在SBC,MMC和SSC之间。

来自用户进程的CAP_SYS_RAWIO

虽然根进程通常具有CAP_SYS_RAWIO,但在用户ID(即非root)下运行的进程通常不会。因此,非root进程可能无法使用SG_IO发送需要CAP_SYS_RAWIO的SCSI命令。即使设备节点文件的权限位允许读取或写入访问,也可能发生这种情况,用户进程在使用SG_IO时将收到EPERM。

默认情况下,将功能分配给其他进程(CAP_SETPCAP)的功能仅限于极少数进程,例如某些内核线程。更改此默认值需要更改并重新编译内核。

由根进程分叉并稍后调用setuid的进程将失去父根进程(以及setuid之前的子进程)具有的CAP_SYS_RAWIO功能。但是,子进程可以在允许的集合中保留根进程的功能,并

在fork()之后调用setuid:/ * ...之后将其提升,仍然以root身份运行... * / 
prctl(PR_SET_KEEPCAPS, 1,0,0,0); 
setuid的(...); 
cap_set_proc(cap_from_text( “CAP_SYS_RAWIO + EP”)); 

这样,具有父根进程的用户进程可以“返回”所需的功能,以通过SG_IO直接将SCSI命令发送到设备。

上述技术可能用于以root权限(大多数是)启动的守护进程,然后在fork()之后更改为另一个用户。对于作者而言,在某些或所有SCSI命令(例如与sd和st驱动程序相关联的节点)上需要CAP_SYS_RAWIO的设备节点上使用SG_IO ioctl的实用程序如何使用上述技术并不明显。
 

SG_IO和st驱动程序

为了实现其用户空间API,st驱动程序必须维护有关读磁头相对于磁带结构元素的位置的信息(文件标记,磁带的开头,数据的结尾)。由于流设备SCSI命令没有地址,因此st驱动程序必须知道已发送了哪些命令。在读取时,当读取失败并且提取感测数据时,会注意到文件标记。如果SG_IO与磁带命令混合,则st驱动程序可能会丢失信息(它不会查看SG_IO命令和结果)。因此,st驱动程序可能无法实现用户期望的语义。如果用户接受此信息或知道何时使用SG_IO不会导致信息丢失,那么使用SG_IO就可以了。

因此,不建议将st驱动程序读取,写入和ioctl命令与通过SG_IO发送的SCSI命令混合,以改变磁带的状态。无论是通过st或sg设备节点发送SG_IO SCSI命令,这都适用。
 

每个命令的最大传输大小

单个SCSI命令可以传输的最大数据量通常是一个问题。各种SCSI命令集(例如,用于磁盘READ和WRITE的SBC-3,用于磁带读取和写入的SSC-3以及用于READ + WRITE BUFFER的SPC-4)允许非常大的数据传输大小,但Linux并不是那么容易。主机总线适配器(HBA)可能具有传输大小限制,传输和SCSI设备本身也是如此。在后一种情况下,SBC-3定义了“块限制”重要产品数据(VPD),而SSC具有READ BLOCK LIMITS SCSI命令。SBC-3的可选块限制VPD页面包含最大和最佳计数。在作者看来,后一种区别非常重要:块susbsystem应该尝试使用最佳大小,而通过用户时应该只限制最大大小。此外,如果传递用户超过SCSI设备强加的最大传输大小,则设备可以报告错误。有一个潜在的假设,即使用传递接口的应用程序知道它们正在做什么,或者至少比各种内核系统知道更多。另一方面,内核有责任分配关键的共享资源,如内存。

在过去,Linux使用单个“足够大”的内存块作为大数据传输的源或目标。然后,分散 - 收集列表添加到中断的位置转换为较小的(通常为“页面”大小(i386体系结构上为4 KB))块,这使得内核更容易进行内存管理。现在,在lk 2.6系列中,单块内存选项正在逐步淘汰。

Linux SCSI子系统通过其SCSI_MAX_PHYS_SEGMENTS定义对分散收集列表施加128个元素限制。linux SCSI子系统分配各种内存池的方式,SCSI_MAX_PHYS_SEGMENTS可以增加到256.与每种类型的HBA相关联,通常有一个低级驱动程序(LLD)。每个LLD都可以使用scsi_host_template :: sg_tablesize字段进一步限制元素的最大数量。在lk 2.6.16之前,sg和st驱动程序仅使用.sg_tablesize字段,因为lk 2.6.16这些驱动程序也受SCSI_MAX_PHYS_SEGMENTS约束。这导致最大转移尺寸可能减半。许多LLD将.sg_tablesize字段设置为SG_ALL(为255),但除非HBA硬件具有约束,否则它们也可以将该字段设置为256。

可以将用户空间存储器分配为来自HBA的DMA传输的源和/或目的地(即,直接IO)。即使用户空间使用单个malloc()分配了大量内存,HBA DMA元素通常也具有不同的内存视图。该视图可能包含许多“页面”大小不连续的部分。这具有消耗或可能耗尽散射 - 聚集元素的效果。

sg驱动程序尝试使用每个元素构建分散集合列表,其中SG_SCATTER_SZ字节大。这个定义可以在include / scsi / sg.h中找到,并且已经设置为32 KB多年。这是i386架构上页面大小(4 KB)的8倍。一些需要非常大的传输的用户会增加这个定义(并且最好保持2的幂)。但是,由于lk 2.6.16的另一个限制发挥作用:MAX_SEGMENT_SIZE定义设置为64 KB。MAX_SEGMENT_SIZE是默认值,可以由LLD调用blk_queue_max_segment_size()覆盖。

在lk 2.6.16中,即使使用sg(和st)驱动程序,另外两个LLD参数也会起作用。这些是scsi_host_template :: max_sectors和scsi_host_template :: use_clustering。  

LLD中的.max_sectors设置是单个SCSI命令的分散收集列表(用于数据传输)中允许的最大512字节扇区数。是的,当尝试发送SCSI WRITE BUFFER命令来上传固件时,这是一个奇怪的限制。Sysfs使得LLD的.max_sectors设置在/ sys / block / sd <x> / queue / max_hw_sectors_kb中可见(转换为千字节)。LLD的.max_sector中的最大允许值似乎是65535(十六进制为0xffff)。假设已经克服了其他限制,这将最大传输大小限制为(32 * 1024 * 1024 - 512)字节。[65535扇区限制是因为Scsi_Host :: max_sectors的类型为“unsigned short”。希望将来这种类型扩展为“int”(或删除)。]

.use_clustering字段应设置为ENABLE_CLUSTERING。如果不是,则块子系统重建它从具有页面大小(例如4KB)元素的sg驱动程序获得的分散收集列表。[实际上是这样做,但是当设置了ENABLE_CLUSTERING时,它会再次合并它们!]
 

结论

在某些情况下,通过SG_IO ioctl发送命令可能会干扰更高级别驱动程序对设备的使用。SG_IO ioctl的用户应该知道他们正在使用强大但低级别的工具,并相应地编写代码。这方面的一个例子是在磁盘上执行自检的实用程序:如果计算机有可能当时在该磁盘上使用文件系统,则“背景”自检应优先于“前台”自检。即使是短前景自检也可能需要长达两分钟才能锁定文件系统。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值