4. DMA基础概念:DMA工作原理、总线主控与从属DMA、DMA传输方向

直接存储器访问(Direct Memory Access, DMA)是高性能PCIe设备驱动开发的基石。它允许外设与系统内存之间直接交换数据,而无需CPU逐字节参与搬运。理解DMA的工作原理、总线主控/从属模式以及传输方向,是后续优化DMA描述符环、实现中断合并的前提。

4.1 DMA工作原理:从CPU轮询到硬件搬运

在没有DMA的传统I/O模式下,CPU必须执行显式的in/out指令或mov指令,从设备寄存器逐字读取数据并写入内存。这种方式在高速数据传输场景下会严重消耗CPU周期,导致系统吞吐量急剧下降。

DMA控制器(DMA Controller)接管了数据搬运任务。其核心工作流程如下:

  1. CPU配置DMA引擎:驱动软件在内存中构建DMA描述符(Descriptor),包含源地址、目的地址、传输长度、控制标志等,并通知DMA引擎起始描述符地址。
  2. DMA引擎自主搬运:DMA控制器从总线(如PCIe总线)上获取总线控制权,按照描述符内容在源端与目的端之间传输数据,无需CPU干预。
  3. 传输完成通知:当DMA传输完成或遇到错误时,DMA控制器通过中断或硬件标志位通知CPU,驱动在中断处理函数中回收描述符并处理数据。
关键指标:DMA传输的延迟通常由“描述符提交延迟 + 总线传输延迟 + 完成通知延迟”三部分组成。在PCIe Gen4 x16链路上,单次DMA传输的硬件延迟可低至微秒级,远低于CPU逐字节搬运的毫秒级延迟。
开发提示:在编写DMA驱动时,务必保证描述符在物理内存中的连续性(或使用IOMMU/SMMU映射),否则DMA引擎可能因地址不连续而触发总线错误。
DMA工作原理流程图 DMA工作原理流程图 CPU配置DMA描述符 源/目的地址 + 长度 DMA引擎获取总线控制权 PCIe总线仲裁 数据搬运 源 → 目的(硬件级) 传输完成中断 通知CPU回收描述符 CPU处理数据 CPU仅在配置和完成阶段参与 数据搬运阶段CPU可执行其他任务

4.2 总线主控DMA与从属DMA

根据DMA控制器在总线上的角色,DMA分为总线主控DMA(Bus-Master DMA)和从属DMA(Slave DMA)两种模式。在PCIe设备驱动开发中,绝大多数高性能设备都采用总线主控DMA。

特性 总线主控DMA(Bus-Master) 从属DMA(Slave DMA)
总线控制权 设备自身作为总线主控,主动发起读写事务 由系统DMA控制器(如ISA DMA控制器)代理
性能 高,可直接利用PCIe高带宽,支持多描述符环 低,受限于系统DMA控制器的通道数量和速度
地址空间 支持64位地址,可访问整个系统物理内存 通常限制在24位或32位地址(低4GB)
典型应用 NVMe SSD、GPU、万兆网卡、FPGA加速卡 老式ISA设备、低速UART/SPI控制器
驱动复杂度 较高,需管理描述符环、DMA映射、一致性 较低,由系统DMA驱动统一管理
避坑指南:在PCIe设备驱动中,如果设备声称支持总线主控DMA,但驱动未正确设置PCI_COMMAND_MASTER位(通过pci_set_master()),设备将无法发起Memory Write/Read事务,导致DMA传输失败。这是新手最容易忽略的问题之一。
总线主控DMA与从属DMA架构对比 总线主控DMA vs 从属DMA 架构对比 总线主控DMA CPU 系统内存 PCIe设备(主控) 主动发起DMA读写 DMA写(数据) DMA读(数据) 从属DMA CPU 系统DMA控制器 系统内存 PCIe设备(从属) 请求DMA服务 代理传输 从属DMA需经过系统DMA控制器中转 延迟高、带宽受限,不适用于高性能场景

4.3 DMA传输方向:设备视角与总线事务

从PCIe设备的角度,DMA传输方向分为设备到内存(Device-to-Memory, D2M)和内存到设备(Memory-to-Device, M2D)。在PCIe总线事务层面,这两种方向分别对应Memory Write(MWr)和Memory Read(MRd)事务。

4.3.1 设备到内存(D2M / DMA写)

  • 方向:PCIe设备 → 系统内存
  • PCIe事务类型:Memory Write (MWr)
  • 典型场景:网卡接收数据包写入内存、NVMe SSD读取数据写入内存、ADC采样数据上传
  • 性能特点:设备主动发起写事务,无需等待CPU读取;写事务通常不需要完成(Completion),延迟较低

4.3.2 内存到设备(M2D / DMA读)

  • 方向:系统内存 → PCIe设备
  • PCIe事务类型:Memory Read (MRd) + Completion (Cpl)
  • 典型场景:GPU从内存读取纹理数据、网卡发送数据包、FPGA加载配置数据
  • 性能特点:设备先发起读请求,内存控制器返回数据(Completion);读事务需要等待完成,延迟相对较高
优化要点:在PCIe Gen4/Gen5设计中,DMA写(MWr)的吞吐量通常高于DMA读(MRd),因为写事务不需要等待Completion。对于读密集型应用(如GPU从内存读取数据),建议使用读合并(Read Combining)或预取(Prefetch)技术来隐藏读延迟。
DMA传输方向与PCIe事务对应关系 DMA传输方向与PCIe事务对应关系 PCIe设备 (主控/从属) 系统内存 (DMA缓冲区) DMA写 (MWr) 设备→内存,无Completion DMA读 (MRd + Cpl) 设备→内存请求,内存返回数据 DMA写延迟通常低于DMA读,因为写事务不需要等待Completion 高吞吐场景优先使用DMA写方向(设备→内存)

4.4 DMA地址映射与一致性

在PCIe设备发起DMA传输前,驱动必须将内核虚拟地址(或用户空间地址)转换为设备可见的总线地址(Bus Address)。这一过程称为DMA映射(DMA Mapping)。根据缓存一致性策略,DMA映射分为两种类型:

映射类型 API函数(Linux) 缓存一致性 适用场景
一致性DMA映射 dma_alloc_coherent() 硬件保证CPU与设备看到的数据一致(无需手动刷新缓存) 描述符环、控制寄存器、小数据量控制结构
流式DMA映射 dma_map_single() / dma_map_sg() 需要驱动在适当位置调用dma_sync_single_for_cpu() / dma_sync_single_for_device() 大数据块传输(网络数据包、存储数据)
性能建议:对于高吞吐DMA场景(如10万IOPS以上的NVMe驱动),优先使用流式DMA映射。一致性DMA映射虽然使用简单,但内部可能使用非缓存(Non-cacheable)或写合并(Write-Combine)内存,导致CPU访问延迟较高。流式映射允许CPU使用缓存访问数据,仅在DMA传输前后同步缓存,整体吞吐量更高。

4.5 本章小结

  • DMA的核心价值在于解放CPU,让硬件自主完成数据搬运,CPU仅在配置和完成阶段介入。
  • PCIe高性能设备必须使用总线主控DMA模式,通过pci_set_master()启用主控能力。
  • DMA写(设备→内存)在PCIe总线上延迟更低,高吞吐场景应优先设计为设备主动写入内存。
  • DMA映射选择直接影响性能:描述符等控制结构用一致性映射,大数据块用流式映射。