deepin-community/kernel DMA映射:设备内存访问管理
【免费下载链接】kernel deepin linux kernel 项目地址: https://gitcode.com/deepin-community/kernel
概述:DMA映射的核心价值
在现代计算机系统中,CPU(中央处理器)与外围设备之间的数据传输效率直接影响系统整体性能。传统的中断驱动I/O方式虽然简单,但在大数据量传输场景下会带来显著的性能开销。DMA(Direct Memory Access,直接内存访问)技术应运而生,它允许外围设备直接在内存和设备之间传输数据,无需CPU的持续干预。
DMA映射是Linux内核中管理设备与内存之间DMA传输的核心机制。它负责处理物理地址与设备地址之间的转换、缓存一致性、内存分配与释放等复杂任务,为驱动程序开发者提供统一的接口。
DMA映射架构解析
核心数据结构
DMA映射子系统基于以下几个关键数据结构:
// DMA方向定义
enum dma_data_direction {
DMA_BIDIRECTIONAL = 0, // 双向传输
DMA_TO_DEVICE = 1, // 到设备(写操作)
DMA_FROM_DEVICE = 2, // 从设备(读操作)
DMA_NONE = 3, // 无传输
};
// DMA属性标志
#define DMA_ATTR_WEAK_ORDERING (1UL << 1) // 弱排序
#define DMA_ATTR_WRITE_COMBINE (1UL << 2) // 写合并
#define DMA_ATTR_NO_KERNEL_MAPPING (1UL << 4) // 无内核映射
#define DMA_ATTR_SKIP_CPU_SYNC (1UL << 5) // 跳过CPU同步
#define DMA_ATTR_FORCE_CONTIGUOUS (1UL << 6) // 强制连续内存
映射操作流程
核心API详解
一致性DMA映射API
一致性映射(Coherent DMA Mapping)用于需要长期保持设备与CPU缓存一致性的场景:
// 分配一致性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);
// 带属性的分配函数
void *dma_alloc_attrs(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag,
unsigned long attrs);
流式DMA映射API
流式映射(Streaming DMA Mapping)用于单次数据传输场景:
// 映射单个缓冲区
dma_addr_t dma_map_single(struct device *dev, void *ptr,
size_t size, enum dma_data_direction dir);
// 取消映射
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);
// 同步操作
void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction dir);
void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction dir);
实际应用示例
网络设备驱动中的DMA使用
// 网络设备接收环初始化
static int init_rx_ring(struct net_device *dev)
{
struct my_private *priv = netdev_priv(dev);
int i;
// 分配DMA一致性内存用于接收描述符环
priv->rx_ring = dma_alloc_coherent(&dev->dev,
RX_RING_SIZE * sizeof(struct rx_desc),
&priv->rx_ring_dma, GFP_KERNEL);
if (!priv->rx_ring)
return -ENOMEM;
// 初始化接收缓冲区
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb = netdev_alloc_skb(dev, RX_BUF_SIZE);
if (!skb) {
// 错误处理
break;
}
// 映射接收缓冲区用于DMA
priv->rx_ring[i].buf_addr = dma_map_single(&dev->dev,
skb->data,
RX_BUF_SIZE,
DMA_FROM_DEVICE);
if (dma_mapping_error(&dev->dev, priv->rx_ring[i].buf_addr)) {
dev_kfree_skb(skb);
break;
}
priv->rx_skbuff[i] = skb;
}
return 0;
}
// 数据接收处理
static void handle_rx(struct net_device *dev)
{
struct my_private *priv = netdev_priv(dev);
int i;
for (i = 0; i < RX_RING_SIZE; i++) {
if (priv->rx_ring[i].status & DESC_OWN) {
// 数据还未就绪,继续等待
continue;
}
// 同步DMA缓冲区给CPU
dma_sync_single_for_cpu(&dev->dev,
priv->rx_ring[i].buf_addr,
RX_BUF_SIZE,
DMA_FROM_DEVICE);
// 处理接收到的数据包
process_rx_packet(dev, priv->rx_skbuff[i]);
// 重新映射缓冲区给设备使用
dma_sync_single_for_device(&dev->dev,
priv->rx_ring[i].buf_addr,
RX_BUF_SIZE,
DMA_FROM_DEVICE);
// 将描述符所有权交还给设备
priv->rx_ring[i].status = DESC_OWN;
}
}
存储设备驱动示例
// SCSI设备DMA传输
static int scsi_dma_transfer(struct scsi_device *sdev, struct scsi_cmnd *cmd)
{
struct device *dev = &sdev->sdev_gendev;
int dir = cmd->sc_data_direction;
int nents;
// 映射分散/聚集列表
nents = dma_map_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd), dir);
if (nents == 0)
return -ENOMEM;
// 设置DMA传输
if (setup_dma_transfer(dev, cmd, nents)) {
dma_unmap_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd), dir);
return -EIO;
}
// 启动DMA传输
start_dma_engine(dev);
return 0;
}
// 传输完成处理
static void scsi_dma_complete(struct scsi_device *sdev, struct scsi_cmnd *cmd)
{
struct device *dev = &sdev->sdev_gendev;
int dir = cmd->sc_data_direction;
// 取消映射
dma_unmap_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd), dir);
// 处理传输结果
process_transfer_result(cmd);
}
高级特性与最佳实践
DMA属性使用
// 使用DMA属性优化性能
void *optimized_dma_alloc(struct device *dev, size_t size)
{
unsigned long attrs = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_NO_KERNEL_MAPPING;
dma_addr_t dma_handle;
void *vaddr;
// 分配写合并内存,无内核映射
vaddr = dma_alloc_attrs(dev, size, &dma_handle, GFP_KERNEL, attrs);
if (!vaddr)
return NULL;
return vaddr;
}
// 强制连续内存分配
void *contiguous_dma_alloc(struct device *dev, size_t size)
{
unsigned long attrs = DMA_ATTR_FORCE_CONTIGUOUS;
dma_addr_t dma_handle;
return dma_alloc_attrs(dev, size, &dma_handle, GFP_KERNEL, attrs);
}
错误处理模式
// 安全的DMA映射模式
int safe_dma_mapping(struct device *dev, void *buffer, size_t size, int dir)
{
dma_addr_t dma_addr;
dma_addr = dma_map_single(dev, buffer, size, dir);
if (dma_mapping_error(dev, dma_addr)) {
dev_err(dev, "DMA mapping failed\n");
return -ENOMEM;
}
// 执行DMA操作
if (perform_dma_operation(dev, dma_addr, size)) {
dma_unmap_single(dev, dma_addr, size, dir);
return -EIO;
}
// 同步数据
if (dir == DMA_FROM_DEVICE)
dma_sync_single_for_cpu(dev, dma_addr, size, dir);
// 取消映射
dma_unmap_single(dev, dma_addr, size, dir);
return 0;
}
性能优化策略
内存对齐考虑
// 优化内存对齐以提高DMA性能
struct optimized_buffer {
void *cpu_addr;
dma_addr_t dma_addr;
size_t size;
};
int create_aligned_buffer(struct device *dev, struct optimized_buffer *buf,
size_t size, size_t alignment)
{
// 分配对齐的内存
buf->cpu_addr = dma_alloc_coherent(dev, size + alignment - 1,
&buf->dma_addr, GFP_KERNEL);
if (!buf->cpu_addr)
return -ENOMEM;
// 调整对齐
buf->size = size;
if ((buf->dma_addr & (alignment - 1)) != 0) {
size_t offset = alignment - (buf->dma_addr & (alignment - 1));
buf->cpu_addr += offset;
buf->dma_addr += offset;
}
return 0;
}
批量操作优化
// 批量DMA映射优化
int batch_dma_map(struct device *dev, struct dma_batch *batch)
{
int i, nents;
// 预计算所有需要的映射
for (i = 0; i < batch->count; i++) {
batch->entries[i].dma_addr = dma_map_single(dev,
batch->entries[i].cpu_addr,
batch->entries[i].size,
batch->dir);
if (dma_mapping_error(dev, batch->entries[i].dma_addr)) {
// 回滚已完成的映射
while (--i >= 0) {
dma_unmap_single(dev, batch->entries[i].dma_addr,
batch->entries[i].size, batch->dir);
}
return -ENOMEM;
}
}
return 0;
}
调试与故障排除
DMA调试支持
deepin-community/kernel提供了完善的DMA调试机制:
// 启用DMA调试
// 在内核配置中启用CONFIG_DMA_API_DEBUG
// 调试信息示例
void debug_dma_operations(struct device *dev)
{
// 内核会自动记录DMA操作信息
// 包括映射/取消映射次数、内存泄漏检测等
}
// 常见的调试信息
// DMA-API: device driver tries to map memory from stack [addr=xxxx]
// DMA-API: device driver frees DMA memory with different size
常见问题解决
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| DMA映射失败 | 内存不足或设备DMA掩码设置不当 | 检查dma_set_mask()设置 |
| 数据不一致 | 缓存同步问题 | 确保正确使用dma_sync_*函数 |
| 性能低下 | 内存未对齐或属性设置不当 | 优化内存对齐和使用DMA属性 |
| 内存泄漏 | 未配对的alloc/free调用 | 使用devres管理资源 |
总结
DMA映射是Linux内核中设备驱动开发的核心技术之一。deepin-community/kernel提供了完整且高效的DMA映射子系统,支持多种映射模式和优化策略。通过合理使用一致性映射和流式映射,结合适当的缓存同步和内存对齐策略,可以显著提升设备I/O性能。
在实际开发中,建议:
- 根据传输特性选择映射类型:长期共享使用一致性映射,单次传输使用流式映射
- 重视错误处理:始终检查dma_mapping_error()返回值
- 合理使用DMA属性:根据设备特性选择最优的内存属性
- 注意缓存一致性:在适当时机进行缓存同步操作
- 利用调试工具:启用DMA_API_DEBUG来检测潜在问题
通过掌握DMA映射技术,开发者能够编写出高性能、稳定可靠的设备驱动程序,充分发挥硬件设备的潜力。
【免费下载链接】kernel deepin linux kernel 项目地址: https://gitcode.com/deepin-community/kernel
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



