文章目录
- [嵌入式Linux DMA深度解析:原理、应用与性能优化实践](#嵌入式Linux DMA深度解析:原理、应用与性能优化实践)
-
- 摘要
- 第一章:DMA技术概述与基础原理
-
- [1.1 DMA的基本概念](#1.1 DMA的基本概念)
- [1.2 DMA传输的基本流程](#1.2 DMA传输的基本流程)
- [1.3 DMA控制器架构](#1.3 DMA控制器架构)
-
- [1.3.1 集中式DMA控制器](#1.3.1 集中式DMA控制器)
- [1.3.2 集成式DMA控制器](#1.3.2 集成式DMA控制器)
- [1.3.3 分散-聚集DMA](#1.3.3 分散-聚集DMA)
- [1.4 DMA传输模式](#1.4 DMA传输模式)
-
- [1.4.1 单次传输模式(Single Transfer Mode)](#1.4.1 单次传输模式(Single Transfer Mode))
- [1.4.2 块传输模式(Block Transfer Mode)](#1.4.2 块传输模式(Block Transfer Mode))
- [1.4.3 需求传输模式(Demand Transfer Mode)](#1.4.3 需求传输模式(Demand Transfer Mode))
- [1.4.4 循环传输模式(Circular Mode)](#1.4.4 循环传输模式(Circular Mode))
- [1.5 DMA地址空间](#1.5 DMA地址空间)
-
- [1.5.1 物理地址与总线地址](#1.5.1 物理地址与总线地址)
- [1.5.2 IOMMU(输入输出内存管理单元)](#1.5.2 IOMMU(输入输出内存管理单元))
- 第二章:Linux内核DMA子系统架构
-
- [2.1 DMA子系统概述](#2.1 DMA子系统概述)
- [2.2 DMA引擎框架](#2.2 DMA引擎框架)
- [2.3 DMA通道管理](#2.3 DMA通道管理)
- [2.4 DMA映射API](#2.4 DMA映射API)
-
- [2.4.1 一致性DMA映射](#2.4.1 一致性DMA映射)
- [2.4.2 流式DMA映射](#2.4.2 流式DMA映射)
- [2.5 DMA同步机制](#2.5 DMA同步机制)
-
- [2.5.1 缓存一致性](#2.5.1 缓存一致性)
- [2.5.2 同步使用模式](#2.5.2 同步使用模式)
- [2.6 DMA池(DMA Pool)](#2.6 DMA池(DMA Pool))
- 第三章:DMA在嵌入式外设中的应用
-
- [3.1 以太网控制器DMA](#3.1 以太网控制器DMA)
-
- [3.1.1 以太网DMA描述符](#3.1.1 以太网DMA描述符)
- [3.1.2 以太网数据发送流程](#3.1.2 以太网数据发送流程)
- [3.2 USB控制器DMA](#3.2 USB控制器DMA)
-
- [3.2.1 USB主机控制器DMA](#3.2.1 USB主机控制器DMA)
- [3.3 音频编解码器DMA](#3.3 音频编解码器DMA)
-
- [3.3.1 ALSA DMA缓冲区管理](#3.3.1 ALSA DMA缓冲区管理)
- [3.4 摄像头接口DMA](#3.4 摄像头接口DMA)
-
- [3.4.1 图像采集DMA](#3.4.1 图像采集DMA)
- [3.5 SPI控制器DMA](#3.5 SPI控制器DMA)
-
- [3.5.1 SPI DMA传输](#3.5.1 SPI DMA传输)
- 第四章:DMA性能优化策略
-
- [4.1 缓冲区对齐优化](#4.1 缓冲区对齐优化)
- [4.2 缓存优化策略](#4.2 缓存优化策略)
-
- [4.2.1 缓存预取](#4.2.1 缓存预取)
- [4.2.2 缓存一致性优化](#4.2.2 缓存一致性优化)
- [4.3 分散-聚集优化](#4.3 分散-聚集优化)
-
- [4.3.1 智能SG列表合并](#4.3.1 智能SG列表合并)
- [4.4 DMA描述符优化](#4.4 DMA描述符优化)
-
- [4.4.1 描述符缓存](#4.4.1 描述符缓存)
- [4.4.2 批处理描述符更新](#4.4.2 批处理描述符更新)
- [4.5 中断优化策略](#4.5 中断优化策略)
-
- [4.5.1 中断合并](#4.5.1 中断合并)
- [4.5.2 MSI-X中断优化](#4.5.2 MSI-X中断优化)
- [4.6 内存访问模式优化](#4.6 内存访问模式优化)
-
- [4.6.1 非时序内存访问](#4.6.1 非时序内存访问)
- [4.6.2 预取优化](#4.6.2 预取优化)
- [4.7 电源管理优化](#4.7 电源管理优化)
-
- [4.7.1 动态频率调整](#4.7.1 动态频率调整)
- 第五章:DMA调试与故障排除
-
- [5.1 DMA调试工具](#5.1 DMA调试工具)
-
- [5.1.1 内核调试支持](#5.1.1 内核调试支持)
- [5.1.2 性能分析工具](#5.1.2 性能分析工具)
- [5.2 常见DMA问题诊断](#5.2 常见DMA问题诊断)
-
- [5.2.1 DMA传输失败诊断](#5.2.1 DMA传输失败诊断)
- [5.2.2 内存一致性问题的调试](#5.2.2 内存一致性问题的调试)
- [5.3 DMA泄漏检测](#5.3 DMA泄漏检测)
-
- [5.3.1 资源泄漏检测](#5.3.1 资源泄漏检测)
- [5.3.2 自动化测试框架](#5.3.2 自动化测试框架)
- [5.4 实时调试技术](#5.4 实时调试技术)
-
- [5.4.1 跟踪点(Tracepoints)](#5.4.1 跟踪点(Tracepoints))
- [5.4.2 动态调试](#5.4.2 动态调试)
- 第六章:高级DMA技术与未来趋势
-
- [6.1 IOMMU与SMMU技术](#6.1 IOMMU与SMMU技术)
-
- [6.1.1 IOMMU高级特性](#6.1.1 IOMMU高级特性)
- [6.1.2 IOMMU性能优化](#6.1.2 IOMMU性能优化)
- [6.2 异构计算中的DMA](#6.2 异构计算中的DMA)
-
- [6.2.1 GPU DMA](#6.2.1 GPU DMA)
- [6.2.2 AI加速器DMA](#6.2.2 AI加速器DMA)
- [6.3 未来趋势与技术展望](#6.3 未来趋势与技术展望)
-
- [6.3.1 CXL(Compute Express Link)与DMA](#6.3.1 CXL(Compute Express Link)与DMA)
- [6.3.2 智能网卡与DPU的DMA](#6.3.2 智能网卡与DPU的DMA)
- [6.3.3 量子计算与DMA](#6.3.3 量子计算与DMA)
- 总结与展望
嵌入式Linux DMA深度解析:原理、应用与性能优化实践
摘要
本文全面深入地探讨嵌入式Linux系统中直接内存访问(DMA)技术的原理与应用。文章从硬件基础出发,详细分析DMA控制器架构,深入解析Linux内核DMA子系统,涵盖DMA映射、缓冲区管理、同步机制等核心概念。通过实际案例展示DMA在各类外设驱动中的应用,并提供完整的性能优化策略和调试方法。文章旨在为嵌入式开发人员提供一套完整的DMA理论和实践指南,帮助读者深入理解并高效利用这一关键技术。
第一章:DMA技术概述与基础原理
1.1 DMA的基本概念
直接内存访问(Direct Memory Access,DMA)是一种允许某些硬件子系统独立于中央处理器(CPU)直接访问系统内存的技术。在没有DMA的情况下,当CPU需要执行I/O操作时,它必须暂停当前任务,执行数据传输,然后恢复之前的任务。这种处理方式被称为程序控制I/O或轮询模式I/O。
DMA的核心优势:
- 降低CPU占用率:CPU只需初始化DMA传输,然后在传输完成时处理中断,期间可以执行其他任务
- 提高数据传输速率:DMA控制器通常针对大数据块传输进行优化
- 减少延迟:避免了CPU频繁的中断处理开销
- 支持并发操作:CPU和外设可以同时工作
1.2 DMA传输的基本流程
典型的DMA传输包含以下步骤:
1. 初始化阶段
CPU配置DMA控制器:
- 设置源地址(外设或内存)
- 设置目的地址(内存或外设)
- 设置传输字节数
- 设置传输模式(单次、循环等)
2. 传输阶段
DMA控制器接管总线控制权:
- 从源地址读取数据
- 向目的地址写入数据
- 更新地址指针和计数器
3. 完成阶段
DMA控制器:
- 释放总线控制权
- 产生中断通知CPU
CPU:
- 处理中断
- 验证传输结果
- 准备下一次传输
1.3 DMA控制器架构
现代嵌入式系统中的DMA控制器通常采用以下架构:
1.3.1 集中式DMA控制器
集中式DMA控制器是独立的硬件模块,为多个外设提供DMA服务:
c
// 典型的集中式DMA控制器寄存器结构
struct dma_controller_regs {
// 控制寄存器
volatile uint32_t config; // 配置寄存器
volatile uint32_t status; // 状态寄存器
volatile uint32_t int_enable; // 中断使能寄存器
// 通道相关寄存器(假设8个通道)
struct {
volatile uint32_t src_addr; // 源地址
volatile uint32_t dst_addr; // 目的地址
volatile uint32_t length; // 传输长度
volatile uint32_t control; // 通道控制
} channel[8];
// 优先级和仲裁寄存器
volatile uint32_t priority;
volatile uint32_t arbiter;
};
1.3.2 集成式DMA控制器
集成式DMA控制器内嵌在特定的外设中(如USB、以太网控制器):
c
// 以太网MAC中的集成DMA控制器
struct eth_dma_regs {
// 发送描述符列表地址
volatile uint32_t tx_desc_list_addr;
// 接收描述符列表地址
volatile uint32_t rx_desc_list_addr;
// 操作模式
volatile uint32_t operation_mode;
// 中断状态和控制
volatile uint32_t interrupt_status;
volatile uint32_t interrupt_enable;
// 发送控制
volatile uint32_t tx_control;
// 接收控制
volatile uint32_t rx_control;
};
1.3.3 分散-聚集DMA
现代DMA控制器支持分散-聚集(Scatter-Gather)功能,允许非连续内存块的单次传输:
c
// 分散-聚集描述符结构
struct scatterlist {
unsigned long page_link; // 指向下一个描述符或页信息
unsigned int offset; // 缓冲区在页面内的偏移
unsigned int length; // 缓冲区长度
dma_addr_t dma_address; // DMA地址
};
// 分散-聚集操作
struct dma_async_tx_descriptor {
enum dma_transaction_type type; // 传输类型
dma_cookie_t cookie; // 传输标识
dma_async_tx_callback callback; // 完成回调
void *callback_param; // 回调参数
struct list_head tx_list; // 链表连接
};
1.4 DMA传输模式
1.4.1 单次传输模式(Single Transfer Mode)
每次传输需要CPU重新配置DMA控制器。适用于不频繁的少量数据传输。
1.4.2 块传输模式(Block Transfer Mode)
DMA控制器连续传输整个数据块,完成后产生中断。适用于大块数据传输。
1.4.3 需求传输模式(Demand Transfer Mode)
由外设的DREQ(DMA请求)信号控制传输节奏,外设准备好数据时请求传输。
1.4.4 循环传输模式(Circular Mode)
DMA控制器在缓冲区边界自动循环,实现连续的数据流传输,常用于音频、视频应用。
1.5 DMA地址空间
理解DMA地址空间是掌握DMA编程的关键:
1.5.1 物理地址与总线地址
c
// 物理地址与总线地址的关系
// CPU视角的物理地址空间
// │ 0x00000000 ──────────── 0xFFFFFFFF │
// │ 物理内存空间 │
//
// DMA控制器视角的总线地址空间
// │ 0x00000000 ──── 0xFFFFFFFF │
// │ 总线地址空间(可能映射) │
//
// 实际映射关系取决于系统架构:
// 1. 在简单系统中,物理地址 = 总线地址
// 2. 在有IOMMU的系统中,存在复杂的映射关系
1.5.2 IOMMU(输入输出内存管理单元)
IOMMU为DMA提供了地址转换和内存保护:
c
// IOMMU的基本工作原理
// CPU虚拟地址 ──映射──> CPU物理地址
// DMA总线地址 ──IOMMU映射──> 实际物理地址
// Linux中的IOMMU支持
struct iommu_domain {
struct iommu_ops *ops; // IOMMU操作函数
void *priv; // 私有数据
unsigned long pgsize_bitmap; // 支持的页面大小
};
// IOMMU映射函数
int iommu_map(struct iommu_domain *domain,
unsigned long iova, // I/O虚拟地址
phys_addr_t paddr, // 物理地址
size_t size, // 映射大小
int prot); // 保护标志
第二章:Linux内核DMA子系统架构
2.1 DMA子系统概述
Linux内核的DMA子系统提供了统一的接口,抽象了不同硬件平台的DMA控制器差异。主要组件包括:
- DMA引擎框架:通用的DMA控制器抽象
- DMA映射API:物理地址与总线地址的转换
- 分散-聚集支持:非连续缓冲区的DMA传输
- 一致性DMA缓冲区:CPU和DMA都可缓存的内存区域
- 流式DMA缓冲区:适用于单次传输的缓冲区
2.2 DMA引擎框架
DMA引擎框架通过dmaengine子系统提供统一的DMA控制器接口:
c
// DMA设备结构
struct dma_device {
struct device *dev; // 关联的设备
struct list_head channels; // DMA通道列表
struct list_head global_node; // 全局链表节点
dma_cap_mask_t caps; // 能力位图
// 操作函数集
int (*device_alloc_chan_resources)(struct dma_chan *chan);
void (*device_free_chan_resources)(struct dma_chan *chan);
struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)(
struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
size_t len, unsigned long flags);
// 更多操作函数...
int (*device_config)(struct dma_chan *chan,
struct dma_slave_config *config);
int (*device_pause)(struct dma_chan *chan);
int (*device_resume)(struct dma_chan *chan);
int (*device_terminate_all)(struct dma_chan *chan);
// 控制函数
void (*device_issue_pending)(struct dma_chan *chan);
enum dma_status (*device_tx_status)(struct dma_chan *chan,
dma_cookie_t cookie,
struct dma_tx_state *txstate);
};
2.3 DMA通道管理
DMA通道是DMA传输的基本执行单元:
c
// DMA通道结构
struct dma_chan {
struct dma_device *device; // 所属DMA设备
dma_cookie_t cookie; // 当前传输标识
struct list_head chan_list; // 设备通道链表
// 客户端使用相关
void *private; // 客户端私有数据
struct dma_slave_config config; // 从设备配置
// 同步原语
spinlock_t lock; // 通道锁
wait_queue_head_t wait; // 等待队列
// 统计信息
int client_count; // 客户端引用计数
int table_count; // 描述符表计数
};
2.4 DMA映射API
Linux提供了完整的DMA映射API,处理物理地址和总线地址的转换:
2.4.1 一致性DMA映射
一致性映射(Coherent DMA Mapping)创建CPU和DMA都可缓存的内存区域:
c
// 一致性DMA分配函数
void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag);
// 一致性DMA释放函数
void dma_free_coherent(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t dma_handle);
// 使用示例
static int my_driver_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
dma_addr_t dma_handle;
void *cpu_addr;
size_t size = PAGE_SIZE * 16; // 64KB
// 分配一致性DMA缓冲区
cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
if (!cpu_addr) {
dev_err(dev, "Failed to allocate coherent DMA buffer\n");
return -ENOMEM;
}
// 配置DMA控制器使用dma_handle作为地址
configure_dma_controller(dma_handle, size);
// CPU可以直接访问cpu_addr
memset(cpu_addr, 0, size);
return 0;
}
2.4.2 流式DMA映射
流式映射(Streaming DMA Mapping)适用于单次传输,需要显式的同步操作:
c
// 流式DMA映射函数
dma_addr_t dma_map_single(struct device *dev, void *ptr,
size_t size, enum dma_data_direction dir);
// 流式DMA解除映射
void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction dir);
// 分散-聚集映射
int dma_map_sg(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir);
// 使用示例
static int transfer_data(struct device *dev, void *data, size_t size)
{
dma_addr_t dma_handle;
enum dma_data_direction dir = DMA_TO_DEVICE;
// 映射缓冲区用于DMA传输
dma_handle = dma_map_single(dev, data, size, dir);
if (dma_mapping_error(dev, dma_handle)) {
dev_err(dev, "Failed to map DMA buffer\n");
return -EFAULT;
}
// 执行DMA传输
start_dma_transfer(dev, dma_handle, size);
// 等待传输完成
wait_for_dma_completion();
// 解除映射
dma_unmap_single(dev, dma_handle, size, dir);
return 0;
}
2.5 DMA同步机制
正确的同步是确保DMA传输可靠性的关键:
2.5.1 缓存一致性
c
// DMA同步函数
void dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr,
size_t size, enum dma_data_direction dir);
void dma_sync_single_for_device(struct device *dev, dma_addr_t addr,
size_t size, enum dma_data_direction dir);
// 分散-聚集同步
void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir);
void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir);
2.5.2 同步使用模式
c
// DMA传输的典型同步流程
static int dma_transfer_with_sync(struct device *dev,
void *buffer, size_t size)
{
dma_addr_t dma_addr;
// 1. 准备数据(CPU操作)
prepare_data(buffer, size);
// 2. 映射缓冲区(CPU -> Device方向)
dma_addr = dma_map_single(dev, buffer, size, DMA_TO_DEVICE);
// 3. 确保设备能看到最新的数据
dma_sync_single_for_device(dev, dma_addr, size, DMA_TO_DEVICE);
// 4. 启动DMA传输
start_dma_transfer(dma_addr, size);
// 5. 等待传输完成
wait_for_completion();
// 6. 对于Device -> CPU的传输,需要同步缓存
if (direction == DMA_FROM_DEVICE) {
dma_sync_single_for_cpu(dev, dma_addr, size, DMA_FROM_DEVICE);
}
// 7. 解除映射
dma_unmap_single(dev, dma_addr, size, direction);
return 0;
}
2.6 DMA池(DMA Pool)
对于小块的DMA内存分配,可以使用DMA池来提高效率:
c
// 创建DMA池
struct dma_pool *dma_pool_create(const char *name, struct device *dev,
size_t size, size_t align, size_t boundary);
// 从DMA池分配内存
void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
dma_addr_t *handle);
// 释放DMA池内存
void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr);
// 销毁DMA池
void dma_pool_destroy(struct dma_pool *pool);
// 使用示例
struct my_device {
struct dma_pool *small_buffer_pool;
struct device *dev;
};
static int init_dma_pools(struct my_device *mydev)
{
// 创建用于小型DMA缓冲区的池
mydev->small_buffer_pool = dma_pool_create("mydev_small",
mydev->dev,
256, // 块大小
32, // 对齐要求
0); // 无边界限制
if (!mydev->small_buffer_pool)
return -ENOMEM;
return 0;
}
static int alloc_small_dma_buffer(struct my_device *mydev)
{
dma_addr_t dma_handle;
void *cpu_addr;
// 从池中分配小缓冲区
cpu_addr = dma_pool_alloc(mydev->small_buffer_pool,
GFP_KERNEL,
&dma_handle);
if (!cpu_addr)
return -ENOMEM;
// 使用缓冲区...
return 0;
}
第三章:DMA在嵌入式外设中的应用
3.1 以太网控制器DMA
以太网控制器是DMA应用的典型场景,通常使用环形缓冲区描述符:
3.1.1 以太网DMA描述符
c
// 以太网DMA描述符结构
struct eth_dma_desc {
volatile uint32_t status; // 状态和控制字
uint32_t length; // 数据长度
dma_addr_t buf_addr; // 缓冲区DMA地址
struct eth_dma_desc *next; // 下一个描述符
};
// 以太网DMA环形缓冲区管理
struct eth_dma_ring {
struct eth_dma_desc *desc; // 描述符数组
dma_addr_t desc_dma; // 描述符的DMA地址
unsigned int size; // 环形缓冲区大小
unsigned int head; // 生产者索引
unsigned int tail; // 消费者索引
spinlock_t lock; // 环形缓冲区锁
};
// 以太网驱动DMA初始化
static int eth_dma_init(struct net_device *ndev)
{
struct eth_priv *priv = netdev_priv(ndev);
struct device *dev = &priv->pdev->dev;
int i;
// 分配发送描述符环形缓冲区
priv->tx_ring.size = TX_DESC_COUNT;
priv->tx_ring.desc = dma_alloc_coherent(dev,
sizeof(struct eth_dma_desc) * TX_DESC_COUNT,
&priv->tx_ring.desc_dma, GFP_KERNEL);
// 初始化发送描述符
for (i = 0; i < TX_DESC_COUNT; i++) {
priv->tx_ring.desc[i].status = DESC_OWNED_BY_DMA;
priv->tx_ring.desc[i].buf_addr = 0;
priv->tx_ring.desc[i].length = 0;
// 设置环形链表
priv->tx_ring.desc[i].next = &priv->tx_ring.desc[(i + 1) % TX_DESC_COUNT];
}
// 配置DMA控制器
writel(priv->tx_ring.desc_dma, priv->mmio + REG_TX_DESC_LIST);
writel(TX_DESC_COUNT, priv->mmio + REG_TX_DESC_COUNT);
// 分配接收描述符环形缓冲区(类似过程)
return 0;
}
3.1.2 以太网数据发送流程
c
// 以太网数据发送(DMA方式)
static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
struct eth_priv *priv = netdev_priv(ndev);
struct eth_dma_desc *desc;
dma_addr_t dma_addr;
unsigned int entry;
// 获取下一个可用的发送描述符
entry = priv->tx_ring.head;
desc = &priv->tx_ring.desc[entry];
// 映射SKB数据用于DMA传输
dma_addr = dma_map_single(&priv->pdev->dev, skb->data,
skb->len, DMA_TO_DEVICE);
if (dma_mapping_error(&priv->pdev->dev, dma_addr)) {
netdev_err(ndev, "Failed to map skb for DMA\n");
return NETDEV_TX_BUSY;
}
// 填充描述符
desc->buf_addr = dma_addr;
desc->length = skb->len;
// 设置状态:先设置其他字段,最后设置OWN位
wmb(); // 写内存屏障,确保数据先写入
desc->status = DESC_OWNED_BY_DMA | DESC_FIRST_SEG | DESC_LAST_SEG;
// 更新环形缓冲区指针
priv->tx_ring.head = (entry + 1) % TX_DESC_COUNT;
// 启动DMA传输
writel(1, priv->mmio + REG_TX_POLL_DEMAND);
// 保存SKB指针,用于传输完成后的清理
priv->tx_skb[entry] = skb;
// 检查是否环形缓冲区已满
if (eth_tx_ring_full(priv)) {
netif_stop_queue(ndev);
}
return NETDEV_TX_OK;
}
// 发送完成中断处理
static irqreturn_t eth_tx_complete(int irq, void *dev_id)
{
struct net_device *ndev = dev_id;
struct eth_priv *priv = netdev_priv(ndev);
unsigned int processed = 0;
// 遍历发送完成描述符
while (priv->tx_ring.tail != priv->tx_ring.head) {
struct eth_dma_desc *desc;
struct sk_buff *skb;
unsigned int entry;
entry = priv->tx_ring.tail;
desc = &priv->tx_ring.desc[entry];
// 检查描述符是否已由DMA控制器释放
if (desc->status & DESC_OWNED_BY_DMA) {
break; // DMA还在处理这个描述符
}
skb = priv->tx_skb[entry];
if (skb) {
// 解除DMA映射
dma_unmap_single(&priv->pdev->dev,
desc->buf_addr,
desc->length,
DMA_TO_DEVICE);
// 释放SKB
dev_kfree_skb_irq(skb);
priv->tx_skb[entry] = NULL;
}
// 清理描述符
desc->buf_addr = 0;
desc->length = 0;
// 更新环形缓冲区指针
priv->tx_ring.tail = (entry + 1) % TX_DESC_COUNT;
processed++;
}
// 如果之前停止了发送队列,现在可以重新启动了
if (netif_queue_stopped(ndev) && !eth_tx_ring_full(priv)) {
netif_wake_queue(ndev);
}
// 更新统计信息
ndev->stats.tx_packets += processed;
return IRQ_HANDLED;
}
3.2 USB控制器DMA
USB控制器使用DMA实现高速数据传输,通常使用队列传输描述符(qTD)和队列头(QH):
3.2.1 USB主机控制器DMA
c
// EHCI(USB 2.0)DMA数据结构
struct ehci_qtd {
// 第一个DMA缓冲区指针
uint32_t buffer[5]; // 缓冲区指针数组
uint32_t buffer_hi[5]; // 64位地址的高32位
// 下一qTD指针
uint32_t next_qtd;
uint32_t alt_next_qtd;
// 令牌字段
uint32_t token;
// 私有数据
void *priv;
} __attribute__((aligned(32))); // 32字节对齐要求
// 队列头结构
struct ehci_qh {
// 水平链接指针
uint32_t qh_link;
// 端点特性
uint32_t ep_char;
// 端点能力
uint32_t ep_cap;
// 当前qTD指针
uint32_t cur_qtd;
// qTD覆盖区域
union {
struct ehci_qtd qtd;
uint32_t qtd_reserved[4];
} overlay;
// 私有数据
void *priv;
} __attribute__((aligned(32)));
// USB DMA传输初始化
static int usb_dma_transfer(struct usb_hcd *hcd,
struct urb *urb,
struct ehci_qh *qh)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct ehci_qtd *qtd;
dma_addr_t qtd_dma;
unsigned int length;
void *buf;
// 为传输分配qTD
qtd = dma_pool_alloc(ehci->qtd_pool, GFP_ATOMIC, &qtd_dma);
if (!qtd)
return -ENOMEM;
memset(qtd, 0, sizeof(*qtd));
// 设置qTD链接
qtd->next_qtd = EHCI_LIST_END;
qtd->alt_next_qtd = EHCI_LIST_END;
// 处理URB数据
buf = urb->transfer_buffer;
length = urb->transfer_buffer_length;
// 映射URB缓冲区用于DMA
if (usb_urb_dir_in(urb)) {
// IN传输:设备到主机
qtd->token = EHCI_QTD_TOKEN_STATUS_ACTIVE |
(length << 16) |
(3 << 10); // 3个错误重试
} else {
// OUT传输:主机到设备
qtd->token = EHCI_QTD_TOKEN_STATUS_ACTIVE |
EHCI_QTD_TOKEN_PID_OUT |
(length << 16) |
(3 << 10);
// 对于OUT传输,需要先将数据复制到DMA缓冲区
if (buf) {
// 映射数据用于DMA传输
dma_addr_t dma_addr;
dma_addr = dma_map_single(&hcd->self.controller,
buf, length, DMA_TO_DEVICE);
// 设置缓冲区指针
ehci_qtd_fill(qtd, dma_addr, length);
}
}
// 将qTD链接到QH
ehci_qh_link_async(ehci, qh, qtd);
return 0;
}
3.3 音频编解码器DMA
音频子系统使用DMA实现低延迟的音频流传输,通常使用循环缓冲区:
3.3.1 ALSA DMA缓冲区管理
c
// ALSA DMA缓冲区管理结构
struct snd_pcm_substream {
struct snd_pcm *pcm;
struct snd_pcm_str *pstr;
// DMA相关
struct snd_dma_buffer dma_buffer;
size_t dma_max;
// 运行时信息
struct snd_pcm_runtime *runtime;
};
// ALSA DMA缓冲区分配
static int snd_pcm_alloc_dma_buffer(struct snd_pcm_substream *substream,
size_t size)
{
struct snd_dma_buffer *dmab = &substream->dma_buffer;
struct device *dev = substream->pcm->card->dev;
// 分配DMA缓冲区
dmab->dev.type = SNDRV_DMA_TYPE_DEV;
dmab->dev.dev = dev;
dmab->area = dma_alloc_coherent(dev, size,
&dmab->addr, GFP_KERNEL);
if (!dmab->area)
return -ENOMEM;
dmab->bytes = size;
dmab->private_data = NULL;
// 设置运行时参数
substream->runtime->dma_area = dmab->area;
substream->runtime->dma_addr = dmab->addr;
substream->runtime->dma_bytes = size;
return 0;
}
// 音频DMA传输回调
static void audio_dma_callback(void *data)
{
struct snd_pcm_substream *substream = data;
struct snd_pcm_runtime *runtime = substream->runtime;
// 更新硬件指针
snd_pcm_period_elapsed(substream);
}
// 配置音频DMA控制器
static int configure_audio_dma(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct device *dev = rtd->dev;
struct dma_chan *chan;
struct dma_slave_config config;
int ret;
// 获取DMA通道
chan = dma_request_slave_channel(dev, "tx");
if (!chan)
return -ENODEV;
// 配置DMA从设备参数
memset(&config, 0, sizeof(config));
config.direction = DMA_MEM_TO_DEV;
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
config.dst_maxburst = 8;
// 设置外设地址(音频FIFO)
config.dst_addr = get_audio_fifo_addr();
// 应用配置
ret = dmaengine_slave_config(chan, &config);
if (ret < 0) {
dma_release_channel(chan);
return ret;
}
// 准备DMA传输
struct dma_async_tx_descriptor *desc;
desc = dmaengine_prep_dma_cyclic(chan,
runtime->dma_addr,
runtime->dma_bytes,
runtime->period_size,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT);
if (!desc) {
dma_release_channel(chan);
return -ENOMEM;
}
// 设置回调函数
desc->callback = audio_dma_callback;
desc->callback_param = substream;
// 提交DMA传输
dmaengine_submit(desc);
dma_async_issue_pending(chan);
// 保存通道供后续使用
runtime->private_data = chan;
return 0;
}
3.4 摄像头接口DMA
摄像头控制器(如CSI、DCMI)使用DMA将图像数据从传感器传输到内存:
3.4.1 图像采集DMA
c
// 摄像头DMA缓冲区管理
struct camera_buffer {
struct vb2_buffer vb; // Video4Linux2缓冲区
struct list_head list; // 链表节点
dma_addr_t dma_addr; // DMA地址
unsigned long size; // 缓冲区大小
unsigned int sequence; // 帧序列号
struct timeval timestamp; // 时间戳
};
// 摄像头DMA描述符
struct camera_dma_desc {
dma_addr_t src_addr; // 源地址(摄像头FIFO)
dma_addr_t dst_addr; // 目的地址(内存)
uint32_t stride; // 行跨度
uint32_t frame_size; // 帧大小
uint32_t control; // 控制字
struct camera_dma_desc *next; // 下一个描述符
};
// 图像采集DMA配置
static int camera_configure_dma(struct camera_device *cam)
{
struct device *dev = cam->dev;
struct camera_buffer *buf;
struct scatterlist *sg;
int ret, i;
// 为每个缓冲区设置分散-聚集列表
list_for_each_entry(buf, &cam->dma_buffers, list) {
// 为每个缓冲区创建分散列表
sg = kcalloc(1, sizeof(*sg), GFP_KERNEL);
if (!sg)
return -ENOMEM;
// 初始化分散列表
sg_init_table(sg, 1);
sg_dma_address(sg) = buf->dma_addr;
sg_dma_len(sg) = buf->size;
// 映射分散列表用于DMA
ret = dma_map_sg(dev, sg, 1, DMA_FROM_DEVICE);
if (ret <= 0) {
kfree(sg);
return -EIO;
}
// 保存分散列表
buf->sg = sg;
buf->sg_count = ret;
}
// 配置DMA控制器
ret = camera_dma_setup(cam);
if (ret < 0)
return ret;
return 0;
}
// 启动图像采集DMA
static int camera_start_dma(struct camera_device *cam)
{
struct dma_chan *chan = cam->dma_chan;
struct camera_buffer *buf;
struct dma_async_tx_descriptor *desc;
int ret;
// 获取下一个缓冲区
buf = list_first_entry(&cam->dma_buffers,
struct camera_buffer, list);
list_rotate_left(&cam->dma_buffers);
// 准备DMA传输
desc = dmaengine_prep_slave_sg(chan,
buf->sg,
buf->sg_count,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!desc)
return -ENOMEM;
// 设置回调
desc->callback = camera_dma_callback;
desc->callback_param = cam;
// 提交传输
ret = dmaengine_submit(desc);
if (ret < 0)
return ret;
// 触发DMA传输
dma_async_issue_pending(chan);
// 更新状态
cam->active_buffer = buf;
cam->frame_count++;
return 0;
}
// DMA完成回调
static void camera_dma_callback(void *param)
{
struct camera_device *cam = param;
struct camera_buffer *buf = cam->active_buffer;
struct vb2_buffer *vb = &buf->vb;
// DMA传输完成,缓冲区已满
dma_sync_sg_for_cpu(cam->dev, buf->sg, buf->sg_count,
DMA_FROM_DEVICE);
// 设置缓冲区时间戳和序列号
vb->timestamp = ktime_get_ns();
vb->sequence = cam->sequence++;
// 通知上层缓冲区就绪
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
// 启动下一个DMA传输
if (cam->streaming)
camera_start_dma(cam);
}
3.5 SPI控制器DMA
SPI控制器可以使用DMA提高大数据块传输的效率:
3.5.1 SPI DMA传输
c
// SPI DMA传输结构
struct spi_dma_transfer {
struct scatterlist tx_sg; // 发送分散列表
struct scatterlist rx_sg; // 接收分散列表
dma_addr_t tx_dma; // 发送DMA地址
dma_addr_t rx_dma; // 接收DMA地址
size_t len; // 传输长度
bool tx_sg_mapped; // 发送SG已映射
bool rx_sg_mapped; // 接收SG已映射
};
// SPI DMA传输实现
static int spi_dma_transfer(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *xfer)
{
struct device *dev = &ctlr->dev;
struct dma_chan *tx_chan, *rx_chan;
struct dma_async_tx_descriptor *tx_desc, *rx_desc;
dma_cookie_t tx_cookie, rx_cookie;
enum dma_data_direction dir;
int ret;
// 获取DMA通道
tx_chan = ctlr->dma_tx;
rx_chan = ctlr->dma_rx;
// 配置发送DMA
if (xfer->tx_buf && tx_chan) {
// 映射发送缓冲区
ret = spi_map_buf(ctlr, dev, &xfer->tx_sg,
xfer->tx_buf, xfer->len, DMA_TO_DEVICE);
if (ret)
return ret;
// 准备发送DMA描述符
tx_desc = dmaengine_prep_slave_sg(tx_chan,
&xfer->tx_sg, 1,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
if (!tx_desc) {
spi_unmap_buf(ctlr, dev, &xfer->tx_sg, DMA_TO_DEVICE);
return -ENOMEM;
}
// 提交发送DMA
tx_cookie = dmaengine_submit(tx_desc);
if (dma_submit_error(tx_cookie)) {
spi_unmap_buf(ctlr, dev, &xfer->tx_sg, DMA_TO_DEVICE);
return -ENOMEM;
}
}
// 配置接收DMA
if (xfer->rx_buf && rx_chan) {
// 映射接收缓冲区
ret = spi_map_buf(ctlr, dev, &xfer->rx_sg,
xfer->rx_buf, xfer->len, DMA_FROM_DEVICE);
if (ret) {
if (tx_chan)
dmaengine_terminate_all(tx_chan);
return ret;
}
// 准备接收DMA描述符
rx_desc = dmaengine_prep_slave_sg(rx_chan,
&xfer->rx_sg, 1,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
if (!rx_desc) {
spi_unmap_buf(ctlr, dev, &xfer->rx_sg, DMA_FROM_DEVICE);
if (tx_chan)
dmaengine_terminate_all(tx_chan);
return -ENOMEM;
}
// 提交接收DMA
rx_cookie = dmaengine_submit(rx_desc);
if (dma_submit_error(rx_cookie)) {
spi_unmap_buf(ctlr, dev, &xfer->rx_sg, DMA_FROM_DEVICE);
if (tx_chan)
dmaengine_terminate_all(tx_chan);
return -ENOMEM;
}
}
// 启动DMA传输
if (tx_chan)
dma_async_issue_pending(tx_chan);
if (rx_chan)
dma_async_issue_pending(rx_chan);
// 等待传输完成
if (tx_chan) {
ret = dma_wait_for_async_tx(tx_cookie);
if (ret) {
if (rx_chan)
dmaengine_terminate_all(rx_chan);
return ret;
}
// 解除发送缓冲区映射
spi_unmap_buf(ctlr, dev, &xfer->tx_sg, DMA_TO_DEVICE);
}
if (rx_chan) {
ret = dma_wait_for_async_tx(rx_cookie);
if (ret)
return ret;
// 同步接收缓冲区
dma_sync_sg_for_cpu(dev, &xfer->rx_sg, 1, DMA_FROM_DEVICE);
// 解除接收缓冲区映射
spi_unmap_buf(ctlr, dev, &xfer->rx_sg, DMA_FROM_DEVICE);
}
return 0;
}
第四章:DMA性能优化策略
4.1 缓冲区对齐优化
正确的缓冲区对齐可以显著提高DMA性能:
c
// DMA缓冲区对齐要求
#define DMA_MIN_ALIGN 32 // 最小对齐要求
#define DMA_CACHE_ALIGN L1_CACHE_BYTES // 缓存行对齐
// 对齐分配函数
static void *alloc_dma_buffer_aligned(struct device *dev,
size_t size,
dma_addr_t *dma_handle,
gfp_t gfp)
{
void *cpu_addr;
size_t aligned_size;
// 计算对齐后的大小
aligned_size = ALIGN(size, DMA_MIN_ALIGN);
// 分配对齐的内存
cpu_addr = dma_alloc_coherent(dev, aligned_size,
dma_handle,
gfp | __GFP_ZERO);
if (!cpu_addr)
return NULL;
// 确保地址对齐
if (!IS_ALIGNED(*dma_handle, DMA_MIN_ALIGN)) {
dma_free_coherent(dev, aligned_size, cpu_addr, *dma_handle);
return NULL;
}
return cpu_addr;
}
// 页面大小对齐
static int alloc_page_aligned_buffer(struct device *dev,
size_t size,
void **cpu_addr,
dma_addr_t *dma_addr)
{
struct page *page;
int order;
// 计算需要的页面数量
order = get_order(size);
// 分配连续页面
page = alloc_pages(GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN, order);
if (!page)
return -ENOMEM;
// 获取CPU地址
*cpu_addr = page_address(page);
// 映射DMA地址
*dma_addr = dma_map_page(dev, page, 0,
PAGE_SIZE << order,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(dev, *dma_addr)) {
__free_pages(page, order);
return -ENOMEM;
}
return 0;
}
4.2 缓存优化策略
4.2.1 缓存预取
c
// DMA缓冲区缓存预取
static void prefetch_dma_buffer(void *buffer, size_t size)
{
char *ptr = buffer;
char *end = ptr + size;
// 预取整个缓冲区
while (ptr < end) {
prefetch(ptr);
ptr += L1_CACHE_BYTES;
}
}
// 智能预取策略
static void smart_prefetch(struct dma_buffer *buf)
{
// 根据缓冲区使用模式选择预取策略
switch (buf->access_pattern) {
case SEQUENTIAL_ACCESS:
// 顺序访问:预取接下来的几个缓存行
prefetch_range(buf->data + buf->pos,
L1_CACHE_BYTES * 4);
break;
case RANDOM_ACCESS:
// 随机访问:预取整个缓冲区
prefetch_dma_buffer(buf->data, buf->size);
break;
case STREAMING_ACCESS:
// 流式访问:使用非时间局部性提示
prefetchw(buf->data); // 预取并准备写入
break;
}
}
4.2.2 缓存一致性优化
c
// 批量同步优化
static void optimized_dma_sync(struct device *dev,
dma_addr_t dma_addr,
size_t size,
enum dma_data_direction dir)
{
// 对于大缓冲区,使用批量同步
if (size > DMA_SYNC_THRESHOLD) {
// 分批同步,避免长时间占用总线
size_t chunk;
for (chunk = 0; chunk < size; chunk += DMA_SYNC_CHUNK) {
size_t sync_size = min(DMA_SYNC_CHUNK, size - chunk);
dma_sync_single_for_device(dev,
dma_addr + chunk,
sync_size,
dir);
// 允许其他操作插空执行
cond_resched();
}
} else {
// 小缓冲区直接同步
dma_sync_single_for_device(dev, dma_addr, size, dir);
}
}
// 写组合优化
static void write_combining_optimization(void)
{
// 对于频繁写入的DMA缓冲区,使用写组合
#ifdef CONFIG_X86
// x86架构的写组合设置
set_memory_wc((unsigned long)buffer, size >> PAGE_SHIFT);
#endif
#ifdef CONFIG_ARM
// ARM架构的缓存策略设置
pgprot_val(vma->vm_page_prot) &= ~L_PTE_BUFFERABLE;
pgprot_val(vma->vm_page_prot) |= L_PTE_WRITEALLOC;
#endif
}
4.3 分散-聚集优化
4.3.1 智能SG列表合并
c
// SG列表合并优化
static int optimize_sg_list(struct scatterlist *sg,
int nents,
size_t *total_len)
{
struct scatterlist *s;
int i, merged = 0;
for_each_sg(sg, s, nents, i) {
// 检查是否可以与下一个SG条目合并
if (i < nents - 1) {
struct scatterlist *next = sg_next(s);
// 检查物理连续性和对齐
if (sg_dma_address(s) + sg_dma_len(s) ==
sg_dma_address(next) &&
is_aligned_for_dma(sg_dma_address(next))) {
// 合并SG条目
sg_dma_len(s) += sg_dma_len(next);
merged++;
}
}
*total_len += sg_dma_len(s);
}
return nents - merged;
}
// 自适应SG分配
static struct scatterlist *adaptive_sg_alloc(int nents, gfp_t gfp)
{
// 根据nents大小选择分配策略
if (nents <= 8) {
// 小数量:使用栈上分配
return __sg_alloc_table_from_pages(NULL, nents, 0,
SG_CHUNK_SIZE,
GFP_KERNEL, NULL);
} else if (nents <= 64) {
// 中等数量:使用kmalloc
return kmalloc_array(nents, sizeof(struct scatterlist), gfp);
} else {
// 大数量:使用vmalloc
return vmalloc(nents * sizeof(struct scatterlist));
}
}
4.4 DMA描述符优化
4.4.1 描述符缓存
c
// DMA描述符缓存池
struct dma_desc_pool {
struct kmem_cache *cache; // SLAB缓存
size_t desc_size; // 描述符大小
int align; // 对齐要求
atomic_t allocated; // 已分配计数
atomic_t freed; // 已释放计数
};
// 创建描述符缓存池
static struct dma_desc_pool *create_desc_pool(const char *name,
size_t desc_size,
int align)
{
struct dma_desc_pool *pool;
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
if (!pool)
return NULL;
// 创建SLAB缓存
pool->cache = kmem_cache_create(name,
desc_size,
align,
SLAB_HWCACHE_ALIGN |
SLAB_PANIC,
NULL);
if (!pool->cache) {
kfree(pool);
return NULL;
}
pool->desc_size = desc_size;
pool->align = align;
return pool;
}
// 从池中分配描述符
static void *alloc_desc_from_pool(struct dma_desc_pool *pool)
{
void *desc;
desc = kmem_cache_alloc(pool->cache, GFP_KERNEL);
if (desc) {
memset(desc, 0, pool->desc_size);
atomic_inc(&pool->allocated);
}
return desc;
}
4.4.2 批处理描述符更新
c
// 批处理描述符更新
static void batch_update_descriptors(struct dma_channel *chan,
struct dma_desc *descs,
int count)
{
int i;
// 一次性更新所有描述符
for (i = 0; i < count; i++) {
// 预取下一个描述符
if (i < count - 1)
prefetch(&descs[i + 1]);
// 批量设置描述符字段
descs[i].status = DESC_VALID;
descs[i].next = &descs[i + 1];
}
// 设置最后一个描述符
descs[count - 1].next = NULL;
// 一次性写回所有描述符
dma_sync_single_for_device(chan->dev,
chan->desc_dma,
count * sizeof(struct dma_desc),
DMA_TO_DEVICE);
}
4.5 中断优化策略
4.5.1 中断合并
c
// DMA中断合并
struct dma_interrupt_coalescing {
unsigned int count_threshold; // 计数阈值
unsigned int time_threshold; // 时间阈值(ms)
unsigned int packet_count; // 当前包计数
unsigned long last_interrupt; // 上次中断时间
struct timer_list timer; // 超时定时器
};
// 延迟中断处理
static void delayed_interrupt_handler(unsigned long data)
{
struct dma_channel *chan = (struct dma_channel *)data;
struct dma_interrupt_coalescing *coal = &chan->coal;
// 检查是否达到阈值
if (coal->packet_count >= coal->count_threshold) {
// 触发中断
handle_dma_interrupt(chan);
coal->packet_count = 0;
}
}
// 中断合并检查
static bool should_defer_interrupt(struct dma_channel *chan)
{
struct dma_interrupt_coalescing *coal = &chan->coal;
unsigned long now = jiffies;
coal->packet_count++;
// 检查是否达到计数阈值
if (coal->packet_count >= coal->count_threshold) {
coal->packet_count = 0;
return false; // 立即触发中断
}
// 检查是否超时
if (time_after(now, coal->last_interrupt +
msecs_to_jiffies(coal->time_threshold))) {
coal->last_interrupt = now;
return false; // 超时触发中断
}
// 重新启动定时器
mod_timer(&coal->timer,
now + msecs_to_jiffies(coal->time_threshold));
return true; // 延迟中断
}
4.5.2 MSI-X中断优化
c
// MSI-X中断分配
static int setup_msix_interrupts(struct dma_device *dma_dev)
{
struct pci_dev *pdev = dma_dev->pdev;
int i, num_vectors, ret;
// 获取支持的MSI-X向量数
num_vectors = pci_msix_vec_count(pdev);
if (num_vectors < 0)
return num_vectors;
// 申请MSI-X向量
ret = pci_alloc_irq_vectors(pdev, num_vectors, num_vectors,
PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
if (ret < 0)
return ret;
// 为每个DMA通道分配独立的MSI-X向量
for (i = 0; i < dma_dev->num_channels; i++) {
int vector = i % num_vectors;
ret = pci_irq_vector(pdev, vector);
if (ret < 0)
goto error;
dma_dev->channels[i].irq = ret;
// 设置中断处理函数
ret = request_irq(dma_dev->channels[i].irq,
dma_channel_interrupt,
0,
dma_dev->name,
&dma_dev->channels[i]);
if (ret)
goto error;
}
return 0;
error:
// 清理资源
while (i-- > 0) {
free_irq(dma_dev->channels[i].irq, &dma_dev->channels[i]);
}
pci_free_irq_vectors(pdev);
return ret;
}
4.6 内存访问模式优化
4.6.1 非时序内存访问
c
// 非时序内存访问优化
static void nontemporal_copy(void *dst, const void *src, size_t size)
{
#ifdef CONFIG_X86
// x86的非时序存储指令
if (static_cpu_has(X86_FEATURE_XMM2)) {
// 使用SSE2非时序存储
asm volatile(
"1:\n"
"movdqu (%1), %%xmm0\n"
"movntdq %%xmm0, (%0)\n"
"add $16, %1\n"
"add $16, %0\n"
"sub $16, %2\n"
"jnz 1b\n"
"sfence\n"
: "+r"(dst), "+r"(src), "+r"(size)
:
: "xmm0", "memory"
);
} else
#endif
{
// 回退到普通复制
memcpy(dst, src, size);
}
}
// DMA缓冲区的非时序访问
static void optimize_dma_buffer_access(struct dma_buffer *buf)
{
// 根据访问模式设置内存类型
switch (buf->access_type) {
case DMA_ACCESS_SEQUENTIAL:
// 顺序访问:使用写组合
set_memory_wc((unsigned long)buf->vaddr,
buf->size >> PAGE_SHIFT);
break;
case DMA_ACCESS_RANDOM:
// 随机访问:使用写回缓存
set_memory_wb((unsigned long)buf->vaddr,
buf->size >> PAGE_SHIFT);
break;
case DMA_ACCESS_STREAMING:
// 流式访问:使用非时序存储
buf->flags |= DMA_BUF_NONTEMPORAL;
break;
}
}
4.6.2 预取优化
c
// 自适应预取
static void adaptive_prefetch(struct dma_operation *op)
{
static unsigned long access_pattern[3] = {0};
static int pattern_index = 0;
// 记录访问模式
access_pattern[pattern_index] = op->bytes_transferred;
pattern_index = (pattern_index + 1) % 3;
// 分析访问模式
if (is_sequential_access(access_pattern)) {
// 顺序访问:积极预取
aggressive_prefetch(op->next_buffer);
} else if (is_random_access(access_pattern)) {
// 随机访问:保守预取
conservative_prefetch(op->next_buffer);
} else {
// 混合模式:适中预取
moderate_prefetch(op->next_buffer);
}
}
// 基于机器学习的预取优化
#ifdef CONFIG_DMA_ML_PREFETCH
static void ml_based_prefetch(struct dma_operation *op)
{
struct ml_prefetch_model *model = get_prefetch_model();
float confidence;
int prefetch_size;
// 使用机器学习模型预测预取大小
prefetch_size = predict_prefetch_size(model, op, &confidence);
if (confidence > 0.7) { // 高置信度
perform_prefetch(op->next_buffer, prefetch_size);
} else if (confidence > 0.4) { // 中等置信度
perform_prefetch(op->next_buffer, prefetch_size / 2);
}
// 低置信度不预取
}
#endif
4.7 电源管理优化
4.7.1 动态频率调整
c
// DMA频率动态调整
struct dma_power_profile {
unsigned int min_freq; // 最小频率
unsigned int max_freq; // 最大频率
unsigned int cur_freq; // 当前频率
unsigned int target_freq; // 目标频率
struct dev_pm_qos_request qos_req; // PM QoS请求
};
// 基于负载的频率调整
static void adjust_dma_frequency(struct dma_controller *ctrl,
unsigned int load_percentage)
{
struct dma_power_profile *profile = &ctrl->power_profile;
unsigned int new_freq;
// 根据负载调整频率
if (load_percentage > 80) {
new_freq = profile->max_freq; // 高负载:最大频率
} else if (load_percentage > 50) {
new_freq = (profile->max_freq * 3) / 4; // 中高负载
} else if (load_percentage > 20) {
new_freq = profile->max_freq / 2; // 中负载
} else {
new_freq = profile->min_freq; // 低负载:最小频率
}
// 更新频率
if (new_freq != profile->cur_freq) {
set_dma_clock_frequency(ctrl, new_freq);
profile->cur_freq = new_freq;
}
}
// 电源状态管理
static void manage_dma_power_state(struct dma_controller *ctrl)
{
unsigned long idle_time = jiffies - ctrl->last_active;
if (idle_time > msecs_to_jiffies(100)) {
// 空闲超过100ms,进入低功耗状态
enter_low_power_state(ctrl);
ctrl->power_state = DMA_POWER_LOW;
} else if (idle_time > msecs_to_jiffies(10)) {
// 空闲超过10ms,进入中等功耗状态
enter_medium_power_state(ctrl);
ctrl->power_state = DMA_POWER_MEDIUM;
} else {
// 活跃状态
ctrl->power_state = DMA_POWER_HIGH;
}
}
第五章:DMA调试与故障排除
5.1 DMA调试工具
5.1.1 内核调试支持
c
// DMA调试配置
#ifdef CONFIG_DMA_DEBUG
// DMA调试接口
struct dma_debug_entry {
struct list_head list;
struct device *dev;
enum dma_data_direction direction;
dma_addr_t dev_addr;
unsigned long size;
void *cpu_addr;
struct page *page;
unsigned long offset;
u8 type;
u8 map_err_type;
};
// DMA调试统计
struct dma_debug_stats {
unsigned long num_maps;
unsigned long num_unmaps;
unsigned long num_allocations;
unsigned long num_frees;
unsigned long errors;
unsigned long leaks;
};
// 启用DMA调试
static int enable_dma_debug(struct device *dev)
{
// 检查内核配置
if (!IS_ENABLED(CONFIG_DMA_API_DEBUG)) {
dev_warn(dev, "DMA debug not enabled in kernel\n");
return -ENOTSUPP;
}
// 设置调试级别
debug_dma_set_dma_mask(dev, DMA_BIT_MASK(64));
// 启用详细日志
global_dma_debug = 1;
return 0;
}
#endif
// DMA调试信息输出
static void dump_dma_debug_info(struct device *dev)
{
#ifdef CONFIG_DMA_API_DEBUG
struct dma_debug_entry *entry;
unsigned long flags;
spin_lock_irqsave(&dma_debug_lock, flags);
// 输出DMA映射信息
list_for_each_entry(entry, &dma_debug_mappings, list) {
if (entry->dev == dev) {
pr_info("DMA mapping: dev=%s, dir=%d, "
"dma_addr=%pad, cpu_addr=%p, size=%lu\n",
dev_name(dev), entry->direction,
&entry->dev_addr, entry->cpu_addr,
entry->size);
}
}
spin_unlock_irqrestore(&dma_debug_lock, flags);
#endif
}
5.1.2 性能分析工具
c
// DMA性能分析
struct dma_perf_stats {
ktime_t start_time;
ktime_t end_time;
unsigned long bytes_transferred;
unsigned int num_transfers;
unsigned int errors;
// 详细统计
struct {
unsigned long setup_time;
unsigned long transfer_time;
unsigned long cleanup_time;
unsigned long wait_time;
} breakdown;
};
// 性能测量宏
#define DMA_PERF_START(stats) \
do { \
(stats)->start_time = ktime_get(); \
(stats)->num_transfers++; \
} while (0)
#define DMA_PERF_END(stats, bytes) \
do { \
(stats)->end_time = ktime_get(); \
(stats)->bytes_transferred += (bytes); \
} while (0)
// 性能分析函数
static void analyze_dma_performance(struct dma_perf_stats *stats)
{
s64 total_time = ktime_to_ns(ktime_sub(stats->end_time,
stats->start_time));
u64 throughput;
if (total_time > 0) {
// 计算吞吐量(MB/s)
throughput = (stats->bytes_transferred * NSEC_PER_SEC) /
(total_time * 1024 * 1024);
pr_info("DMA Performance: %llu MB/s, %lu bytes in %lld ns\n",
throughput, stats->bytes_transferred, total_time);
// 输出详细分析
pr_info(" Setup: %lu ns, Transfer: %lu ns, "
"Cleanup: %lu ns, Wait: %lu ns\n",
stats->breakdown.setup_time,
stats->breakdown.transfer_time,
stats->breakdown.cleanup_time,
stats->breakdown.wait_time);
}
}
5.2 常见DMA问题诊断
5.2.1 DMA传输失败诊断
c
// DMA传输失败诊断函数
static int diagnose_dma_failure(struct dma_channel *chan,
dma_addr_t dma_addr,
size_t size,
enum dma_data_direction dir)
{
struct device *dev = chan->dev;
int ret = 0;
// 1. 检查DMA地址有效性
if (!dma_addr) {
dev_err(dev, "DMA address is NULL\n");
return -EINVAL;
}
// 2. 检查地址对齐
if (!IS_ALIGNED(dma_addr, chan->align_req)) {
dev_err(dev, "DMA address %pad not aligned to %d\n",
&dma_addr, chan->align_req);
ret = -EINVAL;
}
// 3. 检查缓冲区大小
if (size > chan->max_burst_size) {
dev_err(dev, "Size %zu exceeds max burst size %u\n",
size, chan->max_burst_size);
ret = -EINVAL;
}
// 4. 检查DMA映射
#ifdef CONFIG_DMA_API_DEBUG
if (!debug_dma_mapping_error(dev, dma_addr)) {
dev_err(dev, "DMA mapping error at address %pad\n",
&dma_addr);
ret = -EFAULT;
}
#endif
// 5. 检查硬件状态
if (!dma_channel_is_idle(chan)) {
dev_err(dev, "DMA channel is busy\n");
ret = -EBUSY;
}
// 6. 检查中断状态
if (chan->irq < 0) {
dev_err(dev, "DMA channel IRQ not configured\n");
ret = -ENXIO;
}
// 输出详细诊断信息
if (ret) {
dump_dma_channel_state(chan);
dump_dma_buffer_info(dev, dma_addr, size);
}
return ret;
}
// DMA通道状态转储
static void dump_dma_channel_state(struct dma_channel *chan)
{
dev_info(chan->dev, "DMA Channel State:\n");
dev_info(chan->dev, " Status: 0x%08x\n",
readl(chan->regs + DMA_STATUS_REG));
dev_info(chan->dev, " Control: 0x%08x\n",
readl(chan->regs + DMA_CTRL_REG));
dev_info(chan->dev, " Source: 0x%08x\n",
readl(chan->regs + DMA_SRC_REG));
dev_info(chan->dev, " Destination: 0x%08x\n",
readl(chan->regs + DMA_DST_REG));
dev_info(chan->dev, " Length: 0x%08x\n",
readl(chan->regs + DMA_LEN_REG));
// 检查错误标志
u32 status = readl(chan->regs + DMA_STATUS_REG);
if (status & DMA_ERROR_MASK) {
dev_err(chan->dev, "DMA Error detected: 0x%08x\n", status);
if (status & DMA_ERROR_SRC)
dev_err(chan->dev, " Source bus error\n");
if (status & DMA_ERROR_DST)
dev_err(chan->dev, " Destination bus error\n");
if (status & DMA_ERROR_TIMEOUT)
dev_err(chan->dev, " Transfer timeout\n");
if (status & DMA_ERROR_ALIGN)
dev_err(chan->dev, " Alignment error\n");
}
}
5.2.2 内存一致性问题的调试
c
// 内存一致性检查
static void check_cache_coherency(struct device *dev,
dma_addr_t dma_addr,
void *cpu_addr,
size_t size)
{
#ifdef CONFIG_DMA_API_DEBUG_COHERENCY
// 创建测试模式
static const u8 test_pattern[] = {
0x00, 0xFF, 0x55, 0xAA, 0x33, 0xCC, 0x99, 0x66
};
u8 *buffer = cpu_addr;
int i, pattern_len = ARRAY_SIZE(test_pattern);
// 1. 写入测试模式
for (i = 0; i < size; i++) {
buffer[i] = test_pattern[i % pattern_len];
}
// 2. 确保CPU写操作对设备可见
dma_sync_single_for_device(dev, dma_addr, size, DMA_TO_DEVICE);
// 3. 从设备角度读取数据(模拟)
u8 *device_view = ioremap_cache(dma_to_phys(dev, dma_addr), size);
if (device_view) {
// 比较数据
for (i = 0; i < size; i++) {
if (device_view[i] != test_pattern[i % pattern_len]) {
dev_err(dev, "Coherency error at offset %d: "
"CPU=0x%02x, Device=0x%02x\n",
i, buffer[i], device_view[i]);
break;
}
}
iounmap(device_view);
}
// 4. 测试反向传输
memset(buffer, 0, size);
dma_sync_single_for_device(dev, dma_addr, size, DMA_TO_DEVICE);
// 模拟设备写入
device_view = ioremap_cache(dma_to_phys(dev, dma_addr), size);
if (device_view) {
for (i = 0; i < size; i++) {
device_view[i] = test_pattern[i % pattern_len];
}
iounmap(device_view);
}
// 确保设备写操作对CPU可见
dma_sync_single_for_cpu(dev, dma_addr, size, DMA_FROM_DEVICE);
// 验证数据
for (i = 0; i < size; i++) {
if (buffer[i] != test_pattern[i % pattern_len]) {
dev_err(dev, "Reverse coherency error at offset %d: "
"CPU=0x%02x, expected=0x%02x\n",
i, buffer[i], test_pattern[i % pattern_len]);
break;
}
}
#endif
}
// 缓存行对齐检查
static void check_cache_alignment(struct device *dev,
dma_addr_t dma_addr,
size_t size)
{
// 检查起始地址对齐
if (!IS_ALIGNED(dma_addr, L1_CACHE_BYTES)) {
dev_warn(dev, "DMA address %pad not cache-line aligned "
"(alignment: %d bytes)\n",
&dma_addr, L1_CACHE_BYTES);
}
// 检查大小对齐
if (!IS_ALIGNED(size, L1_CACHE_BYTES)) {
dev_warn(dev, "DMA size %zu not cache-line aligned\n", size);
}
// 检查是否跨越缓存行边界
dma_addr_t end_addr = dma_addr + size - 1;
if ((dma_addr & ~(L1_CACHE_BYTES - 1)) !=
(end_addr & ~(L1_CACHE_BYTES - 1))) {
dev_info(dev, "DMA buffer spans multiple cache lines\n");
}
}
5.3 DMA泄漏检测
5.3.1 资源泄漏检测
c
// DMA资源泄漏检测
struct dma_leak_detector {
struct list_head allocations;
spinlock_t lock;
unsigned long num_allocated;
unsigned long num_freed;
unsigned long total_allocated;
unsigned long max_allocated;
};
// 跟踪DMA分配
static void *track_dma_alloc(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp)
{
void *cpu_addr;
cpu_addr = dma_alloc_coherent(dev, size, dma_handle, gfp);
if (cpu_addr) {
struct dma_allocation *alloc;
alloc = kmalloc(sizeof(*alloc), GFP_KERNEL);
if (alloc) {
alloc->dev = dev;
alloc->cpu_addr = cpu_addr;
alloc->dma_handle = *dma_handle;
alloc->size = size;
alloc->timestamp = jiffies;
alloc->pid = current->pid;
strncpy(alloc->comm, current->comm, TASK_COMM_LEN);
spin_lock(&leak_detector.lock);
list_add(&alloc->list, &leak_detector.allocations);
leak_detector.num_allocated++;
leak_detector.total_allocated += size;
if (leak_detector.num_allocated > leak_detector.max_allocated)
leak_detector.max_allocated = leak_detector.num_allocated;
spin_unlock(&leak_detector.lock);
}
}
return cpu_addr;
}
// 跟踪DMA释放
static void track_dma_free(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t dma_handle)
{
struct dma_allocation *alloc, *tmp;
spin_lock(&leak_detector.lock);
list_for_each_entry_safe(alloc, tmp, &leak_detector.allocations, list) {
if (alloc->dev == dev &&
alloc->cpu_addr == cpu_addr &&
alloc->dma_handle == dma_handle) {
list_del(&alloc->list);
kfree(alloc);
leak_detector.num_allocated--;
leak_detector.num_freed++;
break;
}
}
spin_unlock(&leak_detector.lock);
dma_free_coherent(dev, size, cpu_addr, dma_handle);
}
// 报告泄漏
static void report_dma_leaks(void)
{
struct dma_allocation *alloc;
unsigned long now = jiffies;
pr_warn("=== DMA Allocation Leak Report ===\n");
spin_lock(&leak_detector.lock);
if (list_empty(&leak_detector.allocations)) {
pr_info("No DMA leaks detected\n");
} else {
pr_warn("Found %lu potential DMA leaks:\n",
leak_detector.num_allocated);
list_for_each_entry(alloc, &leak_detector.allocations, list) {
unsigned long age = (now - alloc->timestamp) / HZ;
pr_warn(" PID: %d, Comm: %s, Age: %lu seconds\n",
alloc->pid, alloc->comm, age);
pr_warn(" Device: %s, Size: %zu, CPU: %p, DMA: %pad\n",
dev_name(alloc->dev), alloc->size,
alloc->cpu_addr, &alloc->dma_handle);
// 如果泄漏时间过长,输出警告
if (age > 60) { // 超过1分钟
pr_err(" *** POSSIBLE DMA LEAK (age: %lu seconds) ***\n",
age);
}
}
}
pr_info("Statistics: allocated=%lu, freed=%lu, "
"total_bytes=%lu, max_allocated=%lu\n",
leak_detector.num_allocated + leak_detector.num_freed,
leak_detector.num_freed,
leak_detector.total_allocated,
leak_detector.max_allocated);
spin_unlock(&leak_detector.lock);
}
5.3.2 自动化测试框架
c
// DMA自动化测试框架
struct dma_test_case {
const char *name;
int (*setup)(struct dma_test_context *ctx);
int (*run)(struct dma_test_context *ctx);
int (*verify)(struct dma_test_context *ctx);
void (*cleanup)(struct dma_test_context *ctx);
unsigned int iterations;
unsigned int flags;
};
// DMA测试上下文
struct dma_test_context {
struct device *dev;
struct dma_chan *chan;
void *src_buffer;
void *dst_buffer;
dma_addr_t src_dma;
dma_addr_t dst_dma;
size_t buffer_size;
struct completion complete;
int error;
u64 duration_ns;
};
// 执行DMA测试套件
static int run_dma_test_suite(struct device *dev)
{
static const struct dma_test_case test_cases[] = {
{
.name = "Basic DMA transfer",
.setup = setup_basic_transfer,
.run = run_basic_transfer,
.verify = verify_basic_transfer,
.cleanup = cleanup_basic_transfer,
.iterations = 100,
},
{
.name = "Scatter-Gather DMA",
.setup = setup_sg_transfer,
.run = run_sg_transfer,
.verify = verify_sg_transfer,
.cleanup = cleanup_sg_transfer,
.iterations = 50,
},
{
.name = "Cyclic DMA transfer",
.setup = setup_cyclic_transfer,
.run = run_cyclic_transfer,
.verify = verify_cyclic_transfer,
.cleanup = cleanup_cyclic_transfer,
.iterations = 20,
.flags = TEST_FLAG_LONG_RUNNING,
},
// 更多测试用例...
};
int i, ret;
int passed = 0, failed = 0;
pr_info("Starting DMA test suite on device %s\n", dev_name(dev));
for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
const struct dma_test_case *tc = &test_cases[i];
struct dma_test_context ctx = {
.dev = dev,
};
pr_info("Running test: %s\n", tc->name);
// 设置测试环境
ret = tc->setup(&ctx);
if (ret) {
pr_err("Test %s setup failed: %d\n", tc->name, ret);
failed++;
continue;
}
// 运行测试
ret = tc->run(&ctx);
if (ret) {
pr_err("Test %s run failed: %d\n", tc->name, ret);
tc->cleanup(&ctx);
failed++;
continue;
}
// 验证结果
ret = tc->verify(&ctx);
if (ret) {
pr_err("Test %s verification failed: %d\n", tc->name, ret);
failed++;
} else {
pr_info("Test %s passed in %llu ns\n",
tc->name, ctx.duration_ns);
passed++;
}
// 清理
tc->cleanup(&ctx);
}
pr_info("DMA test suite completed: %d passed, %d failed\n",
passed, failed);
return failed == 0 ? 0 : -EIO;
}
// 基本传输测试
static int setup_basic_transfer(struct dma_test_context *ctx)
{
ctx->buffer_size = PAGE_SIZE * 4; // 16KB
// 分配源缓冲区
ctx->src_buffer = dma_alloc_coherent(ctx->dev, ctx->buffer_size,
&ctx->src_dma, GFP_KERNEL);
if (!ctx->src_buffer)
return -ENOMEM;
// 分配目标缓冲区
ctx->dst_buffer = dma_alloc_coherent(ctx->dev, ctx->buffer_size,
&ctx->dst_dma, GFP_KERNEL);
if (!ctx->dst_buffer) {
dma_free_coherent(ctx->dev, ctx->buffer_size,
ctx->src_buffer, ctx->src_dma);
return -ENOMEM;
}
// 初始化数据
memset(ctx->src_buffer, 0xAA, ctx->buffer_size);
memset(ctx->dst_buffer, 0x55, ctx->buffer_size);
// 获取DMA通道
ctx->chan = dma_request_channel(ctx->dev, NULL);
if (!ctx->chan) {
dma_free_coherent(ctx->dev, ctx->buffer_size,
ctx->dst_buffer, ctx->dst_dma);
dma_free_coherent(ctx->dev, ctx->buffer_size,
ctx->src_buffer, ctx->src_dma);
return -ENODEV;
}
init_completion(&ctx->complete);
return 0;
}
5.4 实时调试技术
5.4.1 跟踪点(Tracepoints)
c
// DMA跟踪点定义
#define CREATE_TRACE_POINTS
#include <trace/events/dma.h>
// DMA跟踪点使用
static int traced_dma_transfer(struct dma_chan *chan,
dma_addr_t src, dma_addr_t dst,
size_t len)
{
struct dma_async_tx_descriptor *desc;
dma_cookie_t cookie;
int ret;
// 记录开始跟踪
trace_dma_transfer_start(dev_name(chan->device->dev),
src, dst, len);
// 准备DMA传输
desc = dmaengine_prep_dma_memcpy(chan, dst, src, len, 0);
if (!desc) {
trace_dma_transfer_error(dev_name(chan->device->dev),
-ENOMEM);
return -ENOMEM;
}
// 设置回调
desc->callback = dma_transfer_complete;
desc->callback_param = chan;
// 提交传输
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
if (ret) {
trace_dma_transfer_error(dev_name(chan->device->dev), ret);
return ret;
}
// 记录提交跟踪
trace_dma_transfer_submit(dev_name(chan->device->dev), cookie);
// 启动传输
dma_async_issue_pending(chan);
// 记录启动跟踪
trace_dma_transfer_issue(dev_name(chan->device->dev));
return 0;
}
// DMA完成跟踪
static void dma_transfer_complete(void *param)
{
struct dma_chan *chan = param;
// 记录完成跟踪
trace_dma_transfer_complete(dev_name(chan->device->dev));
// 其他处理...
}
5.4.2 动态调试
c
// DMA动态调试
#ifdef CONFIG_DYNAMIC_DEBUG
#define dma_debug(dev, fmt, ...) \
dynamic_dev_dbg(dev, "DMA: " fmt, ##__VA_ARGS__)
#else
#define dma_debug(dev, fmt, ...) \
dev_dbg(dev, "DMA: " fmt, ##__VA_ARGS__)
#endif
// 详细调试输出
static void debug_dma_operation(struct device *dev,
const char *op,
dma_addr_t dma_addr,
void *cpu_addr,
size_t size,
enum dma_data_direction dir)
{
static const char *dir_str[] = {
[DMA_BIDIRECTIONAL] = "BIDIR",
[DMA_TO_DEVICE] = "TO_DEV",
[DMA_FROM_DEVICE] = "FROM_DEV",
[DMA_NONE] = "NONE",
};
dma_debug(dev, "%s: dma_addr=%pad, cpu_addr=%p, "
"size=%zu, dir=%s\n",
op, &dma_addr, cpu_addr, size,
dir_str[dir]);
// 输出栈跟踪(用于调试泄漏)
if (debug_stack_enabled) {
dump_stack();
}
// 输出内存内容(用于调试数据损坏)
if (debug_memory_enabled && size <= 64) {
dma_debug(dev, " Data: ");
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
cpu_addr, min(size, 64UL));
}
}
// 调试包装函数
static dma_addr_t debug_dma_map_single(struct device *dev, void *ptr,
size_t size,
enum dma_data_direction dir)
{
dma_addr_t dma_addr;
dma_addr = dma_map_single(dev, ptr, size, dir);
if (!dma_mapping_error(dev, dma_addr)) {
debug_dma_operation(dev, "map_single",
dma_addr, ptr, size, dir);
} else {
dma_debug(dev, "map_single failed\n");
}
return dma_addr;
}
第六章:高级DMA技术与未来趋势
6.1 IOMMU与SMMU技术
6.1.1 IOMMU高级特性
c
// IOMMU高级配置
struct iommu_advanced_features {
// 地址转换
bool two_level_translation; // 两级地址转换
bool nested_translation; // 嵌套转换(虚拟化)
bool pasid_support; // PASID支持
bool pri_support; // 页面请求接口
// 内存保护
bool access_flag; // 访问标志
bool dirty_flag; // 脏页标志
bool execute_protection; // 执行保护
bool user_protection; // 用户空间保护
// 性能特性
bool atc_support; // 地址转换缓存
bool coherent_support; // 一致性支持
bool svm_support; // 共享虚拟内存
};
// IOMMU域配置
static int configure_iommu_domain(struct device *dev,
struct iommu_domain *domain)
{
struct iommu_domain_geometry geom;
int ret;
// 获取设备DMA限制
u64 dma_mask = dma_get_required_mask(dev);
// 设置IOMMU域几何特性
geom.aperture_start = 0;
geom.aperture_end = dma_mask;
geom.force_aperture = true;
ret = iommu_domain_set_attr(domain,
DOMAIN_ATTR_GEOMETRY,
&geom);
if (ret)
return ret;
// 启用高级特性
if (iommu_capable(IOMMU_CAP_CACHE_COHERENCY)) {
u32 cache_coherent = 1;
iommu_domain_set_attr(domain,
DOMAIN_ATTR_CACHE_COHERENCY,
&cache_coherent);
}
// 设置页面大小
if (iommu_capable(IOMMU_CAP_PAGE_SIZE_4KB)) {
u32 page_size = PAGE_SIZE;
iommu_domain_set_attr(domain,
DOMAIN_ATTR_PAGE_SIZE,
&page_size);
}
// 启用延迟取消映射
if (iommu_capable(IOMMU_CAP_DEFERRED_FLUSH)) {
u32 deferred = 1;
iommu_domain_set_attr(domain,
DOMAIN_ATTR_DEFER_FLUSH,
&deferred);
}
return 0;
}
// IOMMU SVA(Shared Virtual Addressing)支持
#ifdef CONFIG_IOMMU_SVA
static int enable_iommu_sva(struct device *dev)
{
struct iommu_sva *handle;
int pasid;
// 绑定进程到设备
handle = iommu_sva_bind_device(dev, current->mm, NULL);
if (IS_ERR(handle))
return PTR_ERR(handle);
// 获取PASID
pasid = iommu_sva_get_pasid(handle);
if (pasid < 0) {
iommu_sva_unbind_device(handle);
return pasid;
}
// 配置设备使用PASID
pci_enable_pasid(dev, pasid);
// 保存句柄供后续使用
dev->iommu_sva_handle = handle;
return 0;
}
// SVA DMA映射
static dma_addr_t sva_dma_map(struct device *dev,
void *cpu_addr,
size_t size,
enum dma_data_direction dir)
{
struct iommu_sva *handle = dev->iommu_sva_handle;
dma_addr_t iova;
// 使用进程地址空间
iova = iommu_map(handle->domain,
(unsigned long)cpu_addr,
size,
IOMMU_READ | IOMMU_WRITE);
return iova;
}
#endif
6.1.2 IOMMU性能优化
c
// IOMMU TLB优化
struct iommu_tlb_optimization {
// 批处理TLB失效
struct iommu_batch_invalidate {
struct list_head list;
unsigned long start;
unsigned long end;
size_t size;
} batch;
// 预测性预取
struct {
unsigned long next_expected;
unsigned int prefetch_distance;
bool enabled;
} prefetch;
// TLB监控
struct {
unsigned long hits;
unsigned long misses;
unsigned long flushes;
unsigned long max_usage;
} stats;
};
// 智能TLB失效
static void smart_tlb_invalidate(struct iommu_domain *domain,
unsigned long iova,
size_t size)
{
static struct iommu_tlb_optimization opt;
static DEFINE_SPINLOCK(lock);
unsigned long flags;
spin_lock_irqsave(&lock, flags);
// 检查是否可以批处理
if (opt.batch.size + size <= IOMMU_BATCH_MAX_SIZE &&
opt.batch.end == iova) {
// 合并到现有批处理
opt.batch.end = iova + size;
opt.batch.size += size;
} else {
// 执行现有批处理
if (opt.batch.size > 0) {
iommu_unmap_fast(domain,
opt.batch.start,
opt.batch.size);
opt.batch.size = 0;
}
// 开始新批处理
opt.batch.start = iova;
opt.batch.end = iova + size;
opt.batch.size = size;
}
// 如果批处理足够大,立即执行
if (opt.batch.size >= IOMMU_BATCH_THRESHOLD) {
iommu_unmap_fast(domain,
opt.batch.start,
opt.batch.size);
opt.batch.size = 0;
}
spin_unlock_irqrestore(&lock, flags);
}
// IOMMU ATC(地址转换缓存)优化
static void optimize_iommu_atc(struct device *dev)
{
#ifdef CONFIG_X86
// 为IOMMU ATC设置缓存模式
if (cpu_feature_enabled(X86_FEATURE_IOMMU_ATC)) {
// 启用扩展模式
wrmsrl(MSR_IOMMU_ATC_CTRL,
IOMMU_ATC_ENABLE |
IOMMU_ATC_EXTENDED_MODE |
IOMMU_ATC_PREFETCH);
// 配置缓存大小
wrmsrl(MSR_IOMMU_ATC_SIZE,
IOMMU_ATC_ENTRIES_1024);
// 启用监控
wrmsrl(MSR_IOMMU_ATC_MONITOR,
IOMMU_ATC_MONITOR_HITS |
IOMMU_ATC_MONITOR_MISSES);
}
#endif
}
6.2 异构计算中的DMA
6.2.1 GPU DMA
c
// GPU DMA操作
struct gpu_dma_operation {
struct dma_fence *fence; // 同步栅栏
struct sg_table *sgt; // 分散-聚集表
enum dma_data_direction dir; // 传输方向
u64 gpu_addr; // GPU虚拟地址
dma_addr_t dma_addr; // DMA地址
size_t size; // 传输大小
// 同步对象
struct completion complete;
struct work_struct cleanup_work;
};
// GPU DMA传输
static int gpu_dma_transfer(struct gpu_device *gpu,
u64 gpu_addr,
dma_addr_t dma_addr,
size_t size,
enum dma_data_direction dir)
{
struct gpu_dma_operation *op;
struct dma_fence *fence;
int ret;
op = kzalloc(sizeof(*op), GFP_KERNEL);
if (!op)
return -ENOMEM;
init_completion(&op->complete);
op->gpu_addr = gpu_addr;
op->dma_addr = dma_addr;
op->size = size;
op->dir = dir;
// 创建DMA栅栏
fence = gpu_create_fence(gpu);
if (!fence) {
kfree(op);
return -ENOMEM;
}
op->fence = fence;
// 提交DMA操作到GPU
ret = gpu_submit_dma(gpu, op);
if (ret) {
dma_fence_put(fence);
kfree(op);
return ret;
}
// 等待完成或异步处理
if (!(gpu->flags & GPU_NONBLOCKING)) {
ret = dma_fence_wait_timeout(fence, false,
msecs_to_jiffies(5000));
if (ret == 0) {
// 超时
gpu_cancel_dma(gpu, op);
ret = -ETIMEDOUT;
} else if (ret > 0) {
ret = 0;
}
dma_fence_put(fence);
kfree(op);
} else {
// 异步处理
INIT_WORK(&op->cleanup_work, gpu_dma_cleanup_work);
schedule_work(&op->cleanup_work);
}
return ret;
}
// GPU与系统内存一致性
static int gpu_cache_coherency_init(struct gpu_device *gpu)
{
// 检查GPU缓存一致性能力
if (!gpu_has_feature(gpu, GPU_FEATURE_CACHE_COHERENT)) {
dev_warn(gpu->dev, "GPU lacks cache coherency support\n");
return -ENOTSUPP;
}
// 配置GPU缓存
gpu_write_reg(gpu, GPU_CACHE_CTRL,
GPU_CACHE_ENABLE |
GPU_CACHE_WRITE_BACK |
GPU_CACHE_ALLOCATE);
// 设置系统缓存一致性域
if (arch_has_coherent_dma()) {
gpu_write_reg(gpu, GPU_SYS_CACHE_CTRL,
GPU_SYS_CACHE_ENABLE |
GPU_SYS_CACHE_SNOOP);
}
// 启用IOMMU(如果可用)
if (iommu_present(&platform_bus_type)) {
int ret = iommu_attach_device(gpu->domain, gpu->dev);
if (ret)
return ret;
gpu->iommu_enabled = true;
}
return 0;
}
6.2.2 AI加速器DMA
c
// AI加速器DMA描述符
struct ai_dma_desc {
// 张量描述
struct {
u64 base_addr;
u32 stride[4]; // 各维度步长
u32 shape[4]; // 各维度大小
u32 dtype; // 数据类型
u32 format; // 数据格式(NCHW等)
} tensor;
// 操作描述
struct {
u32 op_type; // 操作类型
u32 op_flags; // 操作标志
u64 scalar; // 标量参数
u32 activation; // 激活函数
} operation;
// 同步信息
struct dma_fence *fence;
struct completion done;
// 链式操作
struct ai_dma_desc *next;
u32 chain_flags;
};
// AI加速器DMA引擎
static int ai_dma_submit(struct ai_device *ai,
struct ai_dma_desc *desc)
{
struct dma_chan *chan = ai->dma_chan;
struct dma_async_tx_descriptor *tx;
dma_cookie_t cookie;
int ret;
// 准备DMA传输
tx = dmaengine_prep_slave_sg(chan,
desc->tensor.sg,
desc->tensor.sg_count,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_PREP_FENCE);
if (!tx)
return -ENOMEM;
// 设置AI特定配置
ai_configure_dma(ai, desc);
// 设置完成回调
tx->callback = ai_dma_complete;
tx->callback_param = desc;
// 提交传输
cookie = dmaengine_submit(tx);
ret = dma_submit_error(cookie);
if (ret)
return ret;
// 记录操作
desc->cookie = cookie;
ai->pending_ops++;
// 触发执行
dma_async_issue_pending(chan);
return 0;
}
// AI DMA链式操作
static int ai_dma_chain_operations(struct ai_device *ai,
struct ai_dma_desc *chain)
{
struct ai_dma_desc *desc;
struct dma_async_tx_descriptor *prev = NULL;
dma_cookie_t cookie;
int ret;
for (desc = chain; desc; desc = desc->next) {
struct dma_async_tx_descriptor *tx;
// 准备DMA传输
tx = dmaengine_prep_slave_sg(ai->dma_chan,
desc->tensor.sg,
desc->tensor.sg_count,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_PREP_FENCE);
if (!tx) {
// 失败,取消已提交的操作
if (prev)
dmaengine_terminate_all(ai->dma_chan);
return -ENOMEM;
}
// 设置回调
tx->callback = ai_dma_complete;
tx->callback_param = desc;
// 设置依赖关系
if (prev)
tx->depends_on = prev;
// 提交传输
cookie = dmaengine_submit(tx);
ret = dma_submit_error(cookie);
if (ret) {
dmaengine_terminate_all(ai->dma_chan);
return ret;
}
prev = tx;
desc->cookie = cookie;
}
// 触发整个链的执行
dma_async_issue_pending(ai->dma_chan);
return 0;
}
6.3 未来趋势与技术展望
6.3.1 CXL(Compute Express Link)与DMA
c
// CXL DMA支持
#ifdef CONFIG_CXL_BUS
struct cxl_dma_context {
struct cxl_memdev *memdev;
struct dev_dax *dax_dev;
struct range *range;
u64 dpa_base; // 设备物理地址基址
u64 hpa_base; // 主机物理地址基址
size_t size;
// CXL特定
struct cxl_dport *dport;
u16 pasid;
bool mem_enabled;
};
// CXL内存的DMA映射
static int cxl_dma_map_memory(struct device *dev,
struct cxl_dma_context *ctx)
{
struct dev_dax *dax = ctx->dax_dev;
struct range *range = ctx->range;
int ret;
// 检查CXL内存支持
if (!cxl_mem_enabled(ctx->memdev)) {
dev_err(dev, "CXL memory not enabled\n");
return -ENODEV;
}
// 映射CXL内存范围
ret = devm_request_mem_region(dev, range->start,
range_len(range),
"cxl-dma");
if (ret)
return ret;
// 设置DMA掩码
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (ret)
return ret;
// 启用CXL内存用于DMA
ret = cxl_mem_enable_dma(ctx->memdev);
if (ret)
return ret;
ctx->mem_enabled = true;
return 0;
}
// CXL感知的DMA分配
static void *cxl_dma_alloc(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp)
{
struct cxl_dma_context *ctx = dev_get_drvdata(dev);
// 优先使用CXL内存(如果可用且合适)
if (ctx && ctx->mem_enabled && size >= CXL_DMA_THRESHOLD) {
return cxl_mem_dma_alloc(ctx->memdev, size,
dma_handle, gfp);
}
// 回退到常规DMA分配
return dma_alloc_coherent(dev, size, dma_handle, gfp);
}
#endif
6.3.2 智能网卡与DPU的DMA
c
// 智能网卡DMA优化
struct smartnic_dma_engine {
// 硬件卸载引擎
struct {
struct dma_chan *crypto; // 加密/解密
struct dma_chan *compress; // 压缩/解压
struct dma_chan *checksum; // 校验和
struct dma_chan *tls; // TLS加速
} offload;
// 零拷贝支持
struct {
bool enabled;
u32 max_fragments;
struct page_pool *page_pool;
} zero_copy;
// 流表加速
struct {
struct rhashtable *flow_table;
atomic_t flow_count;
} flow_accel;
};
// 智能网卡DMA流处理
static int smartnic_dma_process_flow(struct smartnic_dma_engine *engine,
struct sk_buff *skb)
{
struct smartnic_flow_key key;
struct smartnic_flow_entry *flow;
int ret;
// 提取流键
ret = extract_flow_key(skb, &key);
if (ret)
return ret;
// 查找流表
flow = rhashtable_lookup_fast(engine->flow_accel.flow_table,
&key, flow_rht_params);
if (flow) {
// 现有流:使用硬件加速
return process_flow_hw_accel(engine, skb, flow);
} else {
// 新流:软件处理并创建硬件流
return process_flow_sw_and_create(engine, skb, &key);
}
}
// DPU DMA虚拟化
struct dpu_dma_virtualization {
// 虚拟DMA引擎
struct dma_device vdma_dev;
struct list_head vchannels;
// 物理资源映射
struct {
struct dma_device *pdma_dev; // 物理DMA设备
struct dma_chan *pchannels; // 物理通道
u32 num_channels;
struct mutex lock;
} physical;
// 虚拟化功能
struct {
bool sriov_enabled;
u16 num_vfs;
struct dpu_dma_virtualization *vfs;
} virtualization;
};
// 虚拟DMA操作
static struct dma_async_tx_descriptor *
vdma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
dma_addr_t src, size_t len, unsigned long flags)
{
struct vdma_chan *vchan = to_vdma_chan(chan);
struct dpu_dma_virtualization *vdma = vchan->vdma;
struct dma_chan *pchan;
// 选择物理通道
mutex_lock(&vdma->physical.lock);
pchan = select_physical_channel(vdma);
mutex_unlock(&vdma->physical.lock);
if (!pchan)
return NULL;
// 准备物理DMA传输
return dmaengine_prep_dma_memcpy(pchan, dest, src, len, flags);
}
6.3.3 量子计算与DMA
c
// 量子计算DMA接口(概念性)
#ifdef CONFIG_QUANTUM_COMPUTING
struct quantum_dma_transfer {
// 量子态描述
struct {
u64 *amplitude_ptr; // 振幅指针
size_t num_amplitudes; // 振幅数量
u32 num_qubits; // 量子比特数
} quantum_state;
// 经典数据
struct {
void *data;
size_t size;
dma_addr_t dma_addr;
} classical_data;
// 量子操作
struct {
u32 gate_type; // 量子门类型
u32 *qubits; // 目标量子比特
u32 num_targets; // 目标数量
double *parameters; // 门参数
} quantum_op;
// 同步
struct quantum_fence *fence;
};
// 量子-经典DMA传输
static int quantum_classical_dma_transfer(struct quantum_device *qdev,
struct quantum_dma_transfer *xfer)
{
int ret;
// 1. 准备经典数据DMA
if (xfer->classical_data.size > 0) {
ret = dma_map_classical_data(qdev, xfer);
if (ret)
return ret;
}
// 2. 准备量子态传输
if (xfer->quantum_state.num_amplitudes > 0) {
ret = quantum_map_state(qdev, xfer);
if (ret) {
if (xfer->classical_data.size > 0)
dma_unmap_classical_data(qdev, xfer);
return ret;
}
}
// 3. 提交量子操作
ret = quantum_submit_operation(qdev, xfer);
if (ret) {
if (xfer->quantum_state.num_amplitudes > 0)
quantum_unmap_state(qdev, xfer);
if (xfer->classical_data.size > 0)
dma_unmap_classical_data(qdev, xfer);
return ret;
}
// 4. 等待完成
if (!(qdev->flags & QUANTUM_NONBLOCKING)) {
ret = quantum_fence_wait(xfer->fence,
msecs_to_jiffies(10000));
if (ret == 0)
ret = -ETIMEDOUT;
else if (ret > 0)
ret = 0;
}
return ret;
}
#endif
总结与展望
本文全面深入地探讨了嵌入式Linux系统中DMA技术的原理、应用、优化和调试方法。从基础的DMA概念开始,我们逐步深入到了Linux内核DMA子系统的架构,详细分析了DMA在各种外设中的应用,并提供了完整的性能优化策略和调试方法。
关键要点总结:
-
DMA基础:理解物理地址与总线地址的区别,掌握不同DMA传输模式的特点。
-
内核支持:Linux提供了完整的DMA抽象层,包括一致性映射、流式映射、分散-聚集支持等。
-
外设应用:以太网、USB、音频、摄像头等外设都有特定的DMA使用模式和优化方法。
-
性能优化:缓冲区对齐、缓存优化、中断合并、智能预取等策略可以显著提升DMA性能。
-
调试技术:内核调试工具、性能分析、泄漏检测、自动化测试等方法帮助诊断和解决DMA问题。
-
高级技术:IOMMU、异构计算DMA、CXL等新技术正在扩展DMA的能力边界。
未来发展趋势:
- 智能化:基于机器学习的DMA调度和预取优化
- 异构集成:CPU、GPU、AI加速器之间的无缝DMA传输
- 新互连技术:CXL、Chiplet等新技术对DMA架构的影响
- 安全增强:DMA访问控制、加密传输、安全隔离
- 量子计算:量子-经典混合计算中的DMA挑战
随着计算架构的不断演进,DMA技术将继续在系统性能优化中扮演关键角色。嵌入式开发人员需要不断更新知识,掌握新的DMA技术和优化方法,才能设计出高性能、低功耗的嵌入式系统。
希望本文能为读者提供有价值的参考和指导,帮助大家在嵌入式Linux开发中更好地理解和应用DMA技术。