能否将DeviceA的dma_alloc_coherent返回地址用于DeviceB的dma_map_single调用
多PCIe设备间DMA传输API使用规范说明
现有方案风险确认
你当前的实现仅能在x86_64平台正常工作属于架构特性带来的巧合,完全不符合Linux DMA API的平台无关设计规范,迁移到ARM/PPC平台后会出现随机DMA传输错误、内核panic等问题。
疑问解答
1. dma_map_single传入其他设备dma_alloc_coherent返回地址的合规性
- 结论:不合规
- 原因:
dma_map_single的入参要求是内核线性地址空间的内存,仅支持kmalloc、__get_free_pages等从常规内存域分配的地址dma_alloc_coherent返回的内核虚拟地址与分配时绑定的设备强相关,在非x86平台通常属于非缓存的专用映射段,不属于线性地址空间,传入dma_map_single会出现地址转换错误- 不同设备的DMA地址空间完全独立(尤其是开启IOMMU的场景),针对DeviceA分配的相干内存的DMA地址仅对DeviceA有效,直接给DeviceB使用会出现寻址失败
2. 相干内存是否需要调用dma_sync_*系列函数
- 结论:在符合API规范的使用场景下,
dma_alloc_coherent分配的内存确实不需要调用dma_sync_*做缓存同步,这类内存本身已经做了架构层面的缓存一致性保证 - 注意:该结论的前提是你通过合法API将内存映射给目标设备使用,当前你的非法映射场景下,即使内存是相干的也可能出现缓存一致性问题
平台无关的正确实现方案
推荐两种符合API规范的实现方式,可跨x86/ARM/PPC平台运行:
方案一:常规内存分配+双设备独立映射
适用于对内存相干性没有强制要求的场景,兼容性最好:
- 调用
kmalloc或者alloc_pages分配普通内核内存,不需要绑定任何设备 - 针对DeviceA调用
dma_map_single,映射方向设置为DMA_BIDIRECTIONAL或者DMA_FROM_DEVICE,待DeviceA完成内存填充后,调用dma_sync_single_for_device同步给DeviceB使用 - 针对DeviceB调用
dma_map_single,映射方向设置为DMA_BIDIRECTIONAL或者DMA_TO_DEVICE,执行DMA传输 - 传输完成后分别对两个设备调用
dma_unmap_single释放映射,再释放分配的内存
方案二:相干内存+SG表跨设备映射
如果你必须使用dma_alloc_coherent分配的相干内存,按照以下流程操作:
- 调用
dma_alloc_coherent为DeviceA分配内存,拿到内核虚拟地址和DeviceA侧的DMA地址 - 调用
dma_get_sgtable获取该相干内存对应的scatterlist表 - 针对DeviceB调用
dma_map_sg,传入上一步得到的scatterlist表,设置对应的DMA方向 - 传输完成后先对DeviceB调用
dma_unmap_sg释放映射,再调用dma_free_coherent释放原始内存
- 注意:使用该方案时不需要额外调用
dma_sync_*系列函数做缓存同步
额外注意事项
- 两个PCIe设备都需要提前配置正确的DMA寻址掩码,确保分配的内存地址在两个设备的寻址范围内
- 所有DMA映射操作都需要做返回值校验,避免映射失败导致非法地址访问
内容的提问来源于stack exchange,提问作者Vern
相关产品推荐
相关产品推荐

