3、UFS驱动架构:Linux内核UFS子系统、主机控制器驱动、设备驱动、块层交互

UFS驱动适配之所以容易出问题,根本原因在于它并不是“一个驱动”,而是一条从块层到物理链路的完整软件栈。任何一层参数不匹配、时序不满足或状态机不同步,都会表现为上层I/O超时、挂载失败或性能异常。理解这条栈的分层职责与数据流,是后续排障的基础。

一、核心原理:四层协作模型

Linux内核中的UFS子系统自上而下可分为四层,每层职责清晰、接口固定:

  • 块层(Block Layer):接收文件系统下发的bio请求,完成合并、排序、调度,并通过请求队列(blk-mq)将请求下发给SCSI层。
  • SCSI中间层:UFS设备在Linux中被抽象为SCSI设备,因此UFS驱动通过SCSI命令集(如READ(10)、WRITE(10)、UNMAP)与上层交互,SCSI层负责命令排队、错误处理和超时重试。
  • UFS设备驱动(ufs_core / ufs_hcd):即UFS子系统核心,负责UFS协议栈实现,包括UIC命令、UPIU封装、描述符读写、电源模式管理、RPMB、健康信息等。
  • 主机控制器驱动(HCD):如ufs-qcom、ufs-mediatek、ufs-exynos等,直接操作SoC的UFS控制器寄存器,完成M-PHY初始化、UniPro链路建立、DMA传输和中断处理。

四层之间通过标准接口衔接:块层→SCSI层使用request_queue;SCSI层→UFS设备驱动使用scsi_host_template;UFS设备驱动→HCD使用ufs_hba和ufs_hcd_ops。适配工作的核心,就是让这四层的参数、时序和状态保持一致。

Linux UFS 驱动分层架构与数据流 块层 Block Layer bio → blk-mq 请求队列 → 合并/排序/调度 → scsi_request_fn request_queue SCSI 中间层 SCSI Mid Layer 命令排队 · 错误处理 · 超时重试 · scsi_host_template · queuecommand scsi_cmnd UFS 设备驱动 ufs_core / ufs_hcd UIC 命令 · UPIU 封装/解析 · 描述符读写 · 电源模式管理 · RPMB · 健康信息 ufs_hba 结构体 · ufs_hcd_ops 回调 · 任务标签管理 · 错误恢复 ufs_hba 主机控制器驱动 HCD(ufs-qcom / ufs-mediatek / ufs-exynos) M-PHY 初始化 · UniPro 链路建立 · 寄存器操作 · DMA 传输 · 中断处理 时钟/电源/复位控制 · 厂商特定校准 · 链路启动序列 ufs_hcd_ops 数据流方向:文件系统 → 块层 → SCSI层 → UFS Core → HCD → UFS 设备

二、方法流程:一次I/O请求的完整路径

以一次文件读取为例,请求在栈中的流转过程如下:

  1. 文件系统调用submit_bio,将bio提交到块层。
  2. 块层通过blk-mq将bio转换为request,并调用SCSI层的queuecommand。
  3. SCSI层构造scsi_cmnd,调用UFS设备驱动注册的queuecommand回调。
  4. UFS设备驱动将SCSI命令转换为UPIU,写入主机控制器的传输请求列表(UTRL)。
  5. HCD通过UniPro将UPIU发送到UFS设备,设备执行后返回响应UPIU。
  6. HCD收到中断,UFS设备驱动解析响应,完成scsi_cmnd,逐层返回结果。

这条路径中,任何一层的参数错误都会导致请求失败。例如UTRL深度不足会导致命令排队溢出,M-PHY速率不匹配会导致链路错误,电源模式切换时序错误会导致设备无响应。

三、案例/场景:链路启动失败排查

某平台UFS在启动时反复报“link startup failed”,内核日志如下:

[    2.345678] ufs_qcom: ufs_qcom_phy_init: PHY init failed
[    2.345690] ufs_qcom: ufs_qcom_link_startup: link startup failed, retry 1
[    2.345702] ufs_qcom: ufs_qcom_link_startup: link startup failed, retry 2
[    2.345715] ufs_qcom: ufs_qcom_link_startup: link startup failed, retry 3
[    2.345728] ufs_qcom: ufs_qcom_probe: host controller init failed
[    2.345740] ufshcd: ufshcd_probe_hba: hba probe failed, err = -110

排查思路:首先确认HCD的PHY初始化是否成功,检查M-PHY的参考时钟频率、TX/RX均衡参数是否与设备匹配;其次检查UniPro的链路启动序列,确认PA_INIT、PA_SA、PA_ACTIVE等状态是否正常;最后检查设备端的电源模式,确认设备是否处于可响应状态。本例中最终定位为参考时钟频率配置错误,导致M-PHY无法锁定。

提示:链路启动失败时,优先检查HCD的时钟、电源和复位配置,再检查UFS设备的电源模式。大多数“link startup failed”并非设备故障,而是主机侧参数不匹配。

四、误区/复盘:常见适配误区

误区表现正确做法
只改HCD,忽略UFS Core参数链路能启动,但I/O超时同步检查ufs_hba中的caps、quirks和任务标签数
忽略SCSI层超时设置偶发命令超时后设备被复位根据UFS设备特性调整scsi_host_template的timeout值
块层队列深度与UTRL不匹配高负载下命令丢失或性能骤降确保blk-mq队列深度不超过UTRL和UTRD的总容量
电源模式切换未同步设备进入休眠后无法唤醒在UFS Core中正确实现hibern8和power mode change流程
忽略厂商特定校准部分设备能识别,部分不能在HCD中实现厂商要求的PHY校准和链路参数调整
注意:UFS驱动适配不是“改一个文件”的工作。块层、SCSI层、UFS Core和HCD四层必须协同验证。建议在适配新平台时,先确认HCD能完成链路启动,再验证UFS Core能正确读写描述符,最后通过块层进行压力测试。