diff options
Diffstat (limited to 'arch/arm/xen/mm.c')
| -rw-r--r-- | arch/arm/xen/mm.c | 129 | 
1 files changed, 37 insertions, 92 deletions
diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index d33b77e9add3..2b2c208408bb 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0-only  #include <linux/cpu.h> -#include <linux/dma-mapping.h> +#include <linux/dma-noncoherent.h>  #include <linux/gfp.h>  #include <linux/highmem.h>  #include <linux/export.h> @@ -35,105 +35,56 @@ unsigned long xen_get_swiotlb_free_pages(unsigned int order)  	return __get_free_pages(flags, order);  } -enum dma_cache_op { -       DMA_UNMAP, -       DMA_MAP, -};  static bool hypercall_cflush = false; -/* functions called by SWIOTLB */ - -static void dma_cache_maint(dma_addr_t handle, unsigned long offset, -	size_t size, enum dma_data_direction dir, enum dma_cache_op op) +/* buffers in highmem or foreign pages cannot cross page boundaries */ +static void dma_cache_maint(dma_addr_t handle, size_t size, u32 op)  {  	struct gnttab_cache_flush cflush; -	unsigned long xen_pfn; -	size_t left = size; -	xen_pfn = (handle >> XEN_PAGE_SHIFT) + offset / XEN_PAGE_SIZE; -	offset %= XEN_PAGE_SIZE; +	cflush.a.dev_bus_addr = handle & XEN_PAGE_MASK; +	cflush.offset = xen_offset_in_page(handle); +	cflush.op = op;  	do { -		size_t len = left; -	 -		/* buffers in highmem or foreign pages cannot cross page -		 * boundaries */ -		if (len + offset > XEN_PAGE_SIZE) -			len = XEN_PAGE_SIZE - offset; - -		cflush.op = 0; -		cflush.a.dev_bus_addr = xen_pfn << XEN_PAGE_SHIFT; -		cflush.offset = offset; -		cflush.length = len; - -		if (op == DMA_UNMAP && dir != DMA_TO_DEVICE) -			cflush.op = GNTTAB_CACHE_INVAL; -		if (op == DMA_MAP) { -			if (dir == DMA_FROM_DEVICE) -				cflush.op = GNTTAB_CACHE_INVAL; -			else -				cflush.op = GNTTAB_CACHE_CLEAN; -		} -		if (cflush.op) -			HYPERVISOR_grant_table_op(GNTTABOP_cache_flush, &cflush, 1); +		if (size + cflush.offset > XEN_PAGE_SIZE) +			cflush.length = XEN_PAGE_SIZE - cflush.offset; +		else +			cflush.length = size; -		offset = 0; -		xen_pfn++; -		left -= len; -	} while (left); -} +		HYPERVISOR_grant_table_op(GNTTABOP_cache_flush, &cflush, 1); -static void __xen_dma_page_dev_to_cpu(struct device *hwdev, dma_addr_t handle, -		size_t size, enum dma_data_direction dir) -{ -	dma_cache_maint(handle & PAGE_MASK, handle & ~PAGE_MASK, size, dir, DMA_UNMAP); +		cflush.offset = 0; +		cflush.a.dev_bus_addr += cflush.length; +		size -= cflush.length; +	} while (size);  } -static void __xen_dma_page_cpu_to_dev(struct device *hwdev, dma_addr_t handle, -		size_t size, enum dma_data_direction dir) +/* + * Dom0 is mapped 1:1, and while the Linux page can span across multiple Xen + * pages, it is not possible for it to contain a mix of local and foreign Xen + * pages.  Calling pfn_valid on a foreign mfn will always return false, so if + * pfn_valid returns true the pages is local and we can use the native + * dma-direct functions, otherwise we call the Xen specific version. + */ +void xen_dma_sync_for_cpu(struct device *dev, dma_addr_t handle, +		phys_addr_t paddr, size_t size, enum dma_data_direction dir)  { -	dma_cache_maint(handle & PAGE_MASK, handle & ~PAGE_MASK, size, dir, DMA_MAP); +	if (pfn_valid(PFN_DOWN(handle))) +		arch_sync_dma_for_cpu(dev, paddr, size, dir); +	else if (dir != DMA_TO_DEVICE) +		dma_cache_maint(handle, size, GNTTAB_CACHE_INVAL);  } -void __xen_dma_map_page(struct device *hwdev, struct page *page, -	     dma_addr_t dev_addr, unsigned long offset, size_t size, -	     enum dma_data_direction dir, unsigned long attrs) +void xen_dma_sync_for_device(struct device *dev, dma_addr_t handle, +		phys_addr_t paddr, size_t size, enum dma_data_direction dir)  { -	if (is_device_dma_coherent(hwdev)) -		return; -	if (attrs & DMA_ATTR_SKIP_CPU_SYNC) -		return; - -	__xen_dma_page_cpu_to_dev(hwdev, dev_addr, size, dir); -} - -void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, -		size_t size, enum dma_data_direction dir, -		unsigned long attrs) - -{ -	if (is_device_dma_coherent(hwdev)) -		return; -	if (attrs & DMA_ATTR_SKIP_CPU_SYNC) -		return; - -	__xen_dma_page_dev_to_cpu(hwdev, handle, size, dir); -} - -void __xen_dma_sync_single_for_cpu(struct device *hwdev, -		dma_addr_t handle, size_t size, enum dma_data_direction dir) -{ -	if (is_device_dma_coherent(hwdev)) -		return; -	__xen_dma_page_dev_to_cpu(hwdev, handle, size, dir); -} - -void __xen_dma_sync_single_for_device(struct device *hwdev, -		dma_addr_t handle, size_t size, enum dma_data_direction dir) -{ -	if (is_device_dma_coherent(hwdev)) -		return; -	__xen_dma_page_cpu_to_dev(hwdev, handle, size, dir); +	if (pfn_valid(PFN_DOWN(handle))) +		arch_sync_dma_for_device(dev, paddr, size, dir); +	else if (dir == DMA_FROM_DEVICE) +		dma_cache_maint(handle, size, GNTTAB_CACHE_INVAL); +	else +		dma_cache_maint(handle, size, GNTTAB_CACHE_CLEAN);  }  bool xen_arch_need_swiotlb(struct device *dev, @@ -159,7 +110,7 @@ bool xen_arch_need_swiotlb(struct device *dev,  	 * memory and we are not able to flush the cache.  	 */  	return (!hypercall_cflush && (xen_pfn != bfn) && -		!is_device_dma_coherent(dev)); +		!dev_is_dma_coherent(dev));  }  int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order, @@ -173,16 +124,11 @@ int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,  	*dma_handle = pstart;  	return 0;  } -EXPORT_SYMBOL_GPL(xen_create_contiguous_region);  void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order)  {  	return;  } -EXPORT_SYMBOL_GPL(xen_destroy_contiguous_region); - -const struct dma_map_ops *xen_dma_ops; -EXPORT_SYMBOL(xen_dma_ops);  int __init xen_mm_init(void)  { @@ -190,7 +136,6 @@ int __init xen_mm_init(void)  	if (!xen_initial_domain())  		return 0;  	xen_swiotlb_init(1, false); -	xen_dma_ops = &xen_swiotlb_dma_ops;  	cflush.op = 0;  	cflush.a.dev_bus_addr = 0;  |