dma_alloc_coherent() 和物理地址

dma_alloc_coherent() 和物理地址

DMA-API-HOWTO 文档说 dma_alloc_coherent() 返回两个值:可用于从 CPU 访问的虚拟地址和dma_handle传递给卡的虚拟地址。

虚拟地址和dma_handle映射到的物理地址难道不应该是同一个吗?我看到的不是这个。

是否预期dma_handle与其物理地址相同?

我使用的是启用了 IOMMU 的 x86_64 系统。操作系统是 Ubuntu 18.04,内核是 4.15.0-47-generic。

我的代码:

ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));

dmabuffp = dma_alloc_coherent (dev_ptr, (size_t)4*1024*1024, &dma_hndl, GFP_KERNEL | GFP_DMA);

printk(KERN_INFO "init: dma_hndl = 0x%llX\n", dma_hndl);<br />
printk(KERN_INFO "init: dmabuffp VA = 0x%llX\n", (uint64_t)dmabuffp);<br />
printk(KERN_INFO "init: dma_hndl VA = 0x%llX\n", (uint64_t)bus_to_virt(dma_hndl));

printk(KERN_INFO "init: dmabuffp PA = 0x%010llX\n", (uint64_t)virt_to_phys(dmabuffp));<br />
printk(KERN_INFO "init: dma_hndl PA = 0x%010llX\n",(uint64_t)virt_to_phys(bus_to_virt(dma_hndl)));

模块加载后dmesg显示如下:

init: dma_hndl = 0xFFC00000<br />
init: dmabuffp VA = 0xFFFF888E9B000000<br />
init: dma_hndl VA = 0xFFFF88873FC00000<br />
init: dmabuffp PA = 0x085B000000<br />
init: dma_hndl PA = 0x00FFC00000

设备可以使用 进行写入和读取dma_hndl,但其写入的值不在 *buffptr 处。

我遗漏了什么和/或做错了什么?

答案1

额外的测试表明dma_hndl确实映射到与 相同的物理地址dmabuffp。显然virt_to_phys(bus_to_virt(dma_hndl))无法按预期解析,而是循环的,返回原始dma_hndl值而不是 IOMMU 映射的物理地址。

答案2

dma_addr_t一个 I/O 虚拟地址(IOVA),它是设备用来访问 返回的缓冲区的地址dma_alloc_coherent

void * dma_alloc_coherent(struct device *dev, 
                          size_t size, 
                          dma_addr_t *dma_handle, 
                          gfp_t flag)

原因与因为 IOMMU 已启用dma_addr_t不一样。virt_to_phys(dmabuffp)

只有设备使用dma_addr_t进行 DMA 访问。处理器访问需要使用返回的void*或某种virt_to_phys()版本的 以允许用户空间通过 devmem 对其进行 mmap。

如果没有 IOMMU,则dma_addr_t只是缓冲区的virt_to_phy()/转换,因此可以直接用于处理器访问,但这是对 DMA API 的错误使用。virt_to_bus()void*

相关内容