好的!我将用通俗易懂的方式为你总结 Linux 内核中 UFS(Universal Flash Storage) 和 SCSI(Small Computer System Interface) 子系统的设计逻辑和协作关系。这两个子系统共同管理现代存储设备(如固态硬盘、移动设备存储等)。
一、核心概念
1. SCSI 子系统
- 作用:SCSI 是一种广泛使用的存储协议标准,用于管理磁盘、SSD、光驱等设备。Linux 的 SCSI 子系统负责将通用块设备请求转换为具体的 SCSI 命令。
- 分层设计:
- 高层驱动:如
sd(SCSI Disk)驱动,管理磁盘设备。 - 中间层:处理 SCSI 命令队列、错误恢复、设备发现等。
- 低层驱动:适配具体硬件(如 SAS、SATA、UFS 等)。
- 高层驱动:如
2. UFS 子系统
- 作用:UFS 是面向移动设备和嵌入式系统的高速闪存协议(如手机存储)。Linux 的 UFS 子系统通过 SCSI 命令与硬件交互。
- 关键组件:
- UFS 主机控制器驱动:管理 UFS 硬件控制器(如
ufs-hcd)。 - UFS 核心层:处理协议逻辑(如命令封装、错误处理)。
- SCSI 传输层:将 UFS 操作映射到 SCSI 命令。
- UFS 主机控制器驱动:管理 UFS 硬件控制器(如
二、协作关系
1. UFS 作为 SCSI 的"客户端"
- UFS 设备在 Linux 中被抽象为 SCSI 设备,通过 SCSI 中间层与内核其他部分交互。
- 数据流:
用户层(读写文件)→ 内核 VFS → 块层(Block Layer)→ SCSI 子系统 → UFS 子系统 → UFS 硬件
2. SCSI 命令到 UFS 的转换
- SCSI 命令(如读/写)会被 UFS 子系统转换为 UFS Protocol Information Units (UPIU)。
- 示例:用户发起一个读操作:
- SCSI 中间层生成
READ_10命令。 - UFS 核心层将
READ_10封装为 UPIU 格式。 - UFS 主机控制器通过硬件接口发送 UPIU 到 UFS 设备。
- SCSI 中间层生成
三、关键设计细节
1. SCSI 中间层(Mid-Layer)
- 功能:
- 管理 SCSI 命令队列(合并、排序、超时处理)。
- 设备发现和热插拔(通过
scsi_scan模块)。 - 错误恢复(如自动重试、重置设备)。
- 数据结构:
struct scsi_cmnd:表示一个 SCSI 命令。struct Scsi_Host:表示一个 SCSI 主机适配器。
2. UFS 核心层
- 功能:
- 封装 UPIU(命令、数据、响应)。
- 管理 UFS 设备的电源状态(如休眠、激活)。
- 处理错误和异常(如 CRC 校验失败)。
- 关键模块:
ufshcd.c:UFS 主机控制器驱动核心。ufs_quirks.c:处理不同厂商的硬件兼容性问题。
3. 内存管理
- SCSI 和 UFS 使用 DMA:通过
dma_alloc_coherent()分配内存,实现高速数据传输。 - Scatter-Gather 列表:支持分散/聚集 I/O,提升大块数据传输效率。
四、代码结构(Linux 内核)
1. SCSI 子系统
- 目录:
drivers/scsi/sd.c:SCSI 磁盘驱动。scsi_debug.c:调试工具。libsas/:SAS(Serial Attached SCSI)驱动。
2. UFS 子系统
- 目录:
drivers/ufs/ufs-hcd.c:主机控制器驱动。ufs-mediatek.c:某厂商的 UFS 控制器实现(如联发科)。ufs.h:UFS 协议定义和数据结构。
五、调试与工具
1. 常用工具
lsscsi:列出所有 SCSI 设备。sg_utils:发送原始 SCSI 命令。ufs-utils:UFS 专用调试工具。
2. 内核日志
- 查看
dmesg输出,过滤关键字scsi或ufs。 - 示例调试命令:
dmesg | grep -E "scsi|ufs"
3. 性能调优
- 调整队列深度:通过
sysfs修改/sys/block/sdX/queue/nr_requests。 - 电源管理:禁用 UFS 的自动休眠(通过
ufshcd参数)。
六、总结
- SCSI 子系统:是 Linux 存储栈的通用抽象层,支持多种硬件。
- UFS 子系统:通过 SCSI 命令与硬件交互,专注于闪存特性优化。
- 协作流程:用户请求 → 块层 → SCSI 中间层 → UFS 驱动 → 硬件。
通过这种分层设计,Linux 实现了存储协议的统一管理和硬件多样性支持。
如果你有具体问题(如代码逻辑、某个模块的细节),可以进一步讨论!

好的!我将从 高层驱动、中间层、底层驱动 以及它们之间的接口设计角度,详细解释 Linux 中 SCSI 和 UFS 子系统的协作关系。这里会结合代码结构、数据流和实际接口示例。
一、整体分层架构
在 Linux 存储子系统中,各层的分工如下:
用户层(User Space)
↓ 系统调用(read/write)
虚拟文件系统(VFS)
↓ 文件操作(struct file_operations)
块层(Block Layer)
↓ 通用块请求(struct bio)
SCSI 子系统高层驱动(如 sd.c)
↓ SCSI 命令(struct scsi_cmnd)
SCSI 中间层(SCSI Mid-Layer)
↓ SCSI 主机适配器接口(struct Scsi_Host)
SCSI 底层驱动(如 SAS/SATA/UFS 驱动)
↓ 硬件协议(如 UFS UPIU、SATA FIS)
硬件设备(UFS 控制器、SSD 等)
二、各层功能及接口详解
1. 高层驱动(SCSI Upper Layer)
代表模块:sd.c(SCSI Disk 驱动)
作用:将块层的通用块设备请求(struct bio)转换为 SCSI 命令(struct scsi_cmnd)。
关键接口与数据结构:
- 块层接口:
- 通过
struct gendisk和struct request_queue管理块设备。 - 处理块请求的回调函数:
.request_fn或更现代的.queue_rq(多队列驱动)。// 示例:块层请求处理(drivers/scsi/sd.c) static const struct blk_mq_ops sd_mq_ops = { .queue_rq = sd_queue_rq, // 处理块层请求 .complete = sd_complete, // 请求完成回调 };
- 通过
- SCSI 命令生成:
- 将
struct bio转换为struct scsi_cmnd,填充 SCSI 命令(如READ_10,WRITE_10)。// 生成 SCSI 读命令(drivers/scsi/scsi_lib.c) void scsi_init_io(struct scsi_cmnd *cmd) { struct request *req = cmd->request; cmd->cmnd[0] = READ_10; // 操作码 // 填充 LBA、长度等参数 }
- 将
2. SCSI 中间层(SCSI Mid-Layer)
核心模块:scsi_lib.c, scsi_error.c
作用:管理 SCSI 命令的队列、错误处理、设备发现和资源分配。
关键接口与功能:
- 命令提交接口:
- 高层驱动通过
scsi_execute()或scsi_queue_rq()提交命令到中间层。// 提交 SCSI 命令(drivers/scsi/scsi_lib.c) int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, int data_direction, void *buffer, unsigned bufflen, int timeout, int retries, int flags, int *resid);
- 高层驱动通过
- 队列管理:
- 使用
struct blk_mq_tag_set管理多队列请求(现代内核)。 - 实现 I/O 调度(合并、排序)。
- 使用
- 错误处理:
- 中间层检测超时、设备无响应等问题,触发错误恢复(如
scsi_eh线程)。// 错误恢复线程(drivers/scsi/scsi_error.c) void scsi_error_handler(void) { while (!kthread_should_stop()) { // 检查错误并尝试恢复(重置设备、重试命令等) } }
- 中间层检测超时、设备无响应等问题,触发错误恢复(如
- 设备发现与热插拔:
- 通过
scsi_scan_host()扫描 SCSI 总线,识别新设备。 - 创建设备节点(如
/dev/sda)。
- 通过
3. 底层驱动(SCSI Lower Layer / UFS 驱动)
代表模块:UFS 主机控制器驱动(如 ufs-hcd.c)、SAS 驱动(如 mpt3sas)
作用:将 SCSI 命令转换为硬件特定的操作(如 UFS UPIU、SATA FIS)。
关键接口与实现:
- SCSI 主机适配器接口:
- 每个底层驱动注册一个
struct Scsi_Host,表示一个 SCSI 控制器。// 注册 SCSI 主机(drivers/scsi/hosts.c) struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize);
- 每个底层驱动注册一个
- 命令传输接口:
- 实现
struct scsi_host_template中的回调函数,处理 SCSI 命令的发送。// UFS 驱动的传输接口(drivers/ufs/ufshcd.c) static struct scsi_host_template ufshcd_driver_template = { .module = THIS_MODULE, .name = "UFS Host Controller", .queuecommand = ufshcd_queuecommand, // 发送命令到硬件 .eh_abort_handler = ufshcd_abort, // 中止命令 };
- 实现
- UFS 协议处理:
- 将 SCSI 命令转换为 UFS Protocol Information Units (UPIU)。
- 处理 UFS 的特定功能(如电源管理、错误恢复)。
// 转换 SCSI 命令到 UPIU(drivers/ufs/ufshcd.c) static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) { // 将 scsi_cmnd 转换为 UPIU 结构体 struct utp_upiu_req *upiu_req = ...; upiu_req->header.dword_0 = cpu_to_be32(UPIU_HEADER_TRANSACTION_TYPE_COMMAND); // 填充 LUN、CDB 等字段 }
4. 层间接口总结
| 层 | 接口/数据结构 | 功能描述 | |——————|——————————-|————————————————————————–| | 块层 → SCSI 高层 | struct bio → struct scsi_cmnd | 将块请求(如读/写)转换为 SCSI 命令。 | | SCSI 高层 → 中间层 | scsi_execute() | 提交 SCSI 命令到中间层队列。 | | 中间层 → 底层驱动 | struct scsi_host_template | 底层驱动实现的回调函数(如 .queuecommand),用于实际发送命令到硬件。 | | 底层驱动 → 硬件 | UPIU(UFS) / FIS(SATA) | 将 SCSI 命令编码为硬件协议格式,通过寄存器或 DMA 传输到控制器。 |
三、关键数据流示例:读取一个磁盘块
- 用户层:调用
read()系统调用。 - VFS:将请求传递给文件系统(如 ext4)。
- 块层:文件系统生成
struct bio,提交到块设备队列。 - SCSI 高层驱动(sd.c):
- 将
bio转换为struct scsi_cmnd,填充READ_10命令。
- 将
- SCSI 中间层:
- 将命令加入队列,处理可能的合并或排序。
- UFS 底层驱动:
- 调用
ufshcd_queuecommand(),将scsi_cmnd转换为 UPIU。 - 通过内存映射寄存器或 DMA 将 UPIU 发送到 UFS 控制器。
- 调用
- 硬件:
- UFS 控制器解析 UPIU,从闪存读取数据,返回响应。
四、调试与代码定位
1. SCSI 命令跟踪
- 使用
scsi_logging内核参数启用调试:echo 1 > /sys/module/scsi_mod/parameters/logging_level - 查看
dmesg输出中的 SCSI 命令和状态。
2. UFS 驱动调试
- 启用 UFS 调试日志(需内核配置
CONFIG_SCSI_UFS_DEBUG):// 在代码中手动添加调试打印(drivers/ufs/ufshcd.c) ufshcd_dump_regs(hba, 0, UFSHCI_REG_SPACE_SIZE, "UFS Registers:");
3. 关键代码文件
- SCSI 高层驱动:
drivers/scsi/sd.c - SCSI 中间层:
drivers/scsi/scsi_lib.c,drivers/scsi/scsi_error.c - UFS 驱动:
drivers/ufs/ufshcd.c,drivers/ufs/ufs.h
五、总结
- 高层驱动(如
sd.c)负责与块层对接,处理通用块设备逻辑。 - 中间层管理命令队列、错误恢复和设备发现,是 SCSI 子系统的核心逻辑。
- 底层驱动(如 UFS)实现硬件协议转换,直接操作控制器寄存器或 DMA。
- 接口设计:通过
struct scsi_host_template和struct scsi_cmnd实现层间解耦。
这种分层设计使得 Linux 能够支持多种存储协议(SCSI、SATA、UFS 等),同时保持代码的可维护性和扩展性。