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配置。

Linux UFS 驱动软件栈与数据流 块层 Block Layer bio → request 合并/调度 · blk-mq 多队列 · SCSI 中间层 scsi_request UFS 设备驱动 ufs_core UFS 协议描述符 · SCSI 命令封装 · 电源模式 · 错误恢复 · 查询请求 ufshcd.c · ufs_bsg.c · ufs_sysfs.c · ufs_debugfs.c 主机控制器驱动 HCD 寄存器读写 · DMA 映射 · 中断处理 · 时钟/电源域 · 变体差异 ufshcd-pltfrm · ufs-mediatek · ufs-qcom · ufs-exynos UniPro / M-PHY 物理层 链路初始化 · 速率协商 HS-G1~G5 · Lane 配置 · 电气特性 文件系统请求 read/write/ioctl SCSI 命令 READ/WRITE/QUERY UTP 传输 UTRD/UTMR 描述符 物理信号 DIF-N/DIF-P block/blk-mq.c scsi/scsi_lib.c ufs_core ufshcd.c ufs-*.c host 目录 phy/ufs unipro 数据流:文件系统 → 块层 → SCSI → ufs_core → HCD → UniPro/PHY → UFS 设备 控制流:设备初始化、电源模式切换、错误恢复由 ufs_core 与 HCD 协同完成

二、各层核心职责与关键接口

块层是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()系统调用为例,数据流路径如下:

  1. 文件系统将读请求提交给块层,块层生成struct request并放入blk-mq队列。
  2. SCSI中间层将request转换为SCSI命令(如READ_10),并调用UFS设备驱动的queuecommand回调。
  3. ufs_core将SCSI命令封装为UTRD,通过ufshcd_send_command提交给HCD。
  4. HCD将UTRD写入控制器DMA内存,触发控制器发送命令,并等待中断。
  5. 控制器通过UniPro/PHY将命令发送到UFS设备,设备返回数据后,HCD处理完成中断,回收UTRD。
  6. 数据经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设备时快速定位问题所在。