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*