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

问题描述 投票:0回答:1

在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 地址填充。 document说,在某些情况下返回的 dma_handle 只是物理地址,但是当有 IOMMU 连接到设备时,它是另一个虚拟地址,应该转换为物理地址。

我最近为应该与 IOMMU 一起使用的设备编写了一个驱动程序(临时或测试驱动程序),但因为我们的硬件没有配备它,所以我将(用户空间虚拟地址)到(物理地址)的转换放在驱动程序,以便设备可以按原样使用“地址”。传递到设备的数据结构包含一些“地址”。稍后此地址转换将被删除,取而代之的是添加 IOMMU 驱动程序,以便由 IOMMU 完成“某些虚拟”到“物理”地址转换。

我的问题是,什么情况下dma_handle物理地址是?在我的测试驱动程序中,我使用了

get_user_pages()
kmap()
virt_to_phys()
等函数,或者在某些情况下映射大页面,但我想到我可以只使用
dma_alloc_coherent()
并使用 dma_handle 如果在某些情况下,该值是物理地址。 (实际上小缓冲区是由一些数据指向的,我想我应该使用
dma_pool_alloc()
,这是针对小缓冲区的。)

linux linux-device-driver dma
1个回答
0
投票

这纯粹是我的阅读理解和一些代码浏览。看起来返回到 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()。

您还可以在这里发现直接与操作(PA 与 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);
© www.soinside.com 2019 - 2024. All rights reserved.