sd
(SCSI Disk)驱动,管理磁盘设备。ufs-hcd
)。用户层(读写文件)→ 内核 VFS → 块层(Block Layer)→ SCSI 子系统 → UFS 子系统 → UFS 硬件
READ_10
命令。READ_10
封装为 UPIU 格式。scsi_scan
模块)。struct scsi_cmnd
:表示一个 SCSI 命令。struct Scsi_Host
:表示一个 SCSI 主机适配器。ufshcd.c
:UFS 主机控制器驱动核心。ufs_quirks.c
:处理不同厂商的硬件兼容性问题。dma_alloc_coherent()
分配内存,实现高速数据传输。drivers/scsi/
sd.c
:SCSI 磁盘驱动。scsi_debug.c
:调试工具。libsas/
:SAS(Serial Attached SCSI)驱动。drivers/ufs/
ufs-hcd.c
:主机控制器驱动。ufs-mediatek.c
:某厂商的 UFS 控制器实现(如联发科)。ufs.h
:UFS 协议定义和数据结构。lsscsi
:列出所有 SCSI 设备。sg_utils
:发送原始 SCSI 命令。ufs-utils
:UFS 专用调试工具。dmesg
输出,过滤关键字 scsi
或 ufs
。dmesg | grep -E "scsi|ufs"
sysfs
修改 /sys/block/sdX/queue/nr_requests
。ufshcd
参数)。通过这种分层设计,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 等)
代表模块: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, // 请求完成回调
};
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、长度等参数
}
核心模块: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
管理多队列请求(现代内核)。scsi_eh
线程)。 // 错误恢复线程(drivers/scsi/scsi_error.c)
void scsi_error_handler(void) {
while (!kthread_should_stop()) {
// 检查错误并尝试恢复(重置设备、重试命令等)
}
}
scsi_scan_host()
扫描 SCSI 总线,识别新设备。/dev/sda
)。代表模块:UFS 主机控制器驱动(如 ufs-hcd.c
)、SAS 驱动(如 mpt3sas
)
作用:将 SCSI 命令转换为硬件特定的操作(如 UFS UPIU、SATA FIS)。
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, // 中止命令
};
// 转换 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 等字段
}
| 层 | 接口/数据结构 | 功能描述 | |——————|——————————-|————————————————————————–| | 块层 → SCSI 高层 | struct bio
→ struct scsi_cmnd
| 将块请求(如读/写)转换为 SCSI 命令。 | | SCSI 高层 → 中间层 | scsi_execute()
| 提交 SCSI 命令到中间层队列。 | | 中间层 → 底层驱动 | struct scsi_host_template
| 底层驱动实现的回调函数(如 .queuecommand
),用于实际发送命令到硬件。 | | 底层驱动 → 硬件 | UPIU(UFS) / FIS(SATA) | 将 SCSI 命令编码为硬件协议格式,通过寄存器或 DMA 传输到控制器。 |
read()
系统调用。struct bio
,提交到块设备队列。bio
转换为 struct scsi_cmnd
,填充 READ_10
命令。ufshcd_queuecommand()
,将 scsi_cmnd
转换为 UPIU。scsi_logging
内核参数启用调试: echo 1 > /sys/module/scsi_mod/parameters/logging_level
dmesg
输出中的 SCSI 命令和状态。CONFIG_SCSI_UFS_DEBUG
): // 在代码中手动添加调试打印(drivers/ufs/ufshcd.c)
ufshcd_dump_regs(hba, 0, UFSHCI_REG_SPACE_SIZE, "UFS Registers:");
drivers/scsi/sd.c
drivers/scsi/scsi_lib.c
, drivers/scsi/scsi_error.c
drivers/ufs/ufshcd.c
, drivers/ufs/ufs.h
sd.c
)负责与块层对接,处理通用块设备逻辑。struct scsi_host_template
和 struct scsi_cmnd
实现层间解耦。这种分层设计使得 Linux 能够支持多种存储协议(SCSI、SATA、UFS 等),同时保持代码的可维护性和扩展性。