本文主要是介绍深入浅出SCSI子系统(一)Linux 内核中的 SCSI 架构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
深入浅出SCSI子系统(一)Linux 内核中的 SCSI 架构
1.Linux 内核中的 SCSI 架构描述
2.SCSI 较高层
3.SCSI 中间层
4.SCSI 较低层
深入浅出SCSI子系统(一)Linux 内核中的 SCSI 架构
1.Linux 内核中的 SCSI 架构描述
SCSI 子系统在 Linux 内核中的位置:内核的顶部是系统调用接口,处理用户空间调用到内核中合适的目的地的路由(例如 open、read 或 write)。而虚拟文件系统(VFS) 是内核中支持的大多数文件系统的抽象层。它负责将请求路由到合适的文件系统。大多数文件系统都通过缓冲区缓存来相互通信,这种缓存通过缓存最近使用的数据来优化对物理设备的访问。接下来是块设备驱动器层,它包括针对底层设备的各种块驱动器。SCSI 子系统是这种块设备驱动器之一
与 Linux 内核中的其他主流子系统不同,SCSI 子系统是一种分层的架构,共分为三层。顶部的那层叫做较高层,代表的是内核针对 SCSI 和主要设备类型的驱动器的最高接口。接下来的是中间层,也称为公共层或统一层。在这一层包含 SCSI 堆栈的较高层和较低层的一些公共服务。最后是较低层,代表的是适用于 SCSI 的物理接口的实际驱动器
在 ./linux/drivers/scsi 可以找到 SCSI 子系统(SCSI 较高层、中间层和各种驱动器)的源代码。SCSI 数据结构则位于 SCSI 源目录,在 ./linux/include/scsi 也可以找到。比如sd.c sg.c等文件。
Linux SCSI模型基于传统的并行SCSI。主机适配器连接主机I/O总线(通常是PCI总线)和存储I/O总线(这里是SCSI总线)。一台计算机可以有多个主机适配器,而主机适配器可以控制一个(即所谓的单通道适配器)或多个(即所谓的多通道适配器)SCSI总线,一条总线可以有多个目标节点与之相连,并且一个目标节点可以有多个逻辑单元。
在Linux语义中,用SCSI设备描述符表示具体的逻辑单元,这和我们通常谈到的SCSI设备(例如SCSI磁盘)是不一样的。就SCSI磁盘整体而言,它应该是目标节点。在SCSI磁盘内可以有多个逻辑单元,统一由磁盘控制器控制,这些逻辑单元才是真正作为I/O终点的存储设备。但是,为简单起见,现在大多数厂商生产的磁盘只做了基本实现,即实现了LUN0。
在Linux中,使用四元组<host:channel:id:lun>来唯一定位一个SCSI设备。
其中,host为主机适配器在计算机内部的编号,一般的实现方式是,在系统中定义一个全局变量,每发现一个主机适配器,将这个全局变量值赋值给host编号,同时递增该全局变量;channel则为SCSI通道编号,或者为SCSI总线编号,这是相对主机适配器来说的,应该由主机适配器的固件来维护;id为目标节点标识符;lun为在目标节点内的逻辑单元编号。
对于并行SCSI总线,其所能挂接的设备数目是受物理层,即总线宽度,限制的。
SCSI子系统(包括高层、中间层和低层)的源代码位于目录drivers/scsi/下,头文件在include/scsi/目录下。SCSI子系统的主要功能是:
1.探测SCSI设备,在内存建立可供设备驱动使用的核心结构;
2.在sysfs文件系统中构造SCSI子系统的目录树;
3.SCSI高层驱动绑定SCSI设备,在内存中构建对应的核心结构;
4.提供错误恢复API,在SCSI命令错误和超时后被调用。
2.SCSI 较高层
SCSI 子系统的较高层代表的是内核(设备级)最高级别的接口。它由一组驱动器组成,比如块设备(SCSI 磁盘和 SCSI CD-ROM)和字符设备(SCSI 磁带和 SCSI generic)。较高层接受来自上层(比如 VFS)的请求并将其转换成 SCSI 请求。较高层负责完成 SCSI 命令并将状态信息通知上层。
SCSI 磁盘驱动器在 ./linux/drivers/scsi/sd.c 内实现。SCSI 磁盘驱动器通过调用 register_blkdev(作为块驱动器)进行自初始化并通过 scsi_register_driver 提供一组函数以表示所有 SCSI 设备。其中 sd_probe 和 sd_init_command 这两个函数很重要。只要有新的 SCSI 设备附加到系统, SCSI 中间层就会调用 sd_probe 函数。sd_probe 函数可决定此设备是否由 SCSI 磁盘驱动器管理,如果是,就创建新的 scsi_disk 结构来表示它。sd_init_command 函数将来自文件系统层的请求转变成 SCSI 读或写命令(为完成这个 I/O 请求,sd_rw_intr 会被调用)。
SCSI 磁带驱动器在 ./linux/drivers/scsi/st.c 内实现。磁带驱动器是顺序存取设备,会通过 register_chrdev_region 将自身注册为字符设备。SCSI 磁带驱动器还提供了一个 probe 函数,称为 st_probe。该函数会创建一种新磁带设备并将其添加到称为 scsi_tapes 的向量。SCSI 磁带驱动器的独特之处在于,如果可能,它可以直接从用户空间执行 I/O 传输。否则,数据会通过驱动器缓冲被分段。
SCSI CD-ROM 驱动器在 ./linux/drivers/scsi/sr.c 内实现。CD-ROM 驱动器是另一种块设备并为 SCSI 磁盘驱动器提供类似的函数集。sr_probe 函数可用来创建 scsi_sd 结构以表示 CD-ROM 设备,并用 register_cdrom 注册此 CD-ROM。SCSI 磁带驱动器还会导出 sr_init_command,以将请求转换成 SCSI CD-ROM 读或写请求。
SCSI generic 驱动器在 ./linux/drivers/scsi/sg.c 内实现。该驱动器允许用户应用程序向设备发送 SCSI 命令(比如格式化、模式感知或诊断命令)。通过 sg3utils 包还可以从用户空间利用 SCSI generic 驱动器。这个用户空间包包括多种实用工具,可用来发送 SCSI 命令和解析这些命令的响应。
3.SCSI 中间层
SCSI 中间层是 SCSI 较高层和较低层的公共服务层(可以在 ./linux/drivers/scsi/scsi.c 内部分地实现)。它提供了很多可供较高层和较低层驱动器使用的函数,因而可以充当这两层间的连接层。中间层很重要,原因是它抽象化了较低层驱动器(LLD)的实现,可以在 ./linux/drivers/scsi/hosts.c 中部分地实现。这意味着可以以同样的方式使用带不同接口的 Fibre Channel 主机总线适配器(HBA)。
低层驱动器注册和错误处理都由 SCSI 中间层提供。中间层还提供了较高层和较低层间的 SCSI 命令排队。SCSI 中间层的一个重要功能是将来自较高层的命令请求转换成 SCSI 请求。它也负责管理特定于 SCSI 的错误恢复。
中间层可以连接 SCSI 子系统的较高层和较低层。它接受对 SCSI 事务的请求并对这些请求进行排队以便处理 (如 ./linux/drivers/scsi/scsi_lib.c 中所示)。当这些命令完成后,它接受来自 LLD 的 SCSI 响应并通知较较高层此请求已经完成。
中间层最重要的职责之一是错误和超时处理。如果 SCSI 命令没有在合理的时间内完成或者 SCSI 请求返回错误,中间层就会管理错误或重新发送此请求。中间层还可管理较高层恢复,比如请求 HBA (LLD) 或 SCSI 设备重置。SCSI 错误和超时处理程序在 ./linux/drivers/scsi/scsi_error.c 内实现。
4.SCSI 较低层
在最低层的是一组驱动器,称为 SCSI 低层驱动器。它们是一些可与物理设备(比如 HBA)链接的特定驱动器。LLD 提供了自公共中间层到特定于设备的 HBA 的一种抽象。每个 LLD 都提供了到特定底层硬件的接口,但所使用的到中间层的接口却是一组标准接口。
较低层包含大量代码,原因是它要负责处理各种不同的 SCSI 适配器类型。例如,Fibre Channel 协议包含了针对 Emulex 和 QLogic 的各种适配器的 LLD。面向 Adaptec 和 LSI 的 SAS 适配器的 LLD 也包括在内。
这篇关于深入浅出SCSI子系统(一)Linux 内核中的 SCSI 架构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!