linux-kernellinux-device-driver

Get PFN from DMA address (dma_addr_t)?


I would like to get the PFN associated with a memory block allocated with dma_alloc_coherent for use with a PCIe device as shown below:

unsigned long pfn;

buffer = dma_alloc_coherent(&pcie->dev, size, &bus_addr, GFP_KERNEL);

// Get PFN?
virt_to_phys(buffer) >> PAGE_SHIFT;

I'm aware that this is probably not the correct method, but it seems to work... I'm just looking for the right solution to translate the potential bus address (since I do not know if there is an IOMMU) to a PFN. Thanks in advance.

Note: There seems to be an ARM function in the kernel called dma_to_pfn, which seems to be exactly what I need, but for x86.


Solution

  • What you're doing is indeed wrong. From the man page for virt_to_phys():

    This function does not give bus mappings for DMA transfers. In almost all conceivable cases a device driver should not be using this function.

    The equivalent function for DMA addresses is dma_to_phys(), defined in include/linux/dma-direct.h as follows:

    phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr);
    

    Therefore you can do:

    dma_to_phys(&pcie->dev, bus_addr) >> PAGE_SHIFT;
    

    Notice that I am using the bus_addr returned by dma_alloc_coherent(), not buffer, since you obviously need to pass a DMA address (dma_addr_t) to this function, not a virtual address.

    There also seems to be a macro PHYS_PFN() defined in include/linux/pfn.h to get the PFN for a given physical address, if you prefer to use that:

    PHYS_PFN(dma_to_phys(&pcie->dev, bus_addr));