3. DMA 控制器硬件特性:SG-DMA、双缓冲、环形缓冲区与硬件描述符链

高吞吐 DMA 传输的核心在于硬件如何管理内存中的非连续缓冲区、如何避免处理与传输的相互等待、以及如何高效地提交批量传输任务。本章深入剖析四种关键硬件特性:SG-DMA(分散/聚集)、双缓冲(Ping-Pong)、环形缓冲区以及硬件描述符链。理解这些特性是编写高性能 DMA 驱动的基础。

3.1 分散/聚集 DMA(SG-DMA)

传统块 DMA 要求源和目标地址在物理内存中连续。但在实际系统中,数据包(如网络帧)往往分散存储在多个不连续的物理页中。SG-DMA 通过硬件描述符列表,允许一次 DMA 操作传输多个非连续的内存块,无需 CPU 进行数据搬移和拼接。

核心优势:消除 CPU 参与的“ scatter 收集”和“ gather 拼接”操作,降低延迟,提升吞吐量。

3.1.1 SG-DMA 传输流程

  1. 构建描述符:驱动为每个不连续的内存块创建一个硬件描述符,包含:物理地址、长度、下一个描述符指针。
  2. 链式提交:将描述符链的首地址写入 DMA 控制器的启动寄存器。
  3. 硬件遍历:DMA 控制器自动从链首开始,依次读取每个描述符,执行传输,直到遇到“链结束”标记。
  4. 中断通知:传输完成后(或链中某个描述符完成),DMA 控制器触发中断。

3.1.2 描述符结构对比

特性 块 DMA SG-DMA
内存连续性要求 物理连续 物理不连续
CPU 参与度 高(需拼接/拆分数据) 低(硬件自动遍历)
传输大块数据效率 低(需多次发起 DMA) 高(一次发起,批量传输)
描述符数量 1 个 多个(形成链)

3.1.3 SG-DMA 硬件架构图

SG-DMA 分散/聚集传输架构 DMA 控制器 描述符 1 描述符 2 描述符 3 描述符 N (EOL) Buf 1 (页框 0x1000) Buf 2 (页框 0x2000) Buf 3 (页框 0x5000) Buf N (页框 0xA000) 描述符链中的每个描述符指向一个物理不连续的缓冲区,DMA 硬件自动遍历并传输

3.2 双缓冲(Ping-Pong)

双缓冲机制使用两个缓冲区(Ping 和 Pong)交替工作。当 DMA 正在向一个缓冲区(例如 Ping)写入数据时,CPU 可以安全地处理另一个缓冲区(Pong)中的数据,反之亦然。这种机制有效避免了“处理”和“传输”之间的互斥等待。

适用场景:音频流、视频采集、高速数据采集卡等需要连续、无丢帧传输的场景。

3.2.1 工作状态机

状态 DMA 操作 CPU 操作
初始 DMA 目标地址 = Ping 缓冲区 等待数据
传输中 DMA 向 Ping 写入数据 处理 Pong 中的旧数据(或空闲)
切换 Ping 写满,DMA 自动切换目标到 Pong 收到中断,开始处理 Ping 数据
交替 DMA 向 Pong 写入数据 处理 Ping 数据

3.2.2 Ping-Pong 切换流程图

Ping-Pong 双缓冲切换流程 Ping 缓冲区 Pong 缓冲区 DMA 引擎 阶段 1: 写入 阶段 2: 写入 中断触发切换

3.3 环形缓冲区(Ring Buffer)

环形缓冲区是一种固定大小的、首尾相连的缓冲区。DMA 控制器和 CPU 通过维护独立的“头指针”(生产者)和“尾指针”(消费者)来管理数据。当指针到达缓冲区末尾时,自动回绕到起始位置。这是网络驱动(如 NAPI)和音频驱动中最常用的数据交换模型。

3.3.1 关键指针与状态

  • 生产者指针(DMA 写指针):DMA 硬件当前正在写入的位置。
  • 消费者指针(CPU 读指针):CPU 当前正在读取或已处理到的位置。
  • 满状态:生产者指针追上消费者指针(缓冲区满,DMA 应暂停或丢弃数据)。
  • 空状态:消费者指针追上生产者指针(缓冲区空,CPU 无数据可处理)。

3.3.2 环形缓冲区结构图

环形缓冲区(Ring Buffer)结构 槽 0 槽 1 槽 2 槽 3 槽 4 槽 5 槽 7 槽 6 回绕 写指针 (DMA) 读指针 (CPU)

3.4 硬件描述符链

硬件描述符链是 SG-DMA 和环形缓冲区的底层实现基础。每个描述符是一个固定格式的数据结构,通常包含控制位、状态位、缓冲区地址、传输长度以及指向下一个描述符的指针。描述符链的末端通过“EOL”(End of List)标记或回环指针来标识。

3.4.1 典型描述符格式

struct dma_descriptor {
    u32 phys_addr;      // 缓冲区物理地址
    u32 length;         // 传输长度(字节)
    u32 control;        // 控制位:中断使能、链结束、方向等
    u32 status;         // 状态位:完成、错误等(由硬件更新)
    u32 next_desc;      // 下一个描述符的物理地址(0 表示链结束)
} __attribute__((packed));

3.4.2 描述符链管理策略

策略 描述 优点 缺点
静态预分配 驱动初始化时分配固定数量的描述符,形成环形链 无动态内存开销,延迟低 灵活性差,描述符数量固定
动态追加 驱动在运行时动态分配描述符并追加到链尾 灵活性高,可应对突发流量 动态分配可能引入延迟和碎片
回环链 最后一个描述符的 next 指针指向第一个描述符 硬件自动循环,无需软件干预 需要谨慎处理满/空状态

3.4.3 描述符链工作流

硬件描述符链工作流 1. 驱动构建描述符 2. 写入 DMA 启动地址 3. 硬件遍历描述符链 4. 触发完成中断
性能陷阱:描述符必须位于非缓存(non-cacheable)或一致性内存区域,否则 CPU 对描述符的更新可能不会被 DMA 硬件及时看到,导致传输错误。在 ARM 平台上,通常使用 dma_alloc_coherent() 分配描述符内存。

3.5 四种特性的协同与选择

在实际的 DMA 控制器中,这四种特性往往组合使用。例如,一个高性能网络控制器可能使用环形缓冲区管理描述符,每个描述符本身是一个SG 描述符,指向一个不连续的 sk_buff 数据区,并通过双缓冲机制让 CPU 和 DMA 并行操作不同的描述符环。

特性 解决的核心问题 典型应用
SG-DMA 非连续内存的批量传输 网络驱动、存储驱动
双缓冲 处理与传输的流水线并行 音频、视频、数据采集
环形缓冲区 固定大小的流式数据管理 NAPI、USB、SPI
硬件描述符链 批量传输任务的提交与调度 所有高性能 DMA 引擎