dma_alloc_coherent 和 dma_map_single 的区别
dma_alloc_coherent
和 dma_map_single
的主要区别在于 内存分配方式、地址一致性 和 适用场景。
1. dma_alloc_coherent
特点:
- 分配并映射 一个 DMA 兼容的缓冲区。
- 返回的 CPU 地址和 DMA 地址 一致性(coherent),即 CPU 和设备对该区域的访问始终保持同步,不需要额外的缓存同步操作。
- 分配的内存通常来自 DMA 兼容区(如
dma_direct_alloc
),不会是高地址的不可访问区域。 - 适用于 设备会持续访问的缓冲区,例如环形缓冲区(ring buffer)、共享内存(shared buffer)。
使用方式:
void *cpu_addr;
dma_addr_t dma_handle;
cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
if (!cpu_addr) {
dev_err(dev, "Failed to allocate DMA buffer\n");
return -ENOMEM;
}
// 设备和 CPU 可以直接访问 cpu_addr,数据总是同步的
// 使用完成后释放
dma_free_coherent(dev, size, cpu_addr, dma_handle);
2. dma_map_single
特点:
- 仅映射 现有的 CPU 地址(如
kmalloc
分配的地址)。 - 可能需要调用
dma_sync_single_for_{cpu|device}
进行缓存同步,因为 CPU 和设备的视图可能不同步(非一致性)。 - 适用于 临时传输 的数据缓冲区,而不是长时间共享的内存。
- 需要 手动释放映射(
dma_unmap_single
)。
使用方式:
void *cpu_addr = kmalloc(size, GFP_KERNEL);
dma_addr_t dma_handle;
if (!cpu_addr)
return -ENOMEM;
// DMA 映射
dma_handle = dma_map_single(dev, cpu_addr, size, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_handle)) {
dev_err(dev, "DMA mapping failed\n");
kfree(cpu_addr);
return -EIO;
}
// 设备可以访问 dma_handle 指向的地址
// 传输完成后,解除映射
dma_unmap_single(dev, dma_handle, size, DMA_TO_DEVICE);
// 释放 CPU 端的缓冲区
kfree(cpu_addr);
3. 主要区别对比
dma_alloc_coherent |
dma_map_single |
|
---|---|---|
作用 | 分配并映射 DMA 兼容的内存 | 仅映射已有的 CPU 内存 |
缓存一致性 | 一致性内存(不需要 dma_sync ) |
可能是非一致性内存,需要 dma_sync |
适用场景 | 长时间存在的共享缓冲区 | 临时传输数据的缓冲区 |
释放方式 | dma_free_coherent |
dma_unmap_single + kfree |
何时用 dma_alloc_coherent
?
- 设备和 CPU 长期共享 该缓冲区,如 DMA ring buffer。
- 需要避免 CPU 和设备缓存不一致问题(无需
dma_sync
)。
何时用 dma_map_single
?
- 只进行 短时间的 DMA 传输,数据使用完即释放。
- 需要映射
kmalloc
或vmalloc
申请的内存用于 DMA。
总结:
- 短期传输:
kmalloc
+dma_map_single
+dma_unmap_single
- 长期共享:
dma_alloc_coherent
+dma_free_coherent