swiotlb-xen: introduce phys_to_dma/dma_to_phys translations
With some devices physical addresses are different than dma addresses. To be able to deal with these cases, we need to call phys_to_dma on physical addresses (including machine addresses in Xen terminology) before returning them from xen_swiotlb_alloc_coherent and xen_swiotlb_map_page. We also need to convert dma addresses back to physical addresses using dma_to_phys in xen_swiotlb_free_coherent and xen_swiotlb_unmap_page if we want to do any operations on them. Call dma_to_phys in is_xen_swiotlb_buffer. Introduce xen_phys_to_dma and call phys_to_dma in its implementation. Introduce xen_dma_to_phys and call dma_to_phys in its implementation. Call xen_phys_to_dma/xen_dma_to_phys instead of xen_phys_to_bus/xen_bus_to_phys through swiotlb-xen.c. Everything is taken care of by these changes except for xen_swiotlb_alloc_coherent and xen_swiotlb_free_coherent, which need a few explicit phys_to_dma/dma_to_phys calls. Signed-off-by: Stefano Stabellini <stefano.stabellini@xilinx.com> Tested-by: Corey Minyard <cminyard@mvista.com> Tested-by: Roman Shaposhnik <roman@zededa.com> Reviewed-by: Juergen Gross <jgross@suse.com> Link: https://lore.kernel.org/r/20200710223427.6897-9-sstabellini@kernel.org Signed-off-by: Juergen Gross <jgross@suse.com>
This commit is contained in:
parent
e9aab7e4ff
commit
91ffe4ad53
|
@ -52,30 +52,39 @@ static unsigned long xen_io_tlb_nslabs;
|
|||
* Quick lookup value of the bus address of the IOTLB.
|
||||
*/
|
||||
|
||||
static inline dma_addr_t xen_phys_to_bus(struct device *dev, phys_addr_t paddr)
|
||||
static inline phys_addr_t xen_phys_to_bus(struct device *dev, phys_addr_t paddr)
|
||||
{
|
||||
unsigned long bfn = pfn_to_bfn(XEN_PFN_DOWN(paddr));
|
||||
dma_addr_t dma = (dma_addr_t)bfn << XEN_PAGE_SHIFT;
|
||||
phys_addr_t baddr = (phys_addr_t)bfn << XEN_PAGE_SHIFT;
|
||||
|
||||
dma |= paddr & ~XEN_PAGE_MASK;
|
||||
|
||||
return dma;
|
||||
baddr |= paddr & ~XEN_PAGE_MASK;
|
||||
return baddr;
|
||||
}
|
||||
|
||||
static inline phys_addr_t xen_bus_to_phys(struct device *dev, dma_addr_t baddr)
|
||||
static inline dma_addr_t xen_phys_to_dma(struct device *dev, phys_addr_t paddr)
|
||||
{
|
||||
return phys_to_dma(dev, xen_phys_to_bus(dev, paddr));
|
||||
}
|
||||
|
||||
static inline phys_addr_t xen_bus_to_phys(struct device *dev,
|
||||
phys_addr_t baddr)
|
||||
{
|
||||
unsigned long xen_pfn = bfn_to_pfn(XEN_PFN_DOWN(baddr));
|
||||
dma_addr_t dma = (dma_addr_t)xen_pfn << XEN_PAGE_SHIFT;
|
||||
phys_addr_t paddr = dma;
|
||||
|
||||
paddr |= baddr & ~XEN_PAGE_MASK;
|
||||
phys_addr_t paddr = (xen_pfn << XEN_PAGE_SHIFT) |
|
||||
(baddr & ~XEN_PAGE_MASK);
|
||||
|
||||
return paddr;
|
||||
}
|
||||
|
||||
static inline phys_addr_t xen_dma_to_phys(struct device *dev,
|
||||
dma_addr_t dma_addr)
|
||||
{
|
||||
return xen_bus_to_phys(dev, dma_to_phys(dev, dma_addr));
|
||||
}
|
||||
|
||||
static inline dma_addr_t xen_virt_to_bus(struct device *dev, void *address)
|
||||
{
|
||||
return xen_phys_to_bus(dev, virt_to_phys(address));
|
||||
return xen_phys_to_dma(dev, virt_to_phys(address));
|
||||
}
|
||||
|
||||
static inline int range_straddles_page_boundary(phys_addr_t p, size_t size)
|
||||
|
@ -94,7 +103,7 @@ static inline int range_straddles_page_boundary(phys_addr_t p, size_t size)
|
|||
|
||||
static int is_xen_swiotlb_buffer(struct device *dev, dma_addr_t dma_addr)
|
||||
{
|
||||
unsigned long bfn = XEN_PFN_DOWN(dma_addr);
|
||||
unsigned long bfn = XEN_PFN_DOWN(dma_to_phys(dev, dma_addr));
|
||||
unsigned long xen_pfn = bfn_to_local_pfn(bfn);
|
||||
phys_addr_t paddr = (phys_addr_t)xen_pfn << XEN_PAGE_SHIFT;
|
||||
|
||||
|
@ -299,12 +308,12 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
|
|||
if (hwdev && hwdev->coherent_dma_mask)
|
||||
dma_mask = hwdev->coherent_dma_mask;
|
||||
|
||||
/* At this point dma_handle is the physical address, next we are
|
||||
/* At this point dma_handle is the dma address, next we are
|
||||
* going to set it to the machine address.
|
||||
* Do not use virt_to_phys(ret) because on ARM it doesn't correspond
|
||||
* to *dma_handle. */
|
||||
phys = *dma_handle;
|
||||
dev_addr = xen_phys_to_bus(hwdev, phys);
|
||||
phys = dma_to_phys(hwdev, *dma_handle);
|
||||
dev_addr = xen_phys_to_dma(hwdev, phys);
|
||||
if (((dev_addr + size - 1 <= dma_mask)) &&
|
||||
!range_straddles_page_boundary(phys, size))
|
||||
*dma_handle = dev_addr;
|
||||
|
@ -314,6 +323,7 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
|
|||
xen_free_coherent_pages(hwdev, size, ret, (dma_addr_t)phys, attrs);
|
||||
return NULL;
|
||||
}
|
||||
*dma_handle = phys_to_dma(hwdev, *dma_handle);
|
||||
SetPageXenRemapped(virt_to_page(ret));
|
||||
}
|
||||
memset(ret, 0, size);
|
||||
|
@ -334,7 +344,7 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr,
|
|||
|
||||
/* do not use virt_to_phys because on ARM it doesn't return you the
|
||||
* physical address */
|
||||
phys = xen_bus_to_phys(hwdev, dev_addr);
|
||||
phys = xen_dma_to_phys(hwdev, dev_addr);
|
||||
|
||||
/* Convert the size to actually allocated. */
|
||||
size = 1UL << (order + XEN_PAGE_SHIFT);
|
||||
|
@ -349,7 +359,8 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr,
|
|||
TestClearPageXenRemapped(page))
|
||||
xen_destroy_contiguous_region(phys, order);
|
||||
|
||||
xen_free_coherent_pages(hwdev, size, vaddr, (dma_addr_t)phys, attrs);
|
||||
xen_free_coherent_pages(hwdev, size, vaddr, phys_to_dma(hwdev, phys),
|
||||
attrs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -365,7 +376,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
|
|||
unsigned long attrs)
|
||||
{
|
||||
phys_addr_t map, phys = page_to_phys(page) + offset;
|
||||
dma_addr_t dev_addr = xen_phys_to_bus(dev, phys);
|
||||
dma_addr_t dev_addr = xen_phys_to_dma(dev, phys);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
/*
|
||||
|
@ -390,7 +401,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
|
|||
return DMA_MAPPING_ERROR;
|
||||
|
||||
phys = map;
|
||||
dev_addr = xen_phys_to_bus(dev, map);
|
||||
dev_addr = xen_phys_to_dma(dev, map);
|
||||
|
||||
/*
|
||||
* Ensure that the address returned is DMA'ble
|
||||
|
@ -418,7 +429,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
|
|||
static void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
|
||||
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
phys_addr_t paddr = xen_bus_to_phys(hwdev, dev_addr);
|
||||
phys_addr_t paddr = xen_dma_to_phys(hwdev, dev_addr);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
|
@ -434,7 +445,7 @@ static void
|
|||
xen_swiotlb_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr,
|
||||
size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
phys_addr_t paddr = xen_bus_to_phys(dev, dma_addr);
|
||||
phys_addr_t paddr = xen_dma_to_phys(dev, dma_addr);
|
||||
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
xen_dma_sync_for_cpu(dev, dma_addr, paddr, size, dir);
|
||||
|
@ -447,7 +458,7 @@ static void
|
|||
xen_swiotlb_sync_single_for_device(struct device *dev, dma_addr_t dma_addr,
|
||||
size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
phys_addr_t paddr = xen_bus_to_phys(dev, dma_addr);
|
||||
phys_addr_t paddr = xen_dma_to_phys(dev, dma_addr);
|
||||
|
||||
if (is_xen_swiotlb_buffer(dev, dma_addr))
|
||||
swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_DEVICE);
|
||||
|
|
Loading…
Reference in New Issue
Block a user