在什么情况下dma_alloc_coherent() 函数的 *dma_handle 返回物理地址?



在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);

最新更新