3、UFS驱动架构:Linux内核UFS子系统、主机控制器驱动、设备驱动、块层交互
UFS驱动适配不是“写一个驱动文件”那么简单,它是一条从块层到物理链路的完整软件栈。理解这条栈的分层职责与数据流向,是后续调试、性能调优和兼容性适配的基础。本节从整体架构入手,逐层拆解Linux内核UFS子系统的组成与交互关系。
一、整体分层:从块层到物理链路
Linux内核中的UFS存储路径自上而下可分为四层:块层(Block Layer)、UFS设备驱动(UFS Device Driver,即ufs_core)、主机控制器驱动(Host Controller Driver,HCD,如ufshcd、ufs-mediatek、ufs-qcom)以及UniPro/PHY物理层。每一层都有明确的职责边界,层与层之间通过标准接口通信。
块层负责通用块设备抽象,接收文件系统下发的bio请求,进行合并、排序、调度后生成request。UFS设备驱动位于drivers/ufs/core/,负责SCSI命令封装、UFS协议描述符管理、电源模式控制、错误处理等与设备语义相关的工作。主机控制器驱动位于drivers/ufs/host/,负责寄存器操作、DMA映射、中断处理、时钟与电源域管理,是真正“碰硬件”的一层。UniPro/PHY则完成M-PHY电气层与UniPro链路层的初始化、速率协商和Lane配置。
二、各层核心职责与关键接口
块层是UFS对上层文件系统的统一入口。它通过blk-mq多队列机制将I/O请求分发到不同软件队列,再由SCSI中间层转换为SCSI命令。UFS设备在块层表现为一个标准SCSI磁盘设备(如/dev/sda),因此文件系统无需感知UFS协议细节。
UFS设备驱动(ufs_core)是UFS子系统的中枢。它维护UFS设备描述符(Device Descriptor、Configuration Descriptor、Unit Descriptor等),管理设备电源模式(Active、Idle、Sleep、PowerDown),处理UFS查询请求(Query Request),并实现错误恢复流程。它还通过ufs_bsg提供SG_IO透传接口,供用户态工具(如ufs-utils)发送原始UFS命令。
主机控制器驱动(HCD)直接操作UFS主机控制器寄存器。它负责UTRD(UFS Transfer Request Descriptor)和UTMR(UFS Task Management Request)的DMA内存分配与映射,处理控制器中断,管理时钟、复位和电源域。不同SoC厂商的HCD在drivers/ufs/host/下以独立文件存在,共享ufshcd.c中的通用框架。
UniPro/PHY层完成M-PHY的电气初始化、Lane数量配置和高速率协商。它通常由SoC厂商的PHY驱动和UniPro驱动共同实现,通过phy子系统和ufs_hcd的setup_clocks、hce_enable等回调接入。
三、数据流与控制流:一次读请求的完整路径
以一次read()系统调用为例,数据流路径如下:
- 文件系统将读请求提交给块层,块层生成
struct request并放入blk-mq队列。 - SCSI中间层将request转换为SCSI命令(如
READ_10),并调用UFS设备驱动的queuecommand回调。 ufs_core将SCSI命令封装为UTRD,通过ufshcd_send_command提交给HCD。- HCD将UTRD写入控制器DMA内存,触发控制器发送命令,并等待中断。
- 控制器通过UniPro/PHY将命令发送到UFS设备,设备返回数据后,HCD处理完成中断,回收UTRD。
- 数据经SCSI层返回块层,最终拷贝到用户缓冲区。
控制流则包括设备枚举、描述符读取、电源模式切换和错误恢复。例如,当链路出现错误时,ufs_core会触发ufshcd_err_handler,依次执行链路复位、主机控制器复位和设备重新初始化。
四、关键文件与配置速查
| 层级 | 关键文件/目录 | 核心职责 |
|---|---|---|
| 块层 | block/blk-mq.c、drivers/scsi/scsi_lib.c |
请求合并、调度、SCSI命令转换 |
| UFS设备驱动 | drivers/ufs/core/ufshcd.c、ufs_bsg.c |
描述符管理、电源模式、错误恢复、SG_IO |
| 主机控制器驱动 | drivers/ufs/host/ufs-mediatek.c、ufs-qcom.c |
寄存器操作、DMA、中断、时钟/电源域 |
| UniPro/PHY | drivers/phy/、drivers/ufs/host/ufs-*-phy.c |
M-PHY初始化、速率协商、Lane配置 |
| 用户态工具 | ufs-utils、sg3_utils |
发送UFS查询、描述符读写、健康信息 |
五、常见误区与适配复盘
误区一:把HCD当成设备驱动。HCD只负责主机控制器硬件操作,不解析UFS描述符,也不管理设备电源模式。设备语义相关的工作全部在ufs_core中完成。适配时若在HCD中硬编码设备参数,会导致跨设备兼容性变差。
误区二:忽略块层队列深度与UFS命令队列的关系。UFS支持命令队列(CQ),但块层blk-mq的队列深度需要与HCD的can_queue、nr_hw_queues匹配。若配置不当,会出现命令超时或队列饥饿。
复盘建议:适配新平台时,先确认ufs_core与HCD的接口回调是否完整实现,再验证UniPro/PHY的速率协商日志。使用dmesg | grep ufs观察链路初始化过程,用cat /sys/kernel/debug/ufs/下的调试节点检查描述符和电源状态。
# 查看UFS设备与主机控制器信息
dmesg | grep -i ufs
cat /sys/class/scsi_device/*/device/model
cat /sys/kernel/debug/ufs/ufs_dev_info
# 使用ufs-utils读取设备描述符
ufs-utils device_descriptor /dev/sda
理解这四层架构与数据流,是后续进行UFS驱动移植、性能调优和故障定位的前提。每一层的职责边界清晰,层间接口稳定,才能在面对不同SoC和UFS设备时快速定位问题所在。