DMA性能分析 Linux驱动高吞吐传输调试与优化 - 第4节

4.1 DMA 通道的申请与释放:dma_request_channel / dma_release_channel

在 Linux DMA 引擎框架中,DMA 通道(channel)是执行数据传输的核心抽象。驱动必须首先向 DMA 引擎申请一个可用通道,才能发起传输。本节详细解析通道的申请与释放 API,以及背后的资源管理机制。

4.1.1 dma_request_channel:通道申请

dma_request_channel 用于从指定的 DMA 控制器(DMA controller)中获取一个满足特定能力的通道。其原型如下:

struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
                                     dma_filter_fn filter_fn,
                                     void *filter_param);
参数 类型 说明
mask dma_cap_mask_t 位掩码,指定所需 DMA 传输能力(如 DMA_SLAVE、DMA_CYCLIC)
filter_fn dma_filter_fn 回调函数,用于进一步筛选通道(可 NULL)
filter_param void * 传递给 filter_fn 的私有参数

典型调用流程:

  1. 使用 dma_cap_mask_t 声明并初始化能力掩码。
  2. 调用 dma_cap_set 设置所需能力位。
  3. 调用 dma_request_channel 获取通道。
  4. 检查返回值是否为 NULL(申请失败)。
关键点: filter_fn 通常用于匹配特定 DMA 控制器或通道编号。例如,在嵌入式平台中,通过 filter_fn 绑定到指定的硬件 DMA 请求线(DMA request line)。

4.1.2 dma_release_channel:通道释放

当驱动不再使用 DMA 通道时,必须调用 dma_release_channel 将其归还给系统,以便其他驱动复用。

void dma_release_channel(struct dma_chan *chan);

使用注意事项:

  • 必须在所有未完成的 DMA 传输完成或终止后调用。
  • 释放后,通道描述符指针变为悬空,不可再使用。
  • 通常在驱动的 remove 回调或错误处理路径中调用。
常见错误: 忘记释放通道会导致 DMA 引擎资源泄漏,长时间运行后系统可能无法分配新通道。务必在驱动卸载路径中配对调用。

4.1.3 通道生命周期 SVG 图

下图展示了从申请到释放的完整通道生命周期,以及各阶段的关键操作:

DMA 通道生命周期 dma_request_channel 通道就绪 传输操作 传输完成/终止 dma_release_channel 通道空闲 注意:释放前必须确保所有传输已完成或已终止,否则可能导致 DMA 控制器状态异常

4.2 一致性 DMA 内存分配:dma_alloc_coherent

在 DMA 传输中,CPU 和设备需要共享内存缓冲区。dma_alloc_coherent 分配一段一致性(coherent)内存,保证 CPU 和设备看到的缓存一致性,无需手动维护缓存同步。

4.2.1 函数原型与参数

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 标志(如 GFP_KERNEL、GFP_ATOMIC)

返回值: 内核虚拟地址(CPU 访问),失败返回 NULL。

4.2.2 使用场景与限制

  • 适用场景: 控制结构、描述符环、小数据块传输(如 USB 控制传输)。
  • 性能特点: 分配时已保证一致性,读写无额外开销,但分配/释放成本较高。
  • 大小限制: 通常用于分配较小缓冲区(几 KB 到几 MB),大块分配可能失败。
最佳实践: 对于高频小数据交换(如 DMA 描述符表),优先使用一致性分配。对于大数据流(如视频帧),考虑流式映射(streaming DMA mapping)以获得更好性能。

4.2.3 释放函数:dma_free_coherent

void dma_free_coherent(struct device *dev, size_t size,
                       void *cpu_addr, dma_addr_t dma_handle);

必须使用与分配时完全相同的 size、cpu_addr 和 dma_handle 进行释放。

4.2.4 一致性内存分配流程 SVG 图

dma_alloc_coherent 分配流程 驱动调用 dma_alloc_coherent 分配物理连续内存 建立页表映射 返回 CPU 虚拟地址 输出 DMA 总线地址 缓存一致性保证 一致性内存:CPU 和 DMA 设备看到的同一物理内存,无需手动刷新缓存

4.3 DMA 内存池:dma_pool

当驱动需要频繁分配和释放大量固定大小的一致性 DMA 缓冲区时,直接使用 dma_alloc_coherent 会产生较高的开销。dma_pool 提供了一种预分配 + 复用的机制,显著提升小对象分配效率。

4.3.1 核心 API

函数 功能
dma_pool_create 创建 DMA 内存池
dma_pool_alloc 从池中分配一个对象
dma_pool_free 释放对象回池
dma_pool_destroy 销毁整个内存池

4.3.2 dma_pool_create 参数详解

struct dma_pool *dma_pool_create(const char *name,
                                 struct device *dev,
                                 size_t size,
                                 size_t align,
                                 size_t boundary);
参数 说明
name 池名称,用于调试和统计
dev DMA 设备
size 每个对象的大小(字节)
align 对齐要求(字节),必须是 2 的幂
boundary 边界跨越限制(0 表示无限制),避免跨边界导致硬件问题

4.3.3 使用示例

struct dma_pool *pool;
void *cpu_addr;
dma_addr_t dma_addr;

// 创建对象大小为 64 字节、16 字节对齐、无边界限制的池
pool = dma_pool_create("my_dma_pool", dev, 64, 16, 0);
if (!pool) {
    dev_err(dev, "Failed to create DMA pool\n");
    return -ENOMEM;
}

// 从池中分配一个对象
cpu_addr = dma_pool_alloc(pool, GFP_KERNEL, &dma_addr);
if (!cpu_addr) {
    dev_err(dev, "Failed to allocate from DMA pool\n");
    dma_pool_destroy(pool);
    return -ENOMEM;
}

// ... 使用缓冲区 ...

// 释放对象回池
dma_pool_free(pool, cpu_addr, dma_addr);

// 销毁池
dma_pool_destroy(pool);

4.3.4 性能对比:dma_alloc_coherent vs dma_pool

特性 dma_alloc_coherent dma_pool
分配粒度 页对齐,最小 4KB 任意大小(如 64 字节)
分配开销 高(页分配 + 映射) 低(预分配,快速复用)
适用场景 大块、低频分配 小块、高频分配
内存碎片 可能产生内部碎片 固定大小,无碎片
对齐控制 有限(页对齐) 灵活(任意 2 的幂)
典型应用: 网络驱动的 TX/RX 描述符环、USB 的 URB 缓冲区、音频驱动的周期缓冲区。这些场景中,对象大小固定且分配频繁,使用 dma_pool 可降低 50% 以上的分配延迟。

4.3.5 DMA 内存池架构 SVG 图

DMA 内存池架构 dma_pool 管理结构 预分配页 1 预分配页 2 预分配页 N obj obj obj obj obj obj 空闲对象链表 已分配对象链表 dma_pool 预分配物理连续页,将页内划分为固定大小对象,通过空闲链表管理,分配/释放仅需 O(1) 操作 适用于描述符环、控制块等高频小对象场景,显著降低 DMA 内存分配延迟

4.4 本章小结

  • dma_request_channel / dma_release_channel:管理 DMA 通道的生命周期,是驱动与 DMA 引擎交互的入口。
  • dma_alloc_coherent / dma_free_coherent:分配一致性 DMA 内存,保证 CPU 和设备缓存一致性,适用于控制结构和小数据块。
  • dma_pool:通过预分配和对象复用机制,大幅提升固定大小 DMA 缓冲区的分配效率,是高吞吐驱动中的关键优化手段。
下节预告: 第 5 章将深入讲解流式 DMA 映射 API(dma_map_single / dma_map_sg),以及如何在驱动中正确使用这些接口实现高性能数据传输。