SCSI 接口指南

作者:

James Bottomley

作者:

Rob Landley

介绍

协议与总线

很久以前,小型计算机系统接口定义了一种并行 I/O 总线和一种数据协议,用于将各种外围设备(磁盘驱动器、磁带驱动器、调制解调器、打印机、扫描仪、光驱、测试设备和医疗设备)连接到主机。

尽管旧的并行(快速/宽/超宽)SCSI 总线已基本不再使用,但 SCSI 命令集的使用范围比以往任何时候都更广泛,可以通过多种不同的总线与设备通信。

SCSI 协议是一种大端对等基于数据包的协议。 SCSI 命令长度为 6、10、12 或 16 个字节,通常后跟关联的数据有效负载。

SCSI 命令几乎可以通过任何类型的总线传输,并且是连接到 USB、SATA、SAS、光纤通道、FireWire 和 ATAPI 设备的存储设备的默认协议。 SCSI 数据包也通常通过 InfiniBand、TCP/IP (iSCSI),甚至 并行端口 进行交换。

Linux SCSI 子系统的设计

SCSI 子系统使用三层设计,包括上层、中层和下层。 每个涉及 SCSI 子系统的操作(例如从磁盘读取扇区)都会使用每一层的驱动程序:一个上层驱动程序、一个下层驱动程序和 SCSI 中间层。

SCSI 上层以 I/O 和 ioctl() 的块和字符设备节点的形式提供用户空间和内核之间的接口。 SCSI 下层包含特定硬件设备的驱动程序。

两者之间是 SCSI 中间层,类似于网络路由层,例如 IPv4 堆栈。 SCSI 中间层在上面的 /dev 节点和下面的相应设备之间路由基于数据包的数据协议。 它管理命令队列,提供错误处理和电源管理功能,并响应 ioctl() 请求。

SCSI 上层

上层通过提供设备节点来支持用户内核接口。

sd (SCSI 磁盘)

sd (sd_mod.o)

sr (SCSI CD-ROM)

sr (sr_mod.o)

st (SCSI 磁带)

st (st.o)

sg (SCSI 通用)

sg (sg.o)

ch (SCSI 媒体更换器)

ch (ch.c)

SCSI 中间层

SCSI 中间层实现

include/scsi/scsi_device.h

struct scsi_vpd

SCSI 重要产品数据

定义:

struct scsi_vpd {
    struct rcu_head rcu;
    int len;
    unsigned char   data[];
};

成员

rcu

用于 kfree_rcu()

len

**data** 的长度(以字节为单位)。

data

VPD 数据,如各种 T10 SCSI 标准文档中所定义。

shost_for_each_device

shost_for_each_device (sdev, shost)

迭代主机的所有设备

参数

sdev

用作游标的 struct scsi_device

shost

要迭代的 struct scsi_host

描述

迭代器,返回连接到 **shost** 的每个设备。 此循环获取每个设备上的引用并在末尾释放它。 如果你跳出循环,则必须调用 scsi_device_put(sdev)。

__shost_for_each_device

__shost_for_each_device (sdev, shost)

迭代主机的所有设备(已解锁)

参数

sdev

用作游标的 struct scsi_device

shost

要迭代的 struct scsi_host

描述

迭代器,返回连接到 **shost** 的每个设备。 它不获取 scsi_device 上的引用,因此整个循环必须受 shost->host_lock 保护。

注意

使用它的唯一原因是需要访问中断上下文中的设备列表。 否则,你真的想要使用 shost_for_each_device。

int scsi_device_supports_vpd(struct scsi_device *sdev)

测试设备是否支持 VPD 页面

参数

struct scsi_device *sdev

要测试的 struct scsi_device

描述

如果设置了“try_vpd_pages”标志,则它优先。 否则,如果 SCSI 级别至少为 SPC-3 并且未设置“skip_vpd_pages”,我们将假定支持 VPD 页面。

drivers/scsi/scsi.c

SCSI 中间层的主文件。

int scsi_change_queue_depth(struct scsi_device *sdev, int depth)

更改设备的队列深度

参数

struct scsi_device *sdev

有问题的 SCSI 设备

int depth

允许排队到驱动程序的命令数

描述

设置设备队列深度并返回新值。

int scsi_track_queue_full(struct scsi_device *sdev, int depth)

跟踪 QUEUE_FULL 事件以调整队列深度

参数

struct scsi_device *sdev

有问题的 SCSI 设备

int depth

此设备上未完成的 SCSI 命令的当前数量,不包括返回为 QUEUE_FULL 的命令。

描述

此函数将跟踪特定的

SCSI 设备上的连续 QUEUE_FULL 事件,以确定是否以及何时需要调整设备上的队列深度。

锁定状态:条目上未持有任何锁

返回

0 - 不需要更改,>0 - 将队列深度调整为此新深度,
-1 - 使用 host->cmd_per_lun 作为未标记的命令深度回退到未标记的操作

注释

底层驱动程序可以随时调用它,我们将执行

“正确的事”。 我们是中断上下文安全的。

“正确的事情”。我们是中断上下文安全的。

int scsi_get_vpd_page(struct scsi_device *sdev, u8 page, unsigned char *buf, int buf_len)

从 SCSI 设备获取重要产品数据

参数

struct scsi_device *sdev

要询问的设备

u8 page

要返回的 Vital Product Data

unsigned char *buf

存储 VPD 的位置

int buf_len

VPD 缓冲区区域中的字节数

描述

SCSI 设备可以选择性地提供 Vital Product Data。 每个 VPD“页面”都在相应的 SCSI 文档(例如 SPC、SBC)中定义。 如果设备支持此 VPD 页面,则此例程将使用该页面的数据填充 **buf** 并返回 0。 如果不支持 VPD 页面或无法检索其内容,则返回 -EINVAL。

int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer, unsigned int len, unsigned char opcode, unsigned short sa)

查明是否支持给定命令

参数

struct scsi_device *sdev

要查询的 scsi 设备

unsigned char *buffer

暂存缓冲区(长度必须至少为 20 字节)

unsigned int len

缓冲区长度

unsigned char opcode

要查找的命令的操作码

unsigned short sa

要查找的命令的服务操作

描述

使用 REPORT SUPPORTED OPERATION CODES 检查对使用 **opcode** 和 **sa** 标识的命令的支持。 如果命令没有服务操作,则 **sa** 必须为 0。 如果 RSOC 失败,则返回 -EINVAL;如果不支持该命令,则返回 0;如果设备声明支持该命令,则返回 1。

int scsi_device_get(struct scsi_device *sdev)

获取对 scsi_device 的额外引用

参数

struct scsi_device *sdev

要获取引用的设备

描述

获取对 scsi_device 的引用并递增底层 LLDD 模块的使用计数。 调用此函数时,你必须持有父 Scsi_Host 的 host_lock 或已经具有引用。

如果设备被删除或取消,或者 LLD 模块正在卸载过程中,则此操作将失败。

void scsi_device_put(struct scsi_device *sdev)

释放对 scsi_device 的引用

参数

struct scsi_device *sdev

要释放引用的设备。

描述

释放对 scsi_device 的引用并递减底层 LLDD 模块的使用计数。 一旦最后一个用户消失,该设备将被释放。

void starget_for_each_device(struct scsi_target *starget, void *data, void (*fn)(struct scsi_device*, void*))

帮助程序遍历目标的所有设备

参数

struct scsi_target *starget

我们要迭代的设备的目标。

void *data

传递给每个函数调用的不透明数据。

void (*fn)(struct scsi_device *, void *)

要在每个设备上调用的函数

描述

这将遍历 **starget** 的每个设备。 这些设备具有一个引用,必须通过 scsi_host_put 在跳出循环时释放该引用。

void __starget_for_each_device(struct scsi_target *starget, void *data, void (*fn)(struct scsi_device*, void*))

遍历目标的所有设备的助手函数 (未锁定)

参数

struct scsi_target *starget

我们要迭代的设备的目标。

void *data

回调函数 fn() 的参数

void (*fn)(struct scsi_device *, void *)

为每个设备调用的回调函数

描述

此函数遍历 starget 的每个设备。它_不_获取对 scsi_device 的引用,因此整个循环必须由 shost->host_lock 保护。

注意

驱动程序可能想要使用此函数的唯一原因是,它们需要在 irq 上下文中访问设备列表。否则,您真的想使用 starget_for_each_device 代替。

struct scsi_device *__scsi_device_lookup_by_target(struct scsi_target *starget, u64 lun)

查找给定目标的设备 (未锁定)

参数

struct scsi_target *starget

SCSI 目标指针

u64 lun

SCSI 逻辑单元号

描述

查找具有指定 lun 的 scsi_device 以用于给定的 starget。返回的 scsi_device 没有额外的引用。您必须在此调用以及对返回的 scsi_device 的任何访问中持有主机的 host_lock。状态为 SDEV_DEL 的 scsi_device 将被跳过。

注意

驱动程序应该使用此函数的唯一原因是,它们需要在 irq 上下文中访问设备列表。否则,您真的想使用 scsi_device_lookup_by_target 代替。

struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *starget, u64 lun)

查找给定目标的设备

参数

struct scsi_target *starget

SCSI 目标指针

u64 lun

SCSI 逻辑单元号

描述

查找具有指定 lun 的 scsi_device 以用于给定的 starget。返回的 scsi_device 有一个额外的引用,一旦您完成使用它,就需要使用 scsi_device_put 释放该引用。

struct scsi_device *__scsi_device_lookup(struct Scsi_Host *shost, uint channel, uint id, u64 lun)

查找给定主机的设备 (未锁定)

参数

struct Scsi_Host *shost

SCSI 主机指针

uint channel

SCSI 通道 (如果只有一个通道,则为零)

uint id

SCSI 目标编号 (物理单元编号)

u64 lun

SCSI 逻辑单元号

描述

查找具有指定 channelidlun 的 scsi_device 以用于给定的主机。返回的 scsi_device 没有额外的引用。您必须在此调用以及对返回的 scsi_device 的任何访问中持有主机的 host_lock。

注意

驱动程序可能想要使用此函数的唯一原因是,它们需要在 irq 上下文中访问设备列表。否则,您真的想使用 scsi_device_lookup 代替。

struct scsi_device *scsi_device_lookup(struct Scsi_Host *shost, uint channel, uint id, u64 lun)

查找给定主机的设备

参数

struct Scsi_Host *shost

SCSI 主机指针

uint channel

SCSI 通道 (如果只有一个通道,则为零)

uint id

SCSI 目标编号 (物理单元编号)

u64 lun

SCSI 逻辑单元号

描述

查找具有指定 channelidlun 的 scsi_device 以用于给定的主机。返回的 scsi_device 有一个额外的引用,一旦您完成使用它,就需要使用 scsi_device_put 释放该引用。

drivers/scsi/scsicam.c

SCSI 通用访问方法支持函数,用于 HDIO_GETGEO 等。

unsigned char *scsi_bios_ptable(struct block_device *dev)

从设备的第一个扇区中读取 PC 分区表。

参数

struct block_device *dev

来自此设备

描述

从设备读取第一个扇区并返回从偏移量 0x1be 开始的 0x42 字节。

从偏移量 0x1be 开始。

返回

kmalloc(GFP_KERNEL) 内存中的分区表,如果出错,则为 NULL。

bool scsi_partsize(struct block_device *bdev, sector_t capacity, int geom[3])

从 PC 分区表解析柱面/磁头/扇区

参数

struct block_device *bdev

要解析的块设备

sector_t capacity

磁盘的大小,以扇区为单位

int geom[3]

以 [hds, cylinders, sectors] 形式输出

描述

确定用于创建分区表的 BIOS 映射/几何图形,并将结果存储在 geom 中。

返回

失败时为 false,成功时为 true

int scsicam_bios_param(struct block_device *bdev, sector_t capacity, int *ip)

确定磁盘的柱面/磁头/扇区几何图形。

参数

struct block_device *bdev

哪个设备

sector_t capacity

磁盘的大小,以扇区为单位

int *ip

返回值: ip[0]=heads, ip[1]=sectors, ip[2]=cylinders

描述

确定用于 SCSI-CAM 系统中的驱动器的 BIOS 映射/几何图形,并将结果存储在 ip 中,如 HDIO_GETGEO ioctl() 所需。

SCSI-CAM 系统,根据 HDIO_GETGEO ioctl() 的要求,将结果存储在 ip 中。

返回

失败时为 -1,成功时为 0。

drivers/scsi/scsi_error.c

常见的 SCSI 错误/超时处理例程。

void scsi_schedule_eh(struct Scsi_Host *shost)

为 SCSI 主机安排 EH

参数

struct Scsi_Host *shost

要在其上调用错误处理的 SCSI 主机。

描述

在没有 scmd 的情况下安排 SCSI EH。

int scsi_block_when_processing_errors(struct scsi_device *sdev)

防止命令排队。

参数

struct scsi_device *sdev

我们正在执行恢复的设备。

描述

我们阻塞直到主机退出错误恢复,然后检查主机或设备是否脱机。

返回值

当设备因错误恢复而脱机时,返回 0。1 OK 继续。

enum scsi_disposition scsi_check_sense(struct scsi_cmnd *scmd)

检查 scsi cmd 感知

参数

struct scsi_cmnd *scmd

要检查感知的 Cmd。

描述

返回值

SUCCESS 或 FAILED 或 NEEDS_RETRY 或 ADD_TO_MLQUEUE

底层驱动程序可以随时调用它,我们将执行

当检测到延迟错误时,当前命令尚未执行,需要重试。

void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses, unsigned char *cmnd, int cmnd_size, unsigned sense_bytes)

保存 scsi 命令信息作为错误恢复的一部分

参数

struct scsi_cmnd *scmd

要劫持的 SCSI 命令结构

struct scsi_eh_save *ses

用于保存还原信息的结构

unsigned char *cmnd

要发送的 CDB。如果不需要新的 cmnd,则可以为 NULL

int cmnd_size

cmnd 的大小(以字节为单位)(必须 <= MAX_COMMAND_SIZE)

unsigned sense_bytes

要复制的感知数据的大小。或 0(如果 != 0,则忽略 cmnd

描述

此函数用于在重新执行之前保存 scsi 命令信息,作为错误恢复过程的一部分。如果 sense_bytes 为 0,则发送的命令必须是不传输任何数据的命令。如果 sense_bytes != 0,则忽略 cmnd,并且此函数会设置 REQUEST_SENSE 命令和 cmnd 缓冲区以将 sense_bytes 读取到 scmd->sense_buffer 中。

void scsi_eh_restore_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses)

作为错误恢复的一部分还原 scsi 命令信息

参数

struct scsi_cmnd* scmd

要还原的 SCSI 命令结构

struct scsi_eh_save *ses

来自对 scsi_eh_prep_cmnd 的相应调用的已保存信息

描述

撤消上述 scsi_eh_prep_cmnd() 所做的任何损害。

void scsi_eh_finish_cmd(struct scsi_cmnd *scmd, struct list_head *done_q)

处理 eh 完成的 cmd。

参数

struct scsi_cmnd *scmd

eh 已完成的原始 SCSI cmd。

struct list_head *done_q

已处理命令的队列。

底层驱动程序可以随时调用它,我们将执行

我们不想在使用我们仍在处理错误时使用正常的命令完成 - 它可能会导致其他命令排队,这将扰乱我们正在做的事情。因此,我们真的想保留一个待处理命令的列表以进行最终完成,并且一旦我们准备好离开错误处理,我们就会真正处理完成。

int scsi_eh_get_sense(struct list_head *work_q, struct list_head *done_q)

获取设备感知数据。

参数

struct list_head *work_q

要处理的命令队列。

struct list_head *done_q

已处理命令的队列。

描述

看看我们是否需要请求感知信息。如果是这样,现在就获取它,以便我们更好地了解该怎么做。

底层驱动程序可以随时调用它,我们将执行

这会产生不幸的副作用,即如果 shost 适配器不自动请求感知信息,我们最终会在请求它之前将其关闭。

所有驱动程序现在都应该在内部请求感知信息,所以现在我不得不说,如果你最终进入这里,那你就倒霉了。

XXX: 从长远来看,此代码应该消失,但这需要首先审核所有 LLDD。

首先是所有 LLDD。

void scsi_eh_ready_devs(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q)

检查设备就绪状态,如果未就绪则恢复。

参数

struct Scsi_Host *shost

要恢复的主机。

struct list_head *work_q

待处理命令的 list_head

struct list_head *done_q

已处理命令的 list_head

void scsi_eh_flush_done_q(struct list_head *done_q)

完成已处理的命令或重试它们。

参数

struct list_head *done_q

已处理命令的 list_head。

void scsi_report_bus_reset(struct Scsi_Host *shost, int channel)

报告观察到的总线重置

参数

struct Scsi_Host *shost

有问题的 Host

int channel

观察到重置的通道。

描述

底层驱动程序使用的实用程序函数,用于报告它们已在正在处理的总线上观察到总线重置。

锁定状态: 必须持有主机锁。

返回

底层驱动程序可以随时调用它,我们将执行

只有当重置来自未知位置时,才需要调用此函数

由中间层本身发起的重置不需要调用此函数,但这样做应该无害。

此函数的主要目的是确保正确处理 CHECK_CONDITION。

void scsi_report_device_reset(struct Scsi_Host *shost, int channel, int target)

报告观察到的设备重置

参数

struct Scsi_Host *shost

有问题的 Host

int channel

观察到重置的通道

int target

观察到重置的目标

描述

底层驱动程序使用的实用程序函数,用于报告它们已在正在处理的设备上观察到设备重置。

锁定状态:必须持有主机锁

返回

底层驱动程序可以随时调用它,我们将执行

只有当重置来自未知位置时,才需要调用此函数

由中间层本身发起的重置不需要调用此函数,但这样做应该无害。

此函数的主要目的是确保正确处理 CHECK_CONDITION。

bool scsi_get_sense_info_fld(const u8 *sense_buffer, int sb_len, u64 *info_out)

从 sense 数据(固定格式或描述符格式)获取信息字段

参数

const u8 *sense_buffer

sense 数据的字节数组

int sb_len

sense_buffer 中的有效字节数

u64 *info_out

指向 64 位整数的指针,如果找到 8 字节或 4 字节的信息字段,则将放置在此处。

描述

返回值

如果找到信息字段,则为 true;如果未找到,则为 false。

drivers/scsi/scsi_devinfo.c

管理 scsi_dev_info_list,它跟踪黑名单和白名单设备。

int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model, char *strflags, blist_flags_t flags, enum scsi_devinfo_key key)

添加一个 dev_info 列表条目。

参数

int compatible

如果为 true,则以 null 结尾短字符串。否则,使用空格填充。

char *vendor

供应商字符串

char *model

型号(产品)字符串

char *strflags

整数字符串

blist_flags_t flags

如果 strflags 为 NULL,则使用此标志值

enum scsi_devinfo_key key

指定要使用的列表

描述

vendormodelstrflagsflagkey 指定的列表中创建并添加一个 dev_info 条目。如果 compatible,则添加到列表的尾部,不使用空格填充,并设置 devinfo->compatible。 scsi_static_device_list 条目使用 compatible 1 和 clfags NULL 添加。

返回

0 OK,失败时返回 -error。

blist_flags_t scsi_get_device_flags_keyed(struct scsi_device *sdev, const unsigned char *vendor, const unsigned char *model, enum scsi_devinfo_key key)

从动态设备列表中获取设备特定标志

参数

struct scsi_device *sdev

要获取标志的 scsi_device

const unsigned char *vendor

供应商名称

const unsigned char *model

型号名称

enum scsi_devinfo_key key

要查找的列表

描述

搜索 key 指定的 scsi_dev_info_list 中与 vendormodel 匹配的条目,如果找到,则返回匹配的标志值,否则返回主机或全局默认设置。 在扫描时调用。

int scsi_dev_info_add_list(enum scsi_devinfo_key key, const char *name)

添加新的 devinfo 列表

参数

enum scsi_devinfo_key key

要添加的列表的键

const char *name

要添加的列表的名称(用于 /proc/scsi/device_info)

描述

添加请求的列表,成功时返回零,如果该键已注册到列表,则返回 -EEXIST,否则返回其他错误。

int scsi_dev_info_remove_list(enum scsi_devinfo_key key)

销毁添加的 devinfo 列表

参数

enum scsi_devinfo_key key

要销毁的列表的键

描述

首先迭代整个列表,释放所有值,然后释放列表本身。 成功时返回 0,如果找不到该键,则返回 -EINVAL。

drivers/scsi/scsi_ioctl.c

处理 SCSI 设备的 ioctl() 调用。

int scsi_set_medium_removal(struct scsi_device *sdev, char state)

发送命令以允许或阻止介质移除

参数

struct scsi_device *sdev

目标 scsi 设备

char state

要设置的移除状态(阻止或允许)

返回

  • 如果 sdev 不可移动或不可锁定或成功,则返回 0

  • 如果 > 0,则非 0 是 SCSI 结果代码;如果 < 0,则为内核错误代码。

  • 成功时,将 sdev->locked 设置为新状态。

bool scsi_cmd_allowed(unsigned char *cmd, bool open_for_write)

检查是否允许给定命令。

参数

unsigned char *cmd

要检查的 SCSI 命令

bool open_for_write

是否为写入打开文件/块设备?

描述

只有一部分命令允许非特权用户使用。 用于格式化介质、更新固件等的命令是不允许的。

返回

如果允许 cmd,则返回 true,否则返回 false

int scsi_ioctl(struct scsi_device *sdev, bool open_for_write, int cmd, void __user *arg)

将 ioctl 分派到 scsi 设备

参数

struct scsi_device *sdev

接收 ioctl 的 scsi 设备

bool open_for_write

是否为写入打开文件/块设备?

int cmd

是哪个 ioctl

void __user *arg

与 ioctl 相关的数据

描述

scsi_ioctl() 函数与大多数 ioctl 不同之处在于,它不采用主/次编号作为 dev 字段。 而是,它采用指向 struct scsi_device 的指针。

返回

cmd 而异

int scsi_ioctl_block_when_processing_errors(struct scsi_device *sdev, int cmd, bool ndelay)

防止命令排队

参数

struct scsi_device *sdev

目标 scsi 设备

int cmd

是哪个 ioctl

bool ndelay

无延迟(非阻塞)

描述

即使设备未完全运行,我们也可以处理重置。

返回

成功时返回 0,<0 表示错误代码。

drivers/scsi/scsi_lib.c

SCSI 队列库。

void scsi_failures_reset_retries(struct scsi_failures *failures)

将所有故障重置为零

参数

struct scsi_failures *failures

设置了特定故障模式的 struct scsi_failures

int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd, blk_opf_t opf, void *buffer, unsigned int bufflen, int timeout, int ml_retries, const struct scsi_exec_args *args)

插入请求并等待结果

参数

struct scsi_device *sdev

scsi_device

const unsigned char *cmd

scsi 命令

blk_opf_t opf

块层请求 cmd_flags

void *buffer

数据缓冲区

unsigned int bufflen

缓冲区长度

int timeout

以 HZ 为单位的请求超时

int ml_retries

SCSI 中间层将重试请求的次数

const struct scsi_exec_args *args

可选参数。 有关字段说明,请参阅结构定义

描述

如果执行了命令,则返回 scsi_cmnd 结果字段;如果未执行命令,则返回负 Linux 错误代码。

blk_status_t scsi_alloc_sgtables(struct scsi_cmnd *cmd)

分配和初始化数据和完整性散列表

参数

struct scsi_cmnd *cmd

要初始化的 SCSI 命令数据结构。

描述

初始化 cmd->sdb,如果为 cmd 启用了数据完整性,则还初始化 cmd->prot_sdb

返回

  • BLK_STS_OK - 成功时

  • BLK_STS_RESOURCE - 如果故障可以重试

  • BLK_STS_IOERR - 如果故障是致命的

struct request *scsi_alloc_request(struct request_queue *q, blk_opf_t opf, blk_mq_req_flags_t flags)

分配块请求并部分初始化其 scsi_cmnd

参数

struct request_queue *q

设备的请求队列

blk_opf_t opf

请求操作码

blk_mq_req_flags_t flags

块层分配标志

返回

成功时返回 struct request 指针,失败时返回 NULL

struct scsi_device *scsi_device_from_queue(struct request_queue *q)

返回与 request_queue 关联的 sdev

参数

struct request_queue *q

要从中返回 sdev 的请求队列

描述

返回与请求队列关联的 sdev,如果 request_queue 未引用 SCSI 设备,则返回 NULL。

void scsi_block_requests(struct Scsi_Host *shost)

底层驱动程序使用的实用程序函数,用于防止将更多命令排队到设备。

参数

struct Scsi_Host *shost

相关主机

描述

除了底层驱动程序调用 scsi_unblock_requests() 之外,没有任何计时器或其他方法可以解除对请求的阻止。

void scsi_unblock_requests(struct Scsi_Host *shost)

底层驱动程序使用的实用函数,用于允许将更多命令排队到设备。

参数

struct Scsi_Host *shost

相关主机

描述

除了底层驱动程序调用 scsi_unblock_requests() 之外,没有任何计时器或其他方法可以解除对请求的阻止。 这样做是为了作为一个 API 函数,以便对 SCSI 中间层内部的更改不会要求对使用此功能的驱动程序进行大规模更改。

int scsi_mode_select(struct scsi_device *sdev, int pf, int sp, unsigned char *buffer, int len, int timeout, int retries, struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr)

发出模式选择

参数

struct scsi_device *sdev

要查询的 SCSI 设备

int pf

页面格式位 (1 == 标准,0 == 厂商特定)

int sp

保存页面位 (0 == 不保存,1 == 保存)

unsigned char *buffer

请求缓冲区 (不得小于 8 个字节)

int len

请求缓冲区的长度。

int timeout

命令超时

int retries

失败之前的重试次数

struct scsi_mode_data *data

返回一个抽象模式头数据的结构

struct scsi_sense_hdr *sshdr

放置 sense 数据的空间 (如果不需要收集 sense 数据,则为 NULL)。必须是 SCSI_SENSE_BUFFERSIZE 大小。

如果成功则返回零;如果出错,则返回负错误号或 SCSI 状态

int scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, int subpage, unsigned char *buffer, int len, int timeout, int retries, struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr)

发出模式感测,如有必要,从 10 字节回退到 6 字节。

参数

struct scsi_device *sdev

要查询的 SCSI 设备

int dbd

设置以防止模式感测返回块描述符

int modepage

正在请求的模式页

int subpage

正在请求的模式页的子页

unsigned char *buffer

请求缓冲区 (不得小于 8 个字节)

int len

请求缓冲区的长度。

int timeout

命令超时

int retries

失败之前的重试次数

struct scsi_mode_data *data

返回一个抽象模式头数据的结构

struct scsi_sense_hdr *sshdr

放置 sense 数据的空间 (如果不需要收集 sense 数据,则为 NULL)。必须是 SCSI_SENSE_BUFFERSIZE 大小。

如果成功则返回零,如果失败则返回负错误号

int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, struct scsi_sense_hdr *sshdr)

测试单元是否就绪

参数

struct scsi_device *sdev

要更改状态的 SCSI 设备。

int timeout

命令超时

int retries

失败之前的重试次数

struct scsi_sense_hdr *sshdr

解码后的 sense 信息的输出指针。

如果 TUR 失败,则返回零表示不成功,否则返回错误。 对于可移动介质,UNIT_ATTENTION 设置 ->changed 标志。

int scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)

使给定的设备通过设备状态模型。

参数

struct scsi_device *sdev

要更改状态的 SCSI 设备。

enum scsi_device_state state

要更改为的状态。

如果成功则返回零,如果请求的转换是非法的则返回错误。

void sdev_evt_send(struct scsi_device *sdev, struct scsi_event *evt)

将断言的事件发送到 uevent 线程

参数

struct scsi_device *sdev

scsi_device 上发生的事件

struct scsi_event *evt

要发送的事件

异步断言 SCSI 设备事件。

struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, gfp_t gfpflags)

分配一个新的 SCSI 事件

参数

enum scsi_device_event evt_type

要分配的事件类型

gfp_t gfpflags

用于分配的 GFP 标志

分配并返回一个新的 scsi_event。

void sdev_evt_send_simple(struct scsi_device *sdev, enum scsi_device_event evt_type, gfp_t gfpflags)

将断言的事件发送到 uevent 线程

参数

struct scsi_device *sdev

scsi_device 上发生的事件

enum scsi_device_event evt_type

要发送的事件类型

gfp_t gfpflags

用于分配的 GFP 标志

异步断言 SCSI 设备事件,给定事件类型。

int scsi_device_quiesce(struct scsi_device *sdev)

阻止除电源管理之外的所有命令。

参数

struct scsi_device *sdev

要暂停的 SCSI 设备。

这通过尝试转换到 SDEV_QUIESCE 状态 (必须是合法的转换) 来实现。 当设备处于此状态时,只会接受电源管理请求,所有其他请求将被延迟。

必须使用用户上下文调用,可能会休眠。

如果成功则返回零,如果失败则返回错误。

void scsi_device_resume(struct scsi_device *sdev)

重新启动已暂停设备的用户发出的命令。

参数

struct scsi_device *sdev

要恢复的 SCSI 设备。

将设备从暂停状态移动回运行状态并重新启动队列。

必须使用用户上下文调用,可能会休眠。

int scsi_internal_device_block_nowait(struct scsi_device *sdev)

尝试转换到 SDEV_BLOCK 状态

参数

struct scsi_device *sdev

要阻止的设备

描述

暂停指定设备上的 SCSI 命令处理。 不会休眠。

如果成功则返回零,如果失败则返回负错误代码。

底层驱动程序可以随时调用它,我们将执行

此例程将设备转换到 SDEV_BLOCK 状态 (必须是合法的转换)。 当设备处于此状态时,命令处理将被暂停,直到设备离开 SDEV_BLOCK 状态。 另请参阅 scsi_internal_device_unblock_nowait()

int scsi_internal_device_unblock_nowait(struct scsi_device *sdev, enum scsi_device_state new_state)

在阻止请求后恢复设备

参数

struct scsi_device *sdev

要恢复的设备

enum scsi_device_state new_state

取消阻止后要将设备设置为的状态

描述

重新启动先前暂停的 SCSI 设备的设备队列。 不会休眠。

如果成功则返回零,如果失败则返回负错误代码。

底层驱动程序可以随时调用它,我们将执行

此例程将设备转换到 SDEV_RUNNING 状态或其中一个离线状态 (必须是合法的转换),允许中间层推动此设备的队列。

void scsi_block_targets(struct Scsi_Host *shost, struct device *dev)

将所有 SCSI 子设备转换到 SDEV_BLOCK 状态

参数

struct Scsi_Host *shost

此设备所属的 Scsi_Host

struct device *dev

一个或多个 scsi_target 设备的父设备

描述

迭代 **dev** 的所有子设备,这些子设备应该是 scsi_target 设备,并将所有下级 SCSI 设备切换到 SDEV_BLOCK 状态。 等待正在进行的 scsi_queue_rq() 调用完成。 可能会休眠。

注意

**dev** 本身不得是 scsi_target 设备。

int scsi_host_block(struct Scsi_Host *shost)

尝试将所有逻辑单元转换到 SDEV_BLOCK 状态

参数

struct Scsi_Host *shost

要阻止的设备

描述

暂停与 SCSI 主机关联的所有逻辑单元的 SCSI 命令处理,并等待挂起的 scsi_queue_rq() 调用完成。

如果成功则返回零,如果失败则返回负错误代码。

void *scsi_kmap_atomic_sg(struct scatterlist *sgl, int sg_count, size_t *offset, size_t *len)

查找并原子地映射一个 sg 元素

参数

struct scatterlist *sgl

分散-聚集列表

int sg_count

sg 中的段数

size_t *offset

sg 中的字节偏移量,返回时为映射区域中的偏移量

size_t *len

要映射的字节数,返回时为映射的字节数

描述

返回映射页面开始的虚拟地址

void scsi_kunmap_atomic_sg(void *virt)

原子地取消映射一个虚拟地址,该地址先前已使用 scsi_kmap_atomic_sg 映射

参数

void *virt

要取消映射的虚拟地址

int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)

返回唯一的设备标识

参数

struct scsi_device *sdev

SCSI 设备

char *id

标识的缓冲区

size_t id_len

缓冲区的长度

描述

基于设备 VPD 页面 0x83 中的信息,将唯一的设备标识复制到 **id** 中。 该字符串将格式化为 SCSI 名称字符串。

如果成功,则返回标识的长度,如果失败,则返回错误。 如果标识符长于提供的缓冲区,则返回实际的标识符长度,并且缓冲区不会被零填充。

int scsi_vpd_tpg_id(struct scsi_device *sdev, int *rel_id)

返回目标端口组标识符

参数

struct scsi_device *sdev

SCSI 设备

int *rel_id

指向返回相对目标端口的指针 (如果不是 NULL)

描述

从设备的 VPD 页面 0x83 中的信息返回目标端口组标识符。 (可选)成功时将 **rel_id** 设置为相对目标端口。

返回

标识符,如果失败,则返回错误。

void scsi_build_sense(struct scsi_cmnd *scmd, int desc, u8 key, u8 asc, u8 ascq)

为命令构建 sense 数据

参数

struct scsi_cmnd *scmd

应为其格式化 sense 数据的 scsi 命令

int desc

Sense 格式(非零 == 描述符格式, 0 == 固定格式)

u8 key

Sense 键

u8 asc

附加 sense 代码

u8 ascq

附加 sense 代码限定符

drivers/scsi/scsi_lib_dma.c

依赖于 DMA 的 SCSI 库函数 (映射和取消映射 scatter-gather 列表).

int scsi_dma_map(struct scsi_cmnd *cmd)

针对命令的 sg 列表执行 DMA 映射

参数

struct scsi_cmnd *cmd

scsi 命令

描述

返回实际使用的 sg 列表数量,如果 sg 列表为 NULL 则返回 0,如果映射失败则返回 -ENOMEM。

void scsi_dma_unmap(struct scsi_cmnd *cmd)

取消映射 scsi_dma_map 映射的命令 sg 列表

参数

struct scsi_cmnd *cmd

scsi 命令

drivers/scsi/scsi_proc.c

此文件中的函数提供 PROC 文件系统和 SCSI 设备驱动程序之间的接口。它主要用于调试、统计以及将信息直接传递到低级驱动程序。例如,用于管理 /proc/scsi/*

struct scsi_proc_entry

(主机模板,SCSI proc 目录) 关联

定义:

struct scsi_proc_entry {
    struct list_head        entry;
    const struct scsi_host_template *sht;
    struct proc_dir_entry   *proc_dir;
    unsigned int            present;
};

成员

条目

scsi_proc_list 中的条目。

sht

与 procfs 目录关联的 SCSI 主机模板。

proc_dir

与 SCSI 主机模板关联的 procfs 目录。

present

sht 实例化的 SCSI 主机的数量。

struct proc_dir_entry *scsi_template_proc_dir(const struct scsi_host_template *sht)

返回 SCSI 主机模板的 procfs 目录

参数

const struct scsi_host_template *sht

SCSI 主机模板指针。

int scsi_proc_hostdir_add(const struct scsi_host_template *sht)

在 /proc 中为 scsi 主机创建目录

参数

const struct scsi_host_template *sht

此目录的所有者

描述

将 sht->proc_dir 设置为新目录。

void scsi_proc_hostdir_rm(const struct scsi_host_template *sht)

删除 /proc 中 scsi 主机的目录

参数

const struct scsi_host_template *sht

目录的所有者

void scsi_proc_host_add(struct Scsi_Host *shost)

将此主机的条目添加到相应的 /proc 目录

参数

struct Scsi_Host *shost

要添加的主机

void scsi_proc_host_rm(struct Scsi_Host *shost)

从 /proc 中删除此主机的条目

参数

struct Scsi_Host *shost

哪个主机

int proc_print_scsidevice(struct device *dev, void *data)

返回有关此主机的数据

参数

struct device *dev

一个 scsi 设备

void *data

要输出到的 struct seq_file

描述

打印主机、通道、Id、Lun、供应商、型号、版本、类型和修订。

int scsi_add_single_device(uint host, uint channel, uint id, uint lun)

响应用户探测/添加设备请求

参数

uint host

用户提供的十进制整数

uint channel

用户提供的十进制整数

uint id

用户提供的十进制整数

uint lun

用户提供的十进制整数

描述

通过写入“scsi add-single-device”到 /proc/scsi/scsi 来调用。

执行 scsi_host_lookup(),如果该传输类型支持,则执行 user_scan(),否则执行 scsi_scan_host_selected()

注意

这似乎专门针对 SCSI 并行总线。

int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)

响应用户删除设备请求

参数

uint host

用户提供的十进制整数

uint channel

用户提供的十进制整数

uint id

用户提供的十进制整数

uint lun

用户提供的十进制整数

描述

通过写入“scsi remove-single-device”到 /proc/scsi/scsi 来调用。执行 scsi_device_lookup()scsi_remove_device()

ssize_t proc_scsi_write(struct file *file, const char __user *buf, size_t length, loff_t *ppos)

处理写入 /proc/scsi/scsi 的操作

参数

struct file *file

未使用

const char __user *buf

要写入的缓冲区

size_t length

buf 的长度,最多 PAGE_SIZE

loff_t *ppos

未使用

描述

这提供了一种传统机制,用于按主机、通道、ID 和 Lun 添加或删除设备。要使用,请执行“echo ‘scsi add-single-device 0 1 2 3’ > /proc/scsi/scsi”或“echo ‘scsi remove-single-device 0 1 2 3’ > /proc/scsi/scsi”,其中“0 1 2 3”替换为主机、通道、Id 和 Lun。

注意

这似乎针对并行 SCSI。大多数现代总线(USB、SATA、Firewire、光纤通道等)动态分配这些值,以提供一个唯一标识符,仅此而已。

int proc_scsi_open(struct inode *inode, struct file *file)

粘合函数

参数

struct inode *inode

未使用

struct file *file

传递给 single_open()

描述

将 proc_scsi_show 与此文件关联

int scsi_init_procfs(void)

在 procfs 中创建 scsi 和 scsi/scsi

参数

void

无参数

void scsi_exit_procfs(void)

从 procfs 中删除 scsi/scsi 和 scsi

参数

void

无参数

drivers/scsi/scsi_scan.c

扫描主机以确定连接了哪些(如果有)设备。一般的扫描/探测算法如下,根据设备特定标志、编译选项和全局变量(启动或模块加载时间)设置对其进行例外处理。通过 INQUIRY 命令扫描特定 LUN;如果 LUN 连接了设备,则会为此分配并设置一个 scsi_device。对于给定主机上每个通道的每个 id,首先扫描 LUN 0。跳过根本不响应 LUN 0 扫描的主机。否则,如果 LUN 0 连接了设备,则为其分配并设置一个 scsi_device。如果目标是 SCSI-3 或更高版本,则发出 REPORT LUN,并扫描 REPORT LUN 返回的所有 LUN;否则,按顺序扫描 LUN,直到达到某个最大值,或者看到一个无法连接设备的 LUN。

void scsi_sanitize_inquiry_string(unsigned char *s, int len)

从 INQUIRY 结果字符串中删除非图形字符

参数

unsigned char *s

要清理的 INQUIRY 结果字符串

int len

字符串的长度

描述

SCSI 规范规定,INQUIRY 供应商、产品和版本字符串必须完全由图形 ASCII 字符组成,并在右侧用空格填充。由于并非所有设备都遵守此规则,因此我们将用空格替换非图形或非 ASCII 字符。例外:NUL 字符被解释为字符串终止符,因此所有后续字符都设置为空格。

int scsi_add_device(struct Scsi_Host *host, uint channel, uint target, u64 lun)

创建新的 SCSI (LU) 实例

参数

struct Scsi_Host *host

设备所在的 Scsi_Host 实例

uint channel

目标通道号(很少不是 0

uint target

目标 id 号

u64 lun

目标设备的 LUN

描述

探测特定 LUN 并在找到时添加它。

底层驱动程序可以随时调用它,我们将执行

此调用通常在添加 HBA 时在 SCSI 总线扫描期间在内部执行(即 scsi_scan_host())。因此,只有在 HBA 在 scsi_scan_host() 完成后意识到新的 SCSI 设备 (LU) 时,才应调用它。如果成功,此调用可能会导致 sdev_init() 和 sdev_configure() 回调到 LLD。

返回

成功时返回 0,失败时返回负错误代码

void scsi_scan_target(struct device *parent, unsigned int channel, unsigned int id, u64 lun, enum scsi_scan_mode rescan)

扫描目标 id,可能包括目标上的所有 LUN。

参数

struct device *parent

要扫描的主机

unsigned int channel

要扫描的通道

unsigned int id

要扫描的目标 ID

u64 lun

要扫描的特定 LUN,或 SCAN_WILD_CARD

enum scsi_scan_mode rescan

传递给 LUN 扫描例程;SCSI_SCAN_INITIAL 表示不重新扫描,SCSI_SCAN_RESCAN 表示重新扫描现有 LUN,SCSI_SCAN_MANUAL 表示强制扫描,即使设置了“scan=manual”。

描述

扫描 **parent**、**channel** 和 **id** 上的目标 ID。至少扫描 LUN 0,可能扫描目标 ID 上的所有 LUN。

首先尝试 REPORT LUN 扫描,如果这样无法扫描目标,则对目标 ID 上的 LUN 进行顺序扫描。

void scsi_scan_host(struct Scsi_Host *shost)

扫描给定的适配器

参数

struct Scsi_Host *shost

要扫描的适配器

底层驱动程序可以随时调用它,我们将执行

应在 scsi_add_host() 之后调用

drivers/scsi/scsi_sysctl.c

设置 sysctl 条目:“/dev/scsi/logging_level”(DEV_SCSI_LOGGING_LEVEL),用于设置/返回 scsi_logging_level。

drivers/scsi/scsi_sysfs.c

SCSI sysfs 接口例程。

void scsi_remove_device(struct scsi_device *sdev)

从 SCSI 总线上注销设备

参数

struct scsi_device *sdev

要注销的 scsi_device

void scsi_remove_target(struct device *dev)

尝试删除目标及其所有设备

参数

struct device *dev

要删除的通用 starget 或通用 starget 的父级

注意

这有点竞争。如果用户请求添加另一个设备,则可能无法删除目标。

drivers/scsi/hosts.c

中间层到低层 SCSI 驱动程序接口

void scsi_remove_host(struct Scsi_Host *shost)

删除 SCSI 主机

参数

struct Scsi_Host *shost

要删除的 SCSI 主机的指针

int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, struct device *dma_dev)

添加带有 DMA 设备的 SCSI 主机

参数

struct Scsi_Host *shost

要添加的 SCSI 主机指针

struct device *dev

scsi 类类型的 struct device

struct device *dma_dev

主机的 DMA 设备

注意

除非您处于虚拟化主机环境中,否则您很少需要担心这一点,因此请使用更简单的 scsi_add_host() 函数。

描述

返回值

成功时为 0 / 错误时为 != 0

struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int privsize)

注册 SCSI 主机适配器实例。

参数

const struct scsi_host_template *sht

SCSI 主机模板的指针

int privsize

为驱动程序分配的额外字节

注意

分配新的 Scsi_Host 并执行基本初始化。在调用 scsi_add_host 之前,不会将主机发布到 SCSI 中间层。

描述

返回值

指向新的 Scsi_Host 的指针

struct Scsi_Host *scsi_host_lookup(unsigned int hostnum)

按主机号获取对 Scsi_Host 的引用

参数

unsigned int hostnum

要查找的主机号

描述

返回值

指向已定位的 Scsi_Host 的指针,如果找不到则为 NULL。

调用者必须执行 scsi_host_put() 以删除 scsi_host_get() 采用的引用。下面的 put_device() 删除了来自 class_find_device() 的引用。

struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost)

增加 Scsi_Host 引用计数

参数

struct Scsi_Host *shost

指向要增加的 Scsi_Host 的指针。

int scsi_host_busy(struct Scsi_Host *shost)

返回主机忙碌计数器

参数

struct Scsi_Host *shost

指向要增加的 Scsi_Host 的指针。

void scsi_host_put(struct Scsi_Host *shost)

减少 Scsi_Host 引用计数

参数

struct Scsi_Host *shost

指向要减少的 Scsi_Host 的指针。

int scsi_queue_work(struct Scsi_Host *shost, struct work_struct *work)

将工作排队到 Scsi_Host 工作队列。

参数

struct Scsi_Host *shost

指向 Scsi_Host 的指针。

struct work_struct *work

要排队执行的工作。

描述

返回值

1 - 工作已排队等待执行 0 - 工作已排队 -EINVAL - 工作队列不存在

void scsi_flush_work(struct Scsi_Host *shost)

刷新 Scsi_Host 的工作队列。

参数

struct Scsi_Host *shost

指向 Scsi_Host 的指针。

void scsi_host_complete_all_commands(struct Scsi_Host *shost, enum scsi_host_status status)

终止所有正在运行的命令

参数

struct Scsi_Host *shost

应终止命令的 SCSI 主机

enum scsi_host_status status

为终止的命令设置的状态

描述

没有针对修改未完成命令数的保护。调用者有责任确保在调用此函数时停止并发 I/O 提交和/或完成。

void scsi_host_busy_iter(struct Scsi_Host *shost, bool (*fn)(struct scsi_cmnd*, void*), void *priv)

迭代所有忙碌命令

参数

struct Scsi_Host *shost

指向 Scsi_Host 的指针。

bool (*fn)(struct scsi_cmnd *, void *)

在每个忙碌命令上调用的函数

void *priv

传递给 **fn** 的数据指针

描述

如果需要锁定以防止并发命令完成,则必须由调用者提供

drivers/scsi/scsi_common.c

通用支持函数

const char *scsi_device_type(unsigned type)

返回指示设备类型的 17 字符字符串。

参数

unsigned type

要查找的类型编号

u64 scsilun_to_int(struct scsi_lun *scsilun)

将 scsi_lun 转换为 int

参数

struct scsi_lun *scsilun

要转换的 struct scsi_lun。

描述

将 **scsilun** 从 struct scsi_lun 转换为四字节的主机字节序整数,并返回结果。调用者在使用此函数之前必须检查截断。

底层驱动程序可以随时调用它,我们将执行

有关 LUN 格式的描述,请参阅 SCSI-3 后的 SCSI 架构模型,有关 SCSI-3 请参阅 SCSI 控制器命令。

给定一个 struct scsi_lun:d2 04 0b 03 00 00 00 00,此函数返回整数:0x0b03d204

对于小于 256 的 LUN,此编码将返回标准整数 LUN,通常使用寻址方法 0 的单级 LUN 结构。

void int_to_scsilun(u64 lun, struct scsi_lun *scsilun)

将 int 恢复为 scsi_lun

参数

u64 lun

要恢复的整数

struct scsi_lun *scsilun

要设置的 struct scsi_lun。

描述

恢复 scsilun_to_int 的功能,它将 8 字节的 LUN 值打包到 int 中。此例程将 int 解包回 LUN 值。

底层驱动程序可以随时调用它,我们将执行

给定一个整数:0x0b03d204,此函数返回一个 struct scsi_lun:d2 04 0b 03 00 00 00 00

bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len, struct scsi_sense_hdr *sshdr)

将固定或描述符错误数据格式中的主要元素规范化为通用格式。

参数

const u8 *sense_buffer

包含设备返回的错误数据的字节数组

int sb_len

sense_buffer 中的有效字节数

struct scsi_sense_hdr *sshdr

指向将写入公共元素的结构实例的指针。

底层驱动程序可以随时调用它,我们将执行

来自错误数据的“主要元素”是:response_code、sense_key、asc、ascq 和 additional_length(仅适用于描述符格式)。

通常,可以在设备使用 CHECK_CONDITION 状态响应 SCSI 命令后调用此函数。

描述

返回值

如果找到有效的错误数据信息,则为 true,否则为 false;

const u8 *scsi_sense_desc_find(const u8 *sense_buffer, int sb_len, int desc_type)

在描述符错误数据格式中搜索给定的描述符类型。

参数

const u8 * sense_buffer

描述符格式错误数据的字节数组

int sb_len

sense_buffer 中的有效字节数

int desc_type

要查找的描述符类型的值(例如,0 -> 信息)

底层驱动程序可以随时调用它,我们将执行

仅当错误数据采用描述符格式时才有效

描述

返回值

指向找到的(第一个)描述符的开始位置的指针,否则为 NULL

void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq)

在缓冲区中构建错误数据

参数

int desc

Sense 格式(非零 == 描述符格式, 0 == 固定格式)

u8 *buf

在何处构建错误数据

u8 key

Sense 键

u8 asc

附加 sense 代码

u8 ascq

附加 sense 代码限定符

int scsi_set_sense_information(u8 *buf, int buf_len, u64 info)

在格式化的错误数据缓冲区中设置信息字段

参数

u8 *buf

在何处构建错误数据

int buf_len

缓冲区长度

u64 info

要设置的 64 位信息值

描述

返回值

成功时为 0,无效错误缓冲区长度时为 -EINVAL

int scsi_set_sense_field_pointer(u8 *buf, int buf_len, u16 fp, u8 bp, bool cd)

在格式化的 sense 数据缓冲区中设置字段指针 sense key 特定信息

参数

u8 *buf

在何处构建错误数据

int buf_len

缓冲区长度

u16 fp

要设置的字段指针

u8 bp

要设置的位指针

bool cd

命令/数据位

描述

返回值

成功时为 0,无效错误缓冲区长度时为 -EINVAL

传输类

传输类是 SCSI 下层驱动程序的 service 库,可在 sysfs 中公开传输属性。

光纤通道传输

文件 drivers/scsi/scsi_transport_fc.c 定义了光纤通道的传输属性。

u32 fc_get_event_number(void)

获取下一个连续的 FC 事件编号

参数

void

无参数

底层驱动程序可以随时调用它,我们将执行

我们可以内联这个,但这需要公开 fc_event_seq。目前,暂时使用子例程调用。使用 Atomic 避免锁定/解锁...

void fc_host_post_fc_event(struct Scsi_Host *shost, u32 event_number, enum fc_host_event_code event_code, u32 data_len, char *data_buf, u64 vendor_id)

在 fc_host 上发布事件的例程。

参数

struct Scsi_Host *shost

事件发生的主机

u32 event_number

从 get_fc_event_number() 获取的 fc 事件编号

enum fc_host_event_code event_code

正在发布的 fc_host 事件

u32 data_len

事件数据量,以字节为单位

char *data_buf

指向事件数据的指针

u64 vendor_id

供应商 ID 的值

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

void fc_host_post_event(struct Scsi_Host *shost, u32 event_number, enum fc_host_event_code event_code, u32 event_data)

调用以在 fc_host 上发布 even。

参数

struct Scsi_Host *shost

事件发生的主机

u32 event_number

从 get_fc_event_number() 获取的 fc 事件编号

enum fc_host_event_code event_code

正在发布的 fc_host 事件

u32 event_data

正在发布的事件的 32 位数据

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

void fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number, u32 data_len, char *data_buf, u64 vendor_id)

调用以在 fc_host 上发布厂商唯一事件

参数

struct Scsi_Host *shost

事件发生的主机

u32 event_number

从 get_fc_event_number() 获取的 fc 事件编号

u32 data_len

厂商唯一数据的数量,以字节为单位

char * data_buf

指向厂商唯一数据的指针

u64 vendor_id

厂商 ID

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

struct fc_rport *fc_find_rport_by_wwpn(struct Scsi_Host *shost, u64 wwpn)

查找给定 wwpn 的 fc_rport 指针

参数

struct Scsi_Host *shost

fc_rport 相关联的主机

u64 wwpn

fc_rport 设备的 wwpn

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

void fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf, u8 event_acknowledge)

处理收到的 FPIN 的例程。

参数

struct Scsi_Host *shost

收到 FPIN 的主机

u32 fpin_len

FPIN 有效负载的长度,以字节为单位

char *fpin_buf

指向 FPIN 有效负载的指针

u8 event_acknowledge

如果 LLDD 处理此事件,则为 1。

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

enum scsi_timeout_action fc_eh_timed_out(struct scsi_cmnd *scmd)

FC 传输 I/O 超时拦截处理程序

参数

struct scsi_cmnd *scmd

超时的 SCSI 命令

描述

此例程可以防止在 rport 处于阻止状态时调用错误处理程序,通常是由于暂时失去连接。 如果允许错误处理程序继续,则中止 i/o、重置目标等请求很可能会失败,因为无法与设备通信以执行所请求的功能。 这些故障可能导致中间层使设备脱机,需要手动干预才能恢复操作。

此例程在 i/o 超时时调用,用于验证底层 rport 的状态。 如果 rport 被阻止,它将返回 EH_RESET_TIMER,这将继续重新安排超时。 最终,设备将返回,或者 devloss_tmo 将触发,并且当超时触发时,它将被正常处理。 如果 rport 未被阻止,则继续正常错误处理。

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

void fc_remove_host(struct Scsi_Host *shost)

调用以终止 scsi 主机的任何 fc_transport 相关元素。

参数

struct Scsi_Host *shost

哪个 Scsi_Host

描述

预计此例程将在驱动程序调用 scsi_remove_host() 之前立即调用。

警告:使用 fc_transport 的驱动程序,如果未能调用

scsi_remove_host() 之前执行此例程,将会在 /sys/class/fc_remote_ports 中留下悬挂对象。 访问任何这些对象都可能导致系统崩溃!!!

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

struct fc_rport *fc_remote_port_add(struct Scsi_Host *shost, int channel, struct fc_rport_identifiers *ids)

通知 fc 传输远程 FC 端口的存在。

参数

struct Scsi_Host *shost

远程端口连接到的 scsi 主机。

int channel

连接到的 shost 端口上的通道。

struct fc_rport_identifiers *ids

远程端口的全球通用名称、fc 地址和 FC4 端口角色。

描述

LLDD 调用此例程以通知传输远程端口的存在。 LLDD 提供端口的唯一标识符(wwpn、wwn)、其 FC 地址 (port_id) 以及端口的活动 FC4 角色。

对于作为 FCP 目标(又称 scsi 目标)的端口,FC 传输代表 LLDD 维护一致的目标 ID 绑定。 一致的目标 ID 绑定是将目标 ID 分配给远程端口标识符,只要 scsi 主机已连接,该绑定就会持续存在。 远程端口可以消失,然后稍后重新出现,其目标 ID 分配保持不变。 这允许 FC 寻址的转变(如果通过 wwpn 或 wwnn 绑定),而 scsi 子系统没有任何明显的变化,scsi 子系统基于 scsi 主机编号和目标 ID 值。 绑定仅在 scsi 主机连接期间有效。 如果主机分离,然后稍后重新连接,则目标 ID 绑定可能会更改。

此例程负责返回远程端口结构。 此例程将搜索代表一致目标 ID 映射在内部维护的远程端口列表。 如果找到,则将重用远程端口结构。 否则,将分配一个新的远程端口结构。

每当分配远程端口时,都会创建一个新的 fc_remote_port 类设备。

不应从中断上下文调用。

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

void fc_remote_port_delete(struct fc_rport *rport)

通知 fc 传输远程端口不再存在。

参数

struct fc_rport *rport

不再存在的远程端口

描述

LLDD 调用此例程以通知传输远程端口不再是拓扑的一部分。 注意:虽然端口可能不再是拓扑的一部分,但它可能仍然存在于 fc_host 显示的远程端口中。 我们在 2 种情况下执行此操作

  1. 如果端口是 scsi 目标,我们会通过“阻止”它来延迟其删除。 这允许端口暂时消失,然后重新出现,而不会中断连接到它的 SCSI 设备树。 在“阻止”期间,端口仍将存在。

  2. 如果端口是 scsi 目标并且消失的时间超过我们的预期,我们将删除该端口并拆除连接到它的 SCSI 设备树。 但是,我们希望在最终存在的情况下半持久分配给该端口的目标 ID。 端口结构将保留(尽管信息最少),以便目标 ID 绑定也保留。

如果远程端口不是 FCP 目标,它将被完全拆除和释放,包括 fc_remote_port 类设备。

如果远程端口是 FCP 目标,该端口将被置于临时阻止状态。 从 LLDD 的角度来看,rport 不再存在。 从 SCSI 中间层的角度来看,SCSI 目标存在,但它上面的所有 sdev 都被阻止进行进一步的 I/O。 然后预期以下情况。

如果远程端口未在 dev_loss_tmo 超时内返回(通过 LLDD 调用 fc_remote_port_add() 发出信号),则删除 scsi 目标 - 终止所有未完成的 i/o 并删除连接到它的 scsi 设备。 端口结构将被标记为不存在并被部分清除,仅留下足够的信息来识别相对于 scsi 目标 ID 绑定的远程端口(如果它稍后出现)。 只要存在有效的绑定(例如,直到用户更改绑定类型或卸载带有绑定的 scsi 主机),端口将保持原样。

如果远程端口在 dev_loss_tmo 值内返回(并根据目标 ID 绑定类型匹配),则将重用端口结构。 如果它不再是 SCSI 目标,则目标将被拆除。 如果它继续是 SCSI 目标,则目标将被解除阻止(允许恢复 i/o),并且将激活扫描以确保检测到所有 lun。

仅从正常进程上下文调用 - 不能从中断调用。

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

void fc_remote_port_rolechg(struct fc_rport *rport, u32 roles)

通知 fc 传输远程端口上的角色可能已更改。

参数

struct fc_rport *rport

已更改的远程端口。

u32 roles

此端口的新角色。

描述

LLDD 调用此例程以通知传输远程端口上的角色可能已更改。 最大的影响是,如果端口现在变为 FCP 目标,则必须为其分配 scsi 目标 ID。 如果端口不再是 FCP 目标,则分配给它的任何 scsi 目标 ID 值都将保留,以防角色更改回包括 FCP 目标。 如果角色发生变化(期望角色将被恢复),则不会调用 scsi 中间层的任何更改。 如果不这样做,将进行正常的错误处理。

不应从中断上下文调用。

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

int fc_block_rport(struct fc_rport *rport)

阻止用于阻止的 fc_rport 的 SCSI eh 线程。

参数

struct fc_rport *rport

scsi_eh 尝试恢复的远程端口。

描述

可以从 FC LLD scsi_eh 回调调用此例程。 它会阻止 scsi_eh 线程,直到 fc_rport 离开 FC_PORTSTATE_BLOCKED 状态或触发 fast_io_fail_tmo。 这对于避免 scsi_eh 无法执行阻止的 rport 的恢复操作是必要的,这会导致 SCSI 设备脱机。

返回

如果 fc_rport 离开 FC_PORTSTATE_BLOCKED 状态,则为 0。

如果触发了 fast_io_fail_tmo,则为 FAST_IO_FAIL,应将其传递回 scsi_eh。

int fc_block_scsi_eh(struct scsi_cmnd *cmnd)

阻止用于阻止的 fc_rport 的 SCSI eh 线程

参数

struct scsi_cmnd *cmnd

scsi_eh 尝试恢复的 SCSI 命令

描述

可以从 FC LLD scsi_eh 回调调用此例程。 它会阻止 scsi_eh 线程,直到 fc_rport 离开 FC_PORTSTATE_BLOCKED 状态或触发 fast_io_fail_tmo。 这对于避免 scsi_eh 无法执行阻止的 rport 的恢复操作是必要的,这会导致 SCSI 设备脱机。

返回

如果 fc_rport 离开 FC_PORTSTATE_BLOCKED 状态,则为 0。

如果触发了 fast_io_fail_tmo,则为 FAST_IO_FAIL,应将其传递回 scsi_eh。

struct fc_vport *fc_vport_create(struct Scsi_Host *shost, int channel, struct fc_vport_identifiers *ids)

管理应用程序或 LLDD 请求创建 vport

参数

struct Scsi_Host *shost

虚拟端口连接到的 scsi 主机。

int channel

连接到的 shost 端口上的通道。

struct fc_vport_identifiers *ids

虚拟端口的全球通用名称、FC4 端口角色等。

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

int fc_vport_terminate(struct fc_vport *vport)

管理应用程序或 LLDD 请求终止 vport

参数

struct fc_vport *vport

要终止的 fc_vport

描述

调用 LLDD vport_delete() 函数,然后从 shost 和对象树中释放并删除 vport。

底层驱动程序可以随时调用它,我们将执行

此例程假定入口处没有持有锁。

iSCSI 传输类

文件 drivers/scsi/scsi_transport_iscsi.c 定义了 iSCSI 类的传输属性,该类通过 TCP/IP 连接发送 SCSI 数据包。

struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)

从句柄获取 ep

参数

u64 handle

端点句柄

描述

调用方必须执行 iscsi_put_endpoint。

struct iscsi_bus_flash_session *iscsi_create_flashnode_sess(struct Scsi_Host *shost, int index, struct iscsi_transport *transport, int dd_size)

在sysfs中添加 flashnode 会话条目

参数

struct Scsi_Host *shost

指向主机数据的指针

int index

要在sysfs中添加的 flashnode 的索引

struct iscsi_transport *transport

指向传输数据的指针

int dd_size

要分配的总大小

描述

为 flashnode 会话属性添加 sysfs 条目

返回

成功时指向已分配的 flashnode 会话的指针,失败时为 NULL

struct iscsi_bus_flash_conn *iscsi_create_flashnode_conn(struct Scsi_Host *shost, struct iscsi_bus_flash_session *fnode_sess, struct iscsi_transport *transport, int dd_size)

在sysfs中添加 flashnode 连接条目

参数

struct Scsi_Host *shost

指向主机数据的指针

struct iscsi_bus_flash_session *fnode_sess

指向父 flashnode 会话条目的指针

struct iscsi_transport *transport

指向传输数据的指针

int dd_size

要分配的总大小

描述

为 flashnode 连接属性添加 sysfs 条目

返回

成功时指向已分配的 flashnode 连接的指针,失败时为 NULL

struct device *iscsi_find_flashnode_sess(struct Scsi_Host *shost, const void *data, device_match_t fn)

查找 flashnode 会话条目

参数

struct Scsi_Host *shost

指向主机数据的指针

const void *data

指向包含用于比较的值的数据的指针

device_match_t fn

执行实际比较的函数指针

描述

查找 flashnode 会话对象,使用传入的函数指针中定义的逻辑比较传递的数据

返回

成功时指向找到的 flashnode 会话设备对象的指针,失败时为 NULL

struct device *iscsi_find_flashnode_conn(struct iscsi_bus_flash_session *fnode_sess)

查找 flashnode 连接条目

参数

struct iscsi_bus_flash_session *fnode_sess

指向父 flashnode 会话条目的指针

描述

查找 flashnode 连接对象,使用传入的函数指针中定义的逻辑比较传递的数据

返回

成功时指向找到的 flashnode 连接设备对象的指针,失败时为 NULL

void iscsi_destroy_flashnode_sess(struct iscsi_bus_flash_session *fnode_sess)

销毁 flashnode 会话条目

参数

struct iscsi_bus_flash_session *fnode_sess

指向要销毁的 flashnode 会话条目的指针

描述

从 sysfs 中删除 flashnode 会话条目和所有子 flashnode 连接条目

void iscsi_destroy_all_flashnode(struct Scsi_Host *shost)

销毁所有 flashnode 会话条目

参数

struct Scsi_Host *shost

指向主机数据的指针

描述

从 sysfs 中销毁所有 flashnode 会话条目和所有相应的子 flashnode 连接条目

int iscsi_block_scsi_eh(struct scsi_cmnd *cmd)

阻塞 scsi eh 直到会话状态转换

参数

struct scsi_cmnd *cmd

传递给 scsi eh 处理程序的 scsi 命令

描述

如果会话已关闭,则此函数将等待恢复定时器触发或会话重新登录。如果恢复定时器触发,则返回 FAST_IO_FAIL。调用者应将此错误值传递给 scsi eh。

void iscsi_unblock_session(struct iscsi_cls_session *session)

将会话设置为已登录并启动 IO。

参数

struct iscsi_cls_session *session

iscsi 会话

描述

将会话标记为准备好接受 IO。

void iscsi_force_destroy_session(struct iscsi_cls_session *session)

从内核销毁会话

参数

struct iscsi_cls_session *session

要销毁的会话

描述

强制从内核销毁会话。仅当用户空间在系统关闭期间不再运行时,才应使用此方法。

struct iscsi_cls_conn *iscsi_alloc_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)

分配 iscsi 类连接

参数

struct iscsi_cls_session *session

iscsi cls 会话

int dd_size

私有驱动程序数据大小

uint32_t cid

连接 ID

int iscsi_add_conn(struct iscsi_cls_conn *conn)

添加 iscsi 类连接

参数

struct iscsi_cls_conn *conn

iscsi cls 连接

描述

这会将 iscsi_cls_conn 公开给 sysfs,因此请确保在调用此函数之前初始化 sysfs 属性的相关资源。

void iscsi_remove_conn(struct iscsi_cls_conn *conn)

从 sysfs 中删除 iscsi 类连接

参数

struct iscsi_cls_conn *conn

iscsi cls 连接

描述

从 sysfs 中删除 iscsi_cls_conn,并等待先前在 sysfs 中读取/写入 iscsi_cls_conn 的属性完成。

int iscsi_session_event(struct iscsi_cls_session *session, enum iscsi_uevent_e event)

发送会话销毁完成事件

参数

struct iscsi_cls_session *session

iscsi 类会话

enum iscsi_uevent_e event

事件类型

串行连接 SCSI (SAS) 传输类

文件 drivers/scsi/scsi_transport_sas.c 定义了串行连接 SCSI 的传输属性,它是 SATA 的一种变体,旨在用于大型高端系统。

SAS 传输类包含用于处理 SAS HBA、驱动程序模型中 SAS 拓扑的近似表示以及各种 sysfs 属性以向用户空间公开这些拓扑和管理界面的通用代码。

除了基本的 SCSI 核心对象之外,此传输类还引入了两个额外的中间对象:SAS PHY 由 struct sas_phy 表示,定义了 SAS HBA 或扩展器上的“传出”PHY,SAS 远程 PHY 由 struct sas_rphy 表示,定义了 SAS 扩展器或终端设备上的“传入”PHY。请注意,这纯粹是一个软件概念,PHY 和远程 PHY 的底层硬件完全相同。

此代码中没有 SAS 端口的概念,用户可以根据 port_identifier 属性查看哪些 PHY 构成一个宽端口,该属性对于端口中的所有 PHY 都是相同的。

void sas_remove_children(struct device *dev)

拆除设备 SAS 数据结构

参数

struct device *dev

属于 sas 对象的设备

描述

删除给定对象的所有 SAS PHY 和远程 PHY

void sas_remove_host(struct Scsi_Host *shost)

拆除 Scsi_Host 的 SAS 数据结构

参数

struct Scsi_Host *shost

已拆除的 Scsi Host

描述

删除给定 Scsi_Host 的所有 SAS PHY 和远程 PHY,并同时删除 Scsi_Host。

注意

不要再在 Scsi_Host 上调用 scsi_remove_host(),因为它已被删除。

u64 sas_get_address(struct scsi_device *sdev)

返回设备的 SAS 地址

参数

struct scsi_device *sdev

scsi 设备

描述

返回 scsi 设备的 SAS 地址

unsigned int sas_tlr_supported(struct scsi_device *sdev)

检查 vpd 0x90 中的 TLR 位

参数

struct scsi_device *sdev

scsi 设备结构

描述

检查是否支持传输层重试。如果存在 vpd 页 0x90,则支持 TRL。

void sas_disable_tlr(struct scsi_device *sdev)

设置 TLR 标志

参数

struct scsi_device *sdev

scsi 设备结构

描述

将 tlr_enabled 标志设置为 0。

void sas_enable_tlr(struct scsi_device *sdev)

设置 TLR 标志

参数

struct scsi_device *sdev

scsi 设备结构

描述

将 tlr_enabled 标志设置为 1。

bool sas_ata_ncq_prio_supported(struct scsi_device *sdev)

检查是否支持 ATA NCQ 命令优先级

参数

struct scsi_device *sdev

SCSI 设备

描述

检查 ATA 设备是否使用 VPD 页 89h(ATA 信息)支持 NCQ 优先级。由于此 VPD 页仅针对 ATA 设备实现,因此此函数始终为 SCSI 设备返回 false。

struct sas_phy *sas_phy_alloc(struct device *parent, int number)

分配并初始化 SAS PHY 结构

参数

struct device *parent

父设备

int number

Phy 索引

描述

分配一个 SAS PHY 结构。它将添加到设备树中,位于 parent 指定的设备下,该设备必须是 Scsi_Host 或 sas_rphy。

返回

已分配的 SAS PHY,如果分配失败,则为 NULL

int sas_phy_add(struct sas_phy *phy)

将 SAS PHY 添加到设备层次结构

参数

struct sas_phy *phy

要添加的 PHY

描述

将 SAS PHY 发布到系统的其余部分。

void sas_phy_free(struct sas_phy *phy)

释放 SAS PHY

参数

struct sas_phy *phy

要释放的 SAS PHY

描述

释放指定的 SAS PHY。

注意

只能对尚未使用 sas_phy_add() 成功添加的 PHY 调用此函数。

void sas_phy_delete(struct sas_phy *phy)

删除 SAS PHY

参数

struct sas_phy *phy

要删除的 SAS PHY

描述

删除指定的 SAS PHY。如果 SAS PHY 有关联的远程 PHY,则先将其删除。

int scsi_is_sas_phy(const struct device *dev)

检查struct device是否代表一个SAS PHY

参数

const struct device *dev

要检查的设备

返回

如果设备代表一个SAS PHY,则返回1,否则返回0

struct sas_port *sas_port_alloc(struct device *parent, int port_id)

分配和初始化一个SAS端口结构体

参数

struct device *parent

父设备

int port_id

端口号

描述

分配一个 SAS 端口结构。它将被添加到设备树中,位于由 **parent** 指定的设备下方,该设备必须是 Scsi_Host 或 sas_expander_device。

返回

出错时返回NULL

struct sas_port *sas_port_alloc_num(struct device *parent)

分配和初始化一个SAS端口结构体

参数

struct device *parent

父设备

描述

分配一个 SAS 端口结构和一个与之关联的号码。 此接口实际上适用于端口号没有意义的适配器,因此 SAS 类应该管理它们。 它将被添加到设备树中,位于由 **parent** 指定的设备下方,该设备必须是 Scsi_Host 或 sas_expander_device。

返回

出错时返回NULL

int sas_port_add(struct sas_port *port)

将一个SAS端口添加到设备层次结构中

参数

struct sas_port *port

要添加的端口

描述

向系统的其余部分发布一个端口

void sas_port_free(struct sas_port *port)

释放一个SAS端口

参数

struct sas_port *port

要释放的SAS端口

描述

释放指定的SAS端口。

注意

此函数只能在尚未成功使用sas_port_add()添加的端口上调用。

void sas_port_delete(struct sas_port *port)

移除 SAS 端口

参数

struct sas_port *port

要移除的 SAS 端口

描述

移除指定的 SAS 端口。如果 SAS 端口有关联的 phys,则将它们也从端口取消链接。

int scsi_is_sas_port(const struct device *dev)

检查struct device是否代表一个SAS端口

参数

const struct device *dev

要检查的设备

返回

如果设备代表一个 SAS 端口,则返回 1,否则返回 0

struct sas_phy *sas_port_get_phy(struct sas_port *port)

尝试获取对端口成员的引用

参数

struct sas_port *port

要检查的端口

void sas_port_add_phy(struct sas_port *port, struct sas_phy *phy)

将另一个phy添加到端口以形成一个宽端口

参数

struct sas_port *port

要将phy添加到的端口

struct sas_phy *phy

要添加的phy

描述

当端口最初创建时,它是空的(没有 phys)。所有端口必须至少有一个 phy 才能运行,并且所有宽端口必须至少有两个。当前代码对端口和宽端口没有区别,但是唯一可以连接到远程设备的对象是一个端口,所以如果端口连接到任何东西,则必须在具有 phys 的所有设备上形成端口。

void sas_port_delete_phy(struct sas_port *port, struct sas_phy *phy)

从端口或宽端口中移除一个 phy

参数

struct sas_port *port

要从中移除 phy 的端口

struct sas_phy *phy

要移除的 phy

描述

此操作用于再次拆除端口。 在调用 sas_port_delete 之前,必须对每个端口或宽端口执行此操作。

struct sas_rphy *sas_end_device_alloc(struct sas_port *parent)

为终端设备分配一个 rphy

参数

struct sas_port *parent

哪个端口

描述

分配一个 SAS 远程 PHY 结构,连接到 **parent**。

返回

已分配的 SAS PHY,如果分配失败,则为 NULL

struct sas_rphy *sas_expander_alloc(struct sas_port *parent, enum sas_device_type type)

为终端设备分配一个 rphy

参数

struct sas_port *parent

哪个端口

enum sas_device_type type

SAS_EDGE_EXPANDER_DEVICE 或 SAS_FANOUT_EXPANDER_DEVICE

描述

分配一个 SAS 远程 PHY 结构,连接到 **parent**。

返回

已分配的 SAS PHY,如果分配失败,则为 NULL

int sas_rphy_add(struct sas_rphy *rphy)

将一个SAS远程PHY添加到设备层次结构中

参数

struct sas_rphy *rphy

要添加的远程 PHY

描述

向系统的其余部分发布一个 SAS 远程 PHY。

void sas_rphy_free(struct sas_rphy *rphy)

释放一个SAS远程PHY

参数

struct sas_rphy *rphy

要释放的SAS远程PHY

描述

释放指定的SAS远程PHY。

注意

此函数只能在尚未成功使用sas_rphy_add()添加的远程 PHY 上调用(或者已经 sas_rphy_remove() 掉了)。

void sas_rphy_delete(struct sas_rphy *rphy)

移除并释放SAS远程PHY

参数

struct sas_rphy *rphy

要移除和释放的SAS远程PHY

描述

移除指定的SAS远程PHY并释放它。

取消链接 SAS 远程 PHY

参数

struct sas_rphy *rphy

要从其父端口取消链接的 SAS 远程 phy

描述

移除对 rphy 的端口引用

void sas_rphy_remove(struct sas_rphy *rphy)

移除 SAS 远程 PHY

参数

struct sas_rphy *rphy

要移除的 SAS 远程 phy

描述

移除指定的 SAS 远程 PHY。

int scsi_is_sas_rphy(const struct device *dev)

检查struct device是否代表一个SAS远程PHY

参数

const struct device *dev

要检查的设备

返回

如果设备代表一个 SAS 远程 PHY,则返回 1,否则返回 0

struct scsi_transport_template *sas_attach_transport(struct sas_function_template *ft)

实例化 SAS 传输模板

参数

struct sas_function_template *ft

SAS 传输类函数模板

void sas_release_transport(struct scsi_transport_template *t)

释放 SAS 传输模板实例

参数

struct scsi_transport_template *t

传输模板实例

SATA 传输类

SATA 传输由 libata 处理,它在此目录中有自己的文档书。

并行 SCSI (SPI) 传输类

文件 drivers/scsi/scsi_transport_spi.c 定义了传统(快速/宽/超)SCSI 总线的传输属性。

void spi_dv_device(struct scsi_device *sdev)

对设备进行域验证

参数

struct scsi_device *sdev

要验证的 scsi 设备

在当前执行线程中对给定设备执行域验证。 由于 DV 操作可能会休眠,因此当前线程必须具有用户上下文。 也不得持有任何可能导致 DV 发出的 I/O 发生死锁的 SCSI 相关锁。

void spi_schedule_dv_device(struct scsi_device *sdev)

计划对设备进行域验证

参数

struct scsi_device *sdev

要验证的设备

与上面的 spi_dv_device() 相同,只是 DV 将被安排在稍后的工作队列中进行。 所有内存分配都是原子的,因此可以从任何上下文调用,包括那些持有 SCSI 锁的上下文。

void spi_display_xfer_agreement(struct scsi_target *starget)

打印当前目标传输协议

参数

struct scsi_target *starget

要显示协议的目标

描述

每个 SPI 端口都需要为总线上的每个其他端口维护一个传输协议。 此函数打印当前协议的单行摘要; 更多详细信息可在 sysfs 中获得。

int spi_populate_tag_msg(unsigned char *msg, struct scsi_cmnd *cmd)

将标记消息放置在缓冲区中

参数

unsigned char *msg

指向放置标记的区域的指针

struct scsi_cmnd *cmd

指向标记的 scsi 命令的指针

底层驱动程序可以随时调用它,我们将执行

旨在为特定请求创建正确类型的标记消息。 返回标记消息的大小。 如果此设备禁用 TCQ,则可能返回 0。

SCSI RDMA (SRP) 传输类

文件 drivers/scsi/scsi_transport_srp.c 定义了通过远程直接内存访问的 SCSI 的传输属性。

int srp_tmo_valid(int reconnect_delay, int fast_io_fail_tmo, long dev_loss_tmo)

检查超时组合的有效性

参数

int reconnect_delay

重连延迟,以秒为单位。

int fast_io_fail_tmo

快速 I/O 失败超时,以秒为单位。

long dev_loss_tmo

设备丢失超时,以秒为单位。

描述

超时参数的组合必须确保 SCSI 命令在合理的时间内完成。 因此,不允许快速 I/O 故障超时超过 SCSI_DEVICE_BLOCK_MAX_TIMEOUT,如果已禁用快速 I/O 故障,也不允许 dev_loss_tmo 超过该限制。 此外,这些参数必须确保多路径能够及时检测到故障路径。 因此,不允许同时禁用所有三个参数。

void srp_start_tl_fail_timers(struct srp_rport *rport)

启动传输层故障计时器

参数

struct srp_rport *rport

SRP 目标端口。

描述

启动传输层快速 I/O 故障和设备丢失计时器。 不要修改已启动的计时器。

int srp_reconnect_rport(struct srp_rport *rport)

重新连接到 SRP 目标端口

参数

struct srp_rport *rport

SRP 目标端口。

描述

在调用 reconnect() 之前阻止 SCSI 命令排队,以便 queuecommand() 不会与来自 SCSI EH 之外的 reconnect() 并发调用。 这很重要,因为 reconnect() 实现可能会重新分配 queuecommand() 所需的资源。

底层驱动程序可以随时调用它,我们将执行

  • 此函数既不等待未完成的请求完成,也不尝试中止这些请求。 在重新连接到目标端口之前,reconnect() 函数有责任完成未完成的命令。

  • 调用者有责任确保 reconnect() 函数重新分配的资源在此函数进行过程中不会被使用。 一种可能的策略是从 SCSI EH 线程的上下文中调用此函数。 另一种可能的策略是在每个可由 SCSI EH 调用的 SCSI LLD 回调中锁定 rport 互斥锁(scsi_host_template.eh_*() 函数以及 scsi_host_template.queuecommand() 函数)。

enum scsi_timeout_action srp_timed_out(struct scsi_cmnd *scmd)

SCSI 超时 EH 的 SRP 传输拦截

参数

struct scsi_cmnd *scmd

SCSI 命令。

描述

如果在 rport 处于阻止状态时发生超时,请要求 SCSI EH 继续等待 (SCSI_EH_RESET_TIMER)。 否则,让 SCSI 核心处理超时 (SCSI_EH_NOT_HANDLED)。

注意

此函数从软中断上下文中调用,并持有请求队列锁。

void srp_rport_get(struct srp_rport *rport)

增加 rport 引用计数

参数

struct srp_rport *rport

SRP 目标端口。

void srp_rport_put(struct srp_rport *rport)

减少 rport 引用计数

参数

struct srp_rport *rport

SRP 目标端口。

struct srp_rport *srp_rport_add(struct Scsi_Host *shost, struct srp_rport_identifiers *ids)

将 SRP 远程端口添加到设备层次结构

参数

struct Scsi_Host *shost

远程端口连接到的 scsi 主机。

struct srp_rport_identifiers *ids

远程端口的端口 ID。

描述

将端口发布到系统的其余部分。

void srp_rport_del(struct srp_rport *rport)

删除 SRP 远程端口

参数

struct srp_rport *rport

要删除的 SRP 远程端口

描述

删除指定的 SRP 远程端口。

void srp_remove_host(struct Scsi_Host *shost)

拆卸 Scsi_Host 的 SRP 数据结构

参数

struct Scsi_Host *shost

已拆除的 Scsi Host

描述

删除给定 Scsi_Host 的所有 SRP 远程端口。 必须在 SRP HBA 的 scsi_remove_host 之前调用。

void srp_stop_rport_timers(struct srp_rport *rport)

停止传输层恢复计时器

参数

struct srp_rport *rport

要停止计时器的 SRP 远程端口。

描述

必须在 srp_remove_host()scsi_remove_host() 之后调用。 调用者必须持有对 rport (rport->dev) 和 SCSI host (rport->dev.parent) 的引用。

struct scsi_transport_template *srp_attach_transport(struct srp_function_template *ft)

实例化 SRP 传输模板

参数

struct srp_function_template *ft

SRP 传输类函数模板

void srp_release_transport(struct scsi_transport_template *t)

释放 SRP 传输模板实例

参数

struct scsi_transport_template *t

传输模板实例

SCSI 下层

主机总线适配器传输类型

许多现代设备控制器使用 SCSI 命令集作为协议,通过许多不同类型的物理连接与其设备进行通信。

在 SCSI 语言中,能够承载 SCSI 命令的总线称为“传输”,而连接到此类总线的控制器称为“主机总线适配器 (HBA)”。

调试传输

文件 drivers/scsi/scsi_debug.c 模拟一个主机适配器,该适配器连接了可变数量的磁盘(或类似磁盘的设备),共享一个公共的 RAM 容量。 执行大量检查以确保我们没有混淆块,并且如果发现任何异常情况,内核会发生 panic。

为了更逼真,模拟设备具有 SAS 磁盘的传输属性。

有关文档,请参阅 http://sg.danny.cz/sg/scsi_debug.html

待办事项

并行(快速/宽/超宽)SCSI、USB、SATA、SAS、光纤通道、FireWire、ATAPI 设备、Infiniband、并行端口、netlink...