1. DMA一致性内存分配概述

1.1 dma_alloc_coherent 的核心作用

dma_alloc_coherent 是 Linux 内核中用于分配 DMA 一致性内存(Coherent DMA Memory)的核心 API。它保证分配的内存区域在 CPU 和 DMA 控制器(如网卡、存储控制器、USB 主机控制器)之间保持 缓存一致性,无需软件手动刷新缓存或执行内存屏障。

关键特性: 调用 dma_alloc_coherent 返回的虚拟地址和物理地址,CPU 与 DMA 设备对该内存的访问在任何时刻都是一致的,不会出现脏缓存行导致的数据不一致问题。

函数原型如下:

void *dma_alloc_coherent(struct device *dev, size_t size,
                         dma_addr_t *dma_handle, gfp_t flag);
参数 说明
dev 请求 DMA 的设备指针,用于关联设备与内存区域
size 分配的内存大小(字节),必须为页对齐的倍数
dma_handle 输出参数,返回给 DMA 控制器使用的总线地址
flag 内存分配标志,如 GFP_KERNEL、GFP_ATOMIC
返回值 CPU 可访问的虚拟地址;失败返回 NULL

1.2 与流式 DMA 映射的区别

流式 DMA 映射(Streaming DMA Mapping)通过 dma_map_single / dma_unmap_single 管理,两者在缓存处理策略上存在本质差异:

对比维度 一致性 DMA(dma_alloc_coherent) 流式 DMA(dma_map_single)
缓存策略 禁用 CPU 缓存或使用非缓存映射 使用缓存,通过 flush/invalidate 维护一致性
分配时机 驱动初始化时一次性分配 每次 DMA 传输前映射,传输后解映射
生命周期 长期持有,直到驱动卸载 短时持有,传输完成后立即释放
API 开销 分配/释放开销大,但使用中无额外开销 每次传输需 map/unmap,开销较小
适用场景 控制结构、描述符环、状态寄存器 大数据块传输(网络包、文件数据)
错误恢复 分配失败需回滚整个设备初始化 映射失败可重试或使用备用缓冲区
经验法则: 对于频繁访问且数据量小的控制结构(如描述符环、DMA 命令队列),优先使用一致性 DMA;对于大块数据流传输,使用流式 DMA 可获得更好的缓存利用率和性能。

1.3 一致性内存的特性

一致性内存具备以下关键特性,这些特性直接决定了其分配失败时的调试方向:

  • 物理连续性: 分配的内存区域在物理地址空间上是连续的,这是 DMA 控制器寻址的基本要求。
  • 非缓存映射: CPU 页表将该区域标记为不可缓存(或使用特殊缓存策略),避免 CPU 缓存与 DMA 写入之间的数据不一致。
  • 对齐约束: 分配的内存通常以页(4KB)为边界对齐,部分硬件要求更大的对齐(如 64KB、1MB)。
  • 低地址限制: 某些 32 位 DMA 设备只能访问 32 位地址空间,需要从 ZONE_DMA 区域分配。
  • 不可迁移: 一致性内存一旦分配,物理页框被锁定,不能进行内存规整或迁移。
一致性 DMA 内存架构图 CPU 核心 L1/L2 Cache 一致性 DMA 内存 非缓存映射 / 物理连续 DMA 设备 系统总线(AMBA / PCIe) 说明:CPU 通过非缓存映射访问一致性内存,DMA 设备通过总线地址直接读写,两者无需软件干预即可保证数据一致。

1.4 分配失败的影响范围

当 dma_alloc_coherent 返回 NULL 时,整个设备驱动初始化流程通常无法继续。典型的影响包括:

  • 设备无法启动: 无法分配描述符环或命令队列,设备无法接收任何 I/O 请求。
  • 系统可用性下降: 对于存储控制器或网络接口,分配失败可能导致文件系统挂载失败或网络服务不可用。
  • 内存碎片化加剧: 长时间运行的系统中,物理内存碎片化是分配失败的主要原因之一。
  • 调试困难: 失败原因可能是内存不足、区域限制、对齐约束或 IOMMU 配置错误,需要系统化排查。
注意: 在嵌入式系统中,ZONE_DMA 区域通常只有 16MB 或 64MB,频繁分配大块一致性内存极易耗尽该区域。分配前应评估设备对地址范围的限制,必要时使用 dma_set_mask 放宽地址约束。