在linux中,函数dma_alloc_coherent()
有这样的形式(在include/linux/dma-mapping.h中声明)
static inline void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp)
返回的地址是分配的缓冲区的内核地址,dma_handle由内核用设备的DMA地址填充。文件说,在某些情况下,返回的dma_handle只是物理地址,但当有一个IOMMU连接到设备时,它是另一个虚拟地址,应该转换为物理地址。
我最近为一个应该与IOMMU一起使用的设备写了一个驱动程序(一个临时的,或者测试驱动程序),但是因为我们的硬件没有配备它,我把(用户空间虚拟地址)到(物理地址)转换在驱动程序中,这样设备就可以只使用"地址"。为是。传递给设备的数据结构包含一些"地址"。稍后,这个地址转换将被删除,取而代之的是IOMMU驱动程序将被添加,以便"一些虚拟的";";physical"地址转换由IOMMU完成。
我的问题是,在什么情况下dma_handle物理地址吗?在我的测试驱动程序中,我使用了像get_user_pages()
、kmap()
和virt_to_phys()
这样的函数,或者在某些情况下映射了巨大的页面,但是我想到,在某些情况下,如果值是物理地址,我可以只使用dma_alloc_coherent()
并使用dma_handle。(实际上,小缓冲区是由一些数据指向的,我认为我应该使用dma_pool_alloc()
,这是小缓冲区。)
这纯粹是我的阅读理解与一些代码浏览。看起来返回到您的dma_handle的任何内容都应该按原样提供给设备,并且它将是正确的。不要自己做任何地址翻译。
我正在阅读你指出的内核文档。
第一部分似乎表明kmalloc() + dma_map_single() == dma_alloc_coherent()
在VA = kmalloc()中,你得到了VA,当然内核知道它是PA。然后将VA传递给dma_map_single(),得到dma VA(如果没有IOMMU,则为dma PA),本页将其称为总线地址空间
我浏览了dma_alloc_coherent()的代码,发现了一些线索:// no IOMMU: this seems to be returning physical
if (dma_alloc_direct(dev, ops))
cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs);
// IOMMU or possibly some other dma_map_ops ??? not sure why we need an abstraction for DMA device
else if (ops->alloc)
cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
ops->alloc()在drivers/iommu/dma-iommu.c:iommu_dma_ops
中可以是iommu_dma_alloc()
继续沿着这条路径走下去,你会发现以下任何一个:
return iommu_dma_alloc_remap(dev, size, handle, gfp,
dma_pgprot(dev, PAGE_KERNEL, attrs), attrs);
*handle = __iommu_dma_map(dev, page_to_phys(page), size, ioprot,
dev->coherent_dma_mask);
回到dma_map_single()。
你也可以在这里找到类似的分支,直接vs ops (PA vs IOMMU):
if (dma_map_direct(dev, ops) ||
arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size))
addr = dma_direct_map_page(dev, page, offset, size, dir, attrs);
else
addr = ops->map_page(dev, page, offset, size, dir, attrs);
ops->map_page()有一种可能是iommu_dma_map_page()现在我们回到drivers/iommu/dma-iommu.c
最后给出了设备在总线地址空间中应该使用的地址
iova = __iommu_dma_map(dev, phys, size, prot, dma_mask);