Linux DMA描述符链与循环缓冲驱动开发及调优 - 第1节

1.1 DMA 工作原理

直接存储器访问(Direct Memory Access, DMA)是一种允许硬件设备直接与系统内存交换数据而无需 CPU 逐字节干预的机制。在嵌入式 Linux 系统中,DMA 是驱动性能的关键使能器,尤其适用于大数据块搬运、高速外设(如以太网、MMC、显示控制器)以及实时性要求高的场景。

核心思想:CPU 只需发起传输请求,DMA 控制器(DMAC)接管总线控制权,完成数据搬运后通过中断或轮询通知 CPU。CPU 在传输期间可执行其他任务,实现计算与 I/O 并行。

DMA 传输的基本流程

  1. CPU 配置 DMA 通道:设置源地址、目的地址、传输宽度、突发大小、传输方向(内存→外设、外设→内存、内存→内存)。
  2. CPU 启动 DMA:写控制寄存器使能通道,DMAC 获得总线控制权。
  3. DMAC 执行传输:按配置从源地址读取数据,写入目的地址,每完成一个传输单元(或整个块)更新地址指针和计数器。
  4. 传输完成通知:DMAC 触发中断或置位状态寄存器,CPU 响应并处理数据。
DMA 基本工作流程 CPU 配置 DMA 通道 CPU 启动 DMA 传输 DMAC 接管总线搬运数据 传输完成 → 通知 CPU CPU 角色 • 配置源/目的地址 • 设置传输参数 • 使能 DMA 通道 DMAC 角色 • 控制总线仲裁 • 执行地址递增 • 计数传输字节数

1.2 DMA 与 CPU 交互模式

DMA 与 CPU 的交互方式直接影响系统吞吐量和实时性。嵌入式 Linux 驱动中常见的交互模式有三种:中断驱动模式、轮询模式和描述符链表模式。下表对比了它们的核心差异:

交互模式 CPU 负载 实时性 适用场景 典型延迟
中断驱动 低(仅传输完成时响应) 高(立即响应完成事件) 低速外设、非连续传输 几微秒 ~ 几十微秒
轮询模式 高(持续检查状态寄存器) 中(取决于轮询频率) 极高速、可预测的短传输 纳秒级(但浪费 CPU)
描述符链表 极低(批量提交后等待一次中断) 高(支持链式连续传输) 高速流式数据(网络、音视频) 微秒级(批量完成)
驱动开发建议:在 Linux DMA 引擎框架中,dmaengine_prep_slave_sg() 和 dmaengine_submit() 配合描述符链表模式是最常用的高性能路径。轮询模式仅在调试或极低延迟控制回路中使用。

中断驱动模式详解

中断驱动是最经典的 DMA 交互方式。DMAC 完成一次传输后向 CPU 发送中断信号,CPU 在中断服务程序(ISR)中处理数据并准备下一次传输。该模式的关键性能指标是中断延迟和中断合并。

中断驱动 DMA 交互时序 CPU 配置 DMA 执行其他任务 ISR 处理 继续执行 DMAC 数据传输中(总线主控) IRQ 中断触发 CPU 在 DMA 传输期间可执行其他任务,仅在中断触发时短暂介入

1.3 DMA 传输类型

根据数据流向和传输结构,DMA 传输可分为以下三种基本类型。理解这些类型是配置描述符链表和循环缓冲的基础。

1. 内存到内存(Memory-to-Memory, M2M)

  • 数据流:系统内存的一个区域 → 系统内存的另一个区域。
  • 典型用途:数据拷贝、缓冲区移动、图像帧搬运。
  • 特点:无需外设参与,DMAC 完全控制总线;通常使用单次或突发传输。
  • Linux 驱动接口:dmaengine_prep_dma_memcpy()。

2. 内存到外设(Memory-to-Device, M2P)

  • 数据流:系统内存 → 外设 FIFO/寄存器。
  • 典型用途:发送以太网数据包、LCD 显示刷新、音频播放。
  • 特点:外设通常有固定的数据宽度和流控信号;需要处理外设就绪状态。
  • Linux 驱动接口:dmaengine_prep_slave_sg() 配合 DMA_MEM_TO_DEV 方向。

3. 外设到内存(Device-to-Memory, P2M)

  • 数据流:外设 FIFO/寄存器 → 系统内存。
  • 典型用途:接收以太网数据包、摄像头数据采集、音频录制。
  • 特点:外设作为数据源,DMAC 需响应外设请求信号;常与循环缓冲结合实现无丢包采集。
  • Linux 驱动接口:dmaengine_prep_slave_sg() 配合 DMA_DEV_TO_MEM 方向。
传输类型 源端 目的端 外设参与 典型带宽 描述符需求
M2M 内存 内存 无 最高(全内存带宽) 单描述符或链式
M2P 内存 外设 有(流控/握手) 受外设速率限制 循环缓冲描述符链
P2M 外设 内存 有(流控/握手) 受外设速率限制 循环缓冲描述符链
三种 DMA 传输类型示意图 内存区域 A 内存区域 B M2M 数据拷贝 / 缓冲区移动 内存缓冲区 外设(TX FIFO) M2P 以太网发送 / LCD 刷新 / 音频播放 外设(RX FIFO) 内存缓冲区 P2M 以太网接收 / 摄像头采集 / 音频录制
常见误区:M2M 传输虽然带宽最高,但在嵌入式系统中会占用系统总线,影响其他总线主控(如 CPU 取指、GPU 渲染)的访问。实际项目中应优先使用 M2P/P2M 释放 CPU 负载,而非单纯追求 M2M 的峰值带宽。

传输粒度与突发传输

DMA 控制器支持多种传输粒度,驱动配置时需根据外设 FIFO 深度和系统总线宽度选择最优参数:

  • 单次传输(Single Transfer):每次总线事务传输 1 个单元(如 1 字节、2 字节、4 字节)。适合外设 FIFO 较浅或流控严格的场景。
  • 突发传输(Burst Transfer):一次总线事务连续传输多个单元(如 4、8、16 次)。可显著提高总线利用率,但可能增加外设 FIFO 溢出风险。
  • 临界值(Critical Value):外设 FIFO 的阈值设置,决定何时触发 DMA 请求。例如 FIFO 半满时请求一次突发传输。
参数 描述 典型值 性能影响
传输宽度 每次总线事务的数据位宽 8/16/32/64 位 宽度越大,单次吞吐越高
突发长度 一次突发传输包含的单元数 4 / 8 / 16 长度越大,总线效率越高,但延迟增加
FIFO 阈值 触发 DMA 请求的 FIFO 水位 1/4、1/2、3/4 阈值越低,响应越快,但中断频率高
性能调优要点:在循环缓冲模式下,建议将突发长度设置为与 cache line 大小(通常 32 或 64 字节)对齐,避免非对齐访问导致性能下降。同时,FIFO 阈值应保证在 DMA 响应延迟内不会发生下溢(M2P)或上溢(P2M)。