tmscsi SCSI 设备驱动程序

用途

支持通过 SCSI 目标方式设备驱动程序进行处理器到处理器的通信。

注: 并非所有 SCSI I/O 控制器都支持此操作。

语法

#include </usr/include/sys/devinfo.h>
#include </usr/include/sys/tmscsi.h>
#include </usr/include/sys/scsi.h>

描述

小型计算机系统接口 (SCSI) 目标方式设备驱动程序提供了一个接口,以允许使用 SCSI 命令进行处理器到处理器的数据传输。 此单个设备驱动程序同时处理 SCSI 发起方和 SCSI 目标方式角色。

用户通过特殊文件 /dev/tmscsi0.xx/dev/tmscsi1.xx, ... 来访问数据传输功能。 这些都是字符特殊文件。 xx 可以是 伊姆, initiator-mode 接口或 特姆, target-mode 接口。 发起方-方式接口被调用者用于传输数据,目标-方式接口被用于接收数据。

次设备号的最小有效位向设备驱动程序指示由调用者选择的方式接口。 当次要设备号的最低有效位设置为值 1 时,将选择目标方式接口。 当最低有效位设置为值 0 时,选择发起程序-方式接口。 例如,应将 tmscsi0.im 定义为偶数次设备号以选择发起程序方式接口,并将 tmscsi0.tm 定义为奇数次设备号以选择目标方式接口。

当调用者打开发起程序方式特殊文件时,将建立逻辑路径,以允许传输数据。 用户方式调用程序会发出 写 Vx 系统调用以启动数据传输。 内核方式用户发出 fp_write弗 p_rwuio 服务调用以启动数据传输。 然后, SCSI 目标方式设备驱动程序将构建 SCSI 命令以描述传输,并将数据发送到该设备。 一旦写入口点返回,调用程序就可以访问发送缓冲区。

当调用程序打开目标方式的特殊文件时,将建立逻辑路径,从而允许接收数据。 用户方式调用程序会发出 Readvx 系统调用以启动数据接收。 内核方式调用程序发出 fp_read弗 p_rwuio 服务调用,以启动数据接收。 然后, SCSI 目标方式设备驱动程序返回为应用程序接收的数据。

SCSI 目标方式设备驱动程序允许作为发起程序方式设备通过 入口点进行访问。 目标方式设备访问是通过 入口点进行的。 通过使用两个单独的进程 (一个运行 子例程,另一个运行 子例程) ,可以同时访问 入口点。

SCSI 目标方式设备驱动程序未实现任何协议来管理数据的发送和接收,但尝试阻止应用程序过度使用接收到的数据缓冲区。 必须在调用程序中实现维护或以其他方式管理数据的通信所需的任何协议。 通过目标模式设备驱动程序发送或接收数据的唯一延迟是硬件和软件驱动程序环境所固有的那些延迟。

配置信息

在配置 tmscsi0 特殊文件时,将同时创建 tmscsi0.imtmscsi0.tm 特殊文件。 应该存在针对每个设备实例的发起方-方式/目标-方式对,即使仅使用其中一种方式也是如此。 连接的设备的目标方式 SCSI 标识应该与发起方方式 SCSI 标识相同,但在目标方式下,逻辑单元号 (LUN) 被忽略,因为主机 SCSI 适配器 0 作为 LUN 0 进行响应。

如果在连接的启动器设备上支持多个 LUN ,那么将为每个 SCSI 标识 /LUN 组合生成一对 特姆斯奇N 特殊文件 (其中 N 是设备实例)。 发起方方式的特殊文件允许同时访问关联的 SCSI 标识 /LUN 组合。 但是,一次只能打开此 SCSI 标识的目标方式特殊文件中的一个。 这是因为在主机适配器上仅支持一个 LUN 0 ,并且一次只能有一个逻辑连接主动使用此标识。 如果针对给定 SCSI 标识打开了目标方式特殊文件,那么尝试打开针对相同标识的其他目标方式特殊文件将失败。

目标方式设备驱动程序配置入口点必须仅针对发起程序方式设备号进行调用。 驱动程序配置例程根据发起程序方式数据自动为目标方式设备次编号创建配置数据。

与设备相关的子例程

目标方式的设备驱动程序支持 关闭选择ioctl 子例程。

open 子例程

子例程分配和初始化与目标或发起方设备相关的结构。 由于运行 子例程,因此不会向设备发送任何 SCSI 命令。

必须配置 SCSI 发起程序或目标方式设备,并且尚未打开该方式, 子例程才能运行。 要成功打开启动器方式设备,必须打开它的特殊文件以仅进行写操作。 要成功打开目标方式设备,必须仅打开它的特殊文件以进行读取。

错误号 全局变量的可能返回值包括:

描述
EAGAIN 锁定内核服务失败。
EBUSY 已尝试对已打开的设备实例执行打开操作。
EINVAL 尝试使用不正确的打开标志来对设备实例执行打开操作,或者尚未配置设备。
EIO 发生 I/O 错误。
ENOMEM 该 SCSI 设备缺少内存资源。

close 子例程

关闭 子例程对目标设备或发起方设备的目标设备驱动程序的本地资源进行分配。 由于运行 关闭 子例程,因此不会向设备发送任何 SCSI 命令。 错误号 全局变量的可能返回值包括:

描述
EINVAL 已尝试对未配置的设备实例执行关闭操作。
EIO 发生 I/O 错误。

read 子例程

子例程仅对目标方式设备受支持。 通过用户方式 Readvx 子例程,或内核方式 弗 p_rwuio 服务调用支持数据散射。 如果 read 子程序不成功,返回值将设置为 -1 返回值,并且 errno 全局变量将设置为来自设备驱动程序的返回值。 如果返回值不是 -1 ,则表示读取成功,返回代码表示读取的字节数。 这应该由调用者验证。 文件偏移量不适用,因此在目标方式读取时将被忽略。

SCSI 命令提供满足读请求的边界。 如果在 命令中接收到的数据超过当前 操作中请求的数据,那么会将请求的数据传递给调用者,并保留剩余数据并将其返回给此目标设备的下一个 操作。 如果在 命令中接收到的数据少于所请求的数据量,那么将为读请求传递接收到的数据,并且返回值指示已读取的字节数。

如果在发出读请求时尚未完全接收到 命令,那么该请求会阻塞并等待数据。 但是,如果目标设备是在设置了 O_NDELAY 标志的情况下打开的,那么读取不会阻塞; 它会立即返回。 如果没有可用于读请求的数据,那么 将不成功,并且 错误号 全局变量将设置为 EAGAIN。 如果数据可用,那么将返回该数据,并且返回值指示接收到的字节数。 即使针对此数据的 命令尚未结束,也会如此。

注: 如果未设置 O_NDELAY 标志,那么 子例程可以无限期地阻塞,等待数据。 由于读取的数据随时可能出现,因此设备驱动程序不会维护内部计时器来中断读取。 因此,如果需要超时功能,那么必须由调用程序来实现。

如果调用程序希望中断被阻止的 子例程,那么程序可以生成信号。 目标方式设备驱动程序接收信号,并以失败结束当前 子例程。 然后, 错误号 全局变量设置为 EINTR。 即使 命令尚未完成,读操作也会返回已接收到的任何数据。 如果接收到 命令的剩余数据,那么会将其排队,等待另一个读请求或关闭。 当目标接收到该信号并且返回当前读取时,可以启动另一个读取或关闭目标。 如果在生成信号之前,调用程序希望中断的读请求完成,那么读操作将正常完成,并且将忽略该信号。

目标方式设备驱动程序尝试在来自应用程序的请求之前对接收到的数据进行排队。 将使用预读缓冲区 (其长度由 4096 的乘积和配置数据库中的 努姆布夫斯 属性值确定) 来存储已排队的数据。 当应用程序执行 子例程时,会将排队的数据复制到应用程序数据缓冲区,并再次使预读缓冲区空间可用于接收到的数据。 如果将数据复制到调用者的数据缓冲区时发生错误,那么读操作将失败,并且 错误号 全局变量设置为 EFAULT。 如果 子例程的执行速度不够快,导致几乎所有设备的预读缓冲区都被填满,那么数据接收将延迟到应用程序再次运行 子例程为止。 当释放足够的区域时,将从设备恢复数据接收。 数据可能会被延迟,但不会丢失或被忽略。 如果几乎所有预读缓冲区都已填满,那么将保存状态信息以指示此情况。 应用程序可以选择通过 TMIOEVNT 操作来查询此状态。 如果应用程序使用可选 选择/轮询 操作,那么它可以接收此事件以及影响目标方式实例的其他事件的异步通知。

目标方式设备驱动程序仅在其读取入口点中处理接收到的数据。 在没有目标方式设备驱动程序干预的情况下处理所有其他发起方发送的 SCSI 命令。 这也意味着,目标方式设备驱动程序不会直接生成任何 SCSI 检测数据或 SCSI 状态。

所述读取入口点可选择性地与所述选择入口点结合使用,以提供在一个或多个目标设备上对接收到的数据进行异步通知的方法。

错误号 全局变量的可能返回值包括:

描述
EAGAIN 指示由于没有可用的数据,因此非分块读请求将被分块。
EFAULT 将数据复制到调用者的缓冲区时发生错误。
EINTR 被信号中断。
EINVAL 对于未配置,未打开或不是目标方式次设备号的设备实例,尝试执行
EIO 发生 I/O 错误。

write 子例程

仅发起方-方式设备驱动程序支持写入入口点。 写入口点生成单个 SCSI 命令作为对调用程序的写请求的响应。 如果写请求的长度大于主机 SCSI 适配器的最大传输长度,或者如果无法将该请求固定为单个请求,那么 请求将失败并将 错误号 全局变量设置为 EINVAL。 此设备的最大传输大小是通过对目标方式设备驱动程序发出 IOCINFO ioctl 调用而发现的。

某些支持目标方式的适配器支持通过 user_mode writev写 Vx 子例程或内核方式 弗皮 _wruio 服务调用收集写操作的数据。 会收集写缓冲区,以便按顺序将其作为单个 命令进行传输。 目标方式设备驱动程序将信息传递到 SCSI 适配器设备驱动程序以允许它执行所收集的写操作。 由于 SCSI 适配器设备驱动程序可以在软件中执行收集功能 (当硬件不直接支持数据收集时) ,因此该功能可能因内存不足或复制错误而不成功。 返回的 错误号 全局变量设置为 ENOMEMEFAULT。 由于所收集的写操作是如何处理的,因此目标方式设备驱动程序无法执行重试。 发生错误时,调用者必须重试或以其他方式恢复操作。

如果 write 操作不成功,返回值将被设置为 -1 并且 errno 全局变量将被设置为设备驱动程序的返回值。 如果返回值是 -1 以外的值,则表示 write 操作成功,返回值表示写入的字节数。 调用者应该验证发送的字节数以检查是否存在任何错误。 由于整个数据传输长度是在单个 命令中发送的,因此不应该将不等于预期总长度的返回码视为错误。 对于目标方式写操作,文件偏移量不适用且被忽略。

如果调用程序需要中断被阻止的 操作,那么应该生成一个信号。 目标方式设备驱动程序将接收信号并结束当前 操作。 正在进行的 操作失败,并且将 错误号 全局变量设置为 EINTR。 然后,调用程序可以通过发出另一个 操作 (即 ioctl 操作) 来继续执行,或者可以关闭设备。 如果在生成信号之前,调用者尝试中断的 操作已完成,那么写操作将正常完成,并且将忽略该信号。

如果收到 SCSI 忙碌响应或未收到该命令的设备响应状态,那么目标方式设备驱动程序会自动重试 (最多为 /usr/include/sys/tmscsi.h 文件中定义的值 TM_MAXRETRY 指定的尝试次数) send 命令。 缺省情况下,目标方式设备驱动程序会将每次重试尝试延迟大约两秒钟,以允许目标设备成功响应。 调用者可以通过 TMCHGIMPARM 操作来更改延迟的时间量。 如果重试次数已耗尽,并且命令仍未成功,那么写操作将失败。 调用程序可以重试 操作或执行其他适当的错误恢复。 所有其他错误条件都不会重试,而是与相应的 错误号 全局变量一起返回。

缺省情况下,目标方式设备驱动程序会生成一个超时值,该值是允许 命令完成的时间量。 如果 命令在超时值到期之前未完成,那么写操作将失败。 超时值基于所请求传输的长度 (以字节为单位) ,并按如下方式计算:

timeout_value = ((transfer_length / 65536) +1) * 
10

在该计算中, 10 是用于生成超时值的缺省缩放因子。 调用者可通过 TMCHGIMPARM 操作来定制超时值。

在写操作期间可能发生的错误之一是 SCSI 状态为 "检查"。 需要向设备发出 SCSI 请求检测 命令时才会出现检查条件错误。 这将返回设备的 SCSI 检测数据,必须检查这些数据以发现检查条件的准确原因。 为了允许目标方式设备驱动程序在处于发起方方式时与各种目标设备一起工作,该设备驱动程序不评估有关检查条件的设备检测数据。 因此,调用者负责对检测数据进行评估,以确定适当的错误恢复。 提供了 TMGETSENS 操作,以允许调用者获取检测数据。 唯一的 错误号 全局变量 ENXIO用于标识检查条件,以使调用者知道何时发出 TMGETSENS 操作。 此错误不会由 SCSI 设备驱动程序记录在系统错误日志中。 调用程序的写程序必须知道,根据 SCSI 标准, 请求检测 命令必须是设备在发生检查条件错误之后接收到的下一个命令。 如果此启动器将任何其他命令发送到设备,那么检测数据将被清除,并且错误信息将丢失。

在每个 子例程之后,目标方式设备驱动程序生成适当的返回值和 错误号 全局变量。 设备驱动程序还会将为最后一个命令保留的状态区域更新到每个设备。 在发生某些错误以及成功完成时,调用者可以选择读取此状态区域以获取命令的更详细错误状态。 可将 TMIOSTAT 操作用于此目的。 此状态所涵盖的 错误号 全局变量包括 EIOEbUSYENXIOETIMEDOUT

错误号 全局变量的其他可能返回值包括:

描述
EBUSY 检测到 SCSI 预留冲突。 请稍后重试,或者确保设备预留已结束,然后再继续。
EFAULT 这仅在数据收集期间适用。 由于发生内核服务错误, 操作未成功。
EINTR 被信号中断。
EINVAL 已尝试对未配置,未打开或不是启动器方式次设备号的设备实例执行 操作。

传输长度过长,或者无法将整个传输置顶。 请使用较小的传输长度重试命令。

EIO 发生 I/O 错误。 发生了不可重现的错误,或者重试次数已耗尽,但未成功发生不可重现的错误。 执行相应的错误恢复。
ENOCONNECT 指示发生了 SCSI 总线故障。 调用者应该通过使用允许的异步数据传输进行重试来进行响应。 这是通过在重试之前向此设备发出 TMIOASYNC 操作来完成的。 如果尝试了多次重试,那么应该仅在最后一次重试之前执行 TMIOASYNC 操作。
ENOMEM 这仅在数据收集期间适用。 由于缺少系统内存,因此 操作失败。
ENXIO 发生 SCSI 检查条件。 执行 TMGETSENS 操作以获取设备检测数据,然后执行必需的错误恢复。
ETIMEDOUT 该命令已超时。 执行相应的错误恢复。

ioctl 子例程

以下 ioctl 操作是由目标方式设备驱动程序提供的。 其中一些是特定于目标方式设备或发起方方式设备的。 所有设备都需要打开各自的设备实例才能运行操作。

操作 描述
IOCINFO 返回 /usr/include/sys/devinfo.h 文件中定义的结构。
TMCHGIMPARM 允许调用者更改特定设备实例的目标方式设备驱动程序所使用的特定参数。
TMGETSENS 运行 SCSI 请求检测 命令,并将检测数据返回给用户。
TMIOASYNC 允许对特定目标方式设备执行后续发起程序方式命令,以使用异步数据传输。
TMIOCMD 将 SCSI 命令直接发送到连接的设备。
TMIOEVNT 允许调用者查询设备驱动程序以获取某些事件的状态。
TMIORESET 将总线设备重置消息发送至连接的目标方式设备。
TMIOSTAT 允许调用者获取有关先前运行的 TMGETSENS ioctl 操作的详细状态信息。

选择入口点

选择 入口点允许调用者知道指定的事件在一个或多个目标方式设备上何时发生。 事件输入 参数允许调用者指定它希望通过一个或多个标志的按位 OR 来通知它的一个或多个条件中的哪一个。 目标方式的设备驱动程序支持以下 选择 事件:

事件 描述
POLLIN 检查接收到的数据是否可用。
POLLPRI 检查状态是否可用。
POLLSYNC 仅返回当前暂挂的事件。 不发生异步通知。

另一个事件 POLLOUT不适用,因此不受目标方式设备驱动程序的支持。

收入输出 参数指向条件性检查的结果。 设备驱动程序可以返回包含以下标志的按位 OR:

标志 描述
POLLIN 接收到的数据可用。
POLLPRI 状态为 "可用"。

chan 输入 参数用于指定信道号。 这不适用于非多路复用设备驱动程序,并且对于目标方式设备驱动程序,应该设置为 0 值。

当接收到此目标实例的任何数据时,设备驱动程序将指示 POLLIN 事件。 非阻塞 子例程 (如果随后由调用者发出) 返回数据。 对于分块 子例程,直到接收到请求的长度或 命令完成 (以先到者为准) ,读操作才会返回。

当发生异常事件时,设备驱动程序会指示 POLLPRI 事件。 要确定异常事件的原因,调用者必须向报告该 POLLPRI 事件的设备发出 TMIOEVNT 操作。

错误号 全局变量可能返回的值包括:

描述
EINVAL 指定的事件不受支持,或者设备实例未配置或者未打开。

错误记录

由目标方式设备驱动程序检测到的错误可以是下列其中一项:

  • 接收数据时发生不可重现的硬件错误
  • 发起程序命令期间发生不可重现的硬件错误
  • 未恢复的硬件错误
  • 已恢复硬件错误
  • 设备驱动程序检测到软件错误

目标方式设备驱动程序将大多数检测到的错误的错误恢复责任传递给调用者。 对于这些错误,目标方式的设备驱动程序不知道此类型的错误是永久错误还是临时错误。 这些类型的错误将记录为临时错误。

只有目标方式设备驱动程序本身可以通过重试来恢复的错误才能确定为临时或永久的。 如果在重试期间成功 (已恢复的错误) ,那么会将该错误记录为临时错误; 如果重试失败 (未恢复的错误) ,那么会将该错误记录为永久错误。 如果发生已恢复的错误,那么向调用者返回码指示成功; 如果发生未恢复的错误,那么返回码指示失败。 调用者可以选择重试该命令或操作,但对于未恢复的错误,重试成功的概率较低。