3、DMA传输原理:DMA控制器架构、描述符机制、数据流路径

在UFS(Universal Flash Storage)子系统中,DMA(Direct Memory Access)是决定传输性能的关键引擎。CPU如果逐字节搬运数据,不仅会占满总线带宽,还会让协议栈无法及时处理命令队列。DMA控制器通过硬件描述符驱动,把数据从主机内存搬到UFS设备内部缓存,或反向搬运,从而释放CPU算力。理解DMA控制器架构、描述符机制和数据流路径,是后续调优吞吐与延迟的基础。

一、DMA控制器架构:从主机到UFS的搬运中枢

UFS的DMA通常位于主机控制器(Host Controller)内部,与UTP(UFS Transport Protocol)层、UniPro链路层和物理层协同工作。典型架构包含以下核心单元:

  • 命令队列接口(CQ Interface):接收软件下发的UTP命令描述符(UTRD),解析读写方向、数据长度和DMA地址。
  • DMA引擎(DMA Engine):负责实际的数据搬运,支持多通道、多描述符并行处理,常见有读通道和写通道分离。
  • 描述符缓存(Descriptor Cache):预取并缓存物理区域描述符(PRDT),减少对主机内存的频繁访问。
  • 地址转换单元(IOMMU/SMMU):将设备可见的IOVA转换为物理地址,保证DMA访问的安全与隔离。
  • 中断/完成队列(Completion Queue):DMA完成后写入完成状态,通知驱动回收资源。

这些单元通过内部总线与UniPro控制器连接。DMA引擎并不直接理解文件系统语义,它只认描述符中的地址、长度和方向。因此,描述符的组织方式直接决定了DMA的效率和并发能力。

UFS DMA 控制器架构与数据流路径 主机内存 UTRD 命令描述符 PRDT 数据描述符 UFS 主机控制器 命令队列接口 CQ 描述符缓存 PRDT Cache DMA 引擎(读/写通道) IOMMU / SMMU 地址转换 完成队列 CQ UFS 设备 UniPro 链路层 UTP 传输层 NAND 缓存 / 存储阵列 命令/描述符 数据地址 数据搬运 状态/完成 图例: 主机内存 控制器内部单元 DMA 引擎 地址转换 UFS 设备 数据流:主机内存 → 控制器 DMA 引擎 → UniPro → UFS 设备;完成状态反向回传。

二、描述符机制:DMA的“任务清单”

DMA控制器不会自己知道要搬什么,它依赖描述符。UFS中常见的描述符分为两类:

  • UTRD(UTP Transfer Request Descriptor):描述一次传输请求,包含命令类型(读/写)、数据长度、方向、PRDT偏移等。
  • PRDT(Physical Region Description Table):描述数据缓冲区的物理分散/聚集列表,每个条目包含基地址和长度。

描述符通常放在主机内存中,由驱动填充后写入命令队列。DMA引擎通过“门铃”寄存器触发,然后读取描述符并执行。描述符的格式和数量直接影响DMA的启动延迟和吞吐。

/* 简化 UTRD 结构示意 */
struct utrd {
    __le32  dw0;        // 命令类型、方向、中断使能
    __le32  dw1;        // 数据长度
    __le64  prdt_offset; // PRDT 在内存中的偏移
    __le32  prdt_length; // PRDT 条目数
    __le32  reserved;
};

/* PRDT 条目 */
struct prdt_entry {
    __le64  base_addr;  // 数据缓冲区物理地址
    __le32  length;     // 传输长度
    __le32  reserved;
};

描述符机制的关键优化点在于:减少描述符数量、提高单条描述符覆盖长度、保证地址连续或合理分散。如果PRDT条目过多,DMA引擎需要频繁读取描述符,增加启动开销;如果单条长度过小,则无法充分利用总线突发传输。

提示:在Linux UFS驱动中,可以通过调整 prdt_count 和 sg_tablesize 来平衡描述符数量与内存碎片。对于大块连续传输,尽量合并PRDT条目。

三、数据流路径:从命令到完成的完整链路

一次典型的UFS DMA读传输,数据流路径如下:

  1. 命令下发:驱动构造UTRD和PRDT,写入主机内存,然后写门铃寄存器通知控制器。
  2. 描述符获取:控制器CQ接口读取UTRD,DMA引擎解析PRDT并缓存。
  3. 地址转换:IOMMU将PRDT中的IOVA转换为物理地址,检查权限。
  4. 数据搬运:DMA引擎向UFS设备发起读请求,设备将数据通过UniPro链路返回,DMA引擎按PRDT条目写入主机内存。
  5. 完成通知:所有数据搬完后,DMA引擎写入完成队列,触发中断,驱动回收描述符和缓冲区。

写传输方向相反:主机内存数据先被DMA引擎读出,经UniPro发送到UFS设备,设备写入NAND后返回状态。无论读写,描述符解析、地址转换和数据搬运是三个串行阶段,任何一段出现瓶颈都会拖慢整体性能。

四、案例与场景:描述符配置对吞吐的影响

假设某平台UFS 3.1,理论链路带宽约11.6 Gbps。测试顺序读,块大小128 KB,使用不同PRDT策略:

策略 PRDT条目数 单条长度 实测吞吐 CPU占用
碎片化描述符 32 4 KB 约 2.1 GB/s 较高
中等合并 8 16 KB 约 3.4 GB/s 中等
大块连续 1 128 KB 约 4.2 GB/s 低

从表中可见,减少PRDT条目、增大单条长度,能显著提升吞吐并降低CPU占用。原因是DMA引擎读取描述符的次数减少,地址转换开销降低,总线突发效率提高。

注意:不要盲目追求单条超大描述符。如果内存碎片严重,强行合并会导致分配失败或延迟抖动。应结合IOMMU映射粒度和内存分配策略综合调整。

五、常见误区与复盘

  • 误区一:DMA不需要CPU参与。实际上描述符准备、门铃触发、完成中断处理都需要CPU,优化目标是减少CPU介入频率,而非完全消除。
  • 误区二:描述符越多越灵活。过多描述符会增加DMA引擎的解析负担和内存带宽占用,反而降低有效吞吐。
  • 误区三:忽略IOMMU开销。每次地址转换都有TLB查找成本,大页映射和IOVA连续性可以显著降低这部分开销。
  • 复盘要点:用性能计数器观察DMA引擎利用率、描述符读取次数、IOMMU miss率,定位是描述符瓶颈还是链路瓶颈。