From e9e87eb3f9180b2b0470409b24fb39b8a1941520 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 11 Nov 2014 11:14:45 +0000 Subject: xen/arm: remove handling of XENFEAT_grant_map_identity The feature has been removed from Xen. Also Linux cannot use it on ARM32 without CONFIG_ARM_LPAE. Signed-off-by: Stefano Stabellini Acked-by: Ian Campbell Reviewed-by: David Vrabel Reviewed-by: Catalin Marinas --- arch/arm/xen/enlighten.c | 5 --- arch/arm/xen/mm32.c | 85 +----------------------------------------------- 2 files changed, 1 insertion(+), 89 deletions(-) (limited to 'arch') diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index 0e15f01..c7ca936 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -261,11 +261,6 @@ static int __init xen_guest_init(void) xen_setup_features(); - if (!xen_feature(XENFEAT_grant_map_identity)) { - pr_warn("Please upgrade your Xen.\n" - "If your platform has any non-coherent DMA devices, they won't work properly.\n"); - } - if (xen_feature(XENFEAT_dom0)) xen_start_info->flags |= SIF_INITDOMAIN|SIF_PRIVILEGED; else diff --git a/arch/arm/xen/mm32.c b/arch/arm/xen/mm32.c index 3b99860..a5a93fc 100644 --- a/arch/arm/xen/mm32.c +++ b/arch/arm/xen/mm32.c @@ -5,70 +5,6 @@ #include -static DEFINE_PER_CPU(unsigned long, xen_mm32_scratch_virt); -static DEFINE_PER_CPU(pte_t *, xen_mm32_scratch_ptep); - -static int alloc_xen_mm32_scratch_page(int cpu) -{ - struct page *page; - unsigned long virt; - pmd_t *pmdp; - pte_t *ptep; - - if (per_cpu(xen_mm32_scratch_ptep, cpu) != NULL) - return 0; - - page = alloc_page(GFP_KERNEL); - if (page == NULL) { - pr_warn("Failed to allocate xen_mm32_scratch_page for cpu %d\n", cpu); - return -ENOMEM; - } - - virt = (unsigned long)__va(page_to_phys(page)); - pmdp = pmd_offset(pud_offset(pgd_offset_k(virt), virt), virt); - ptep = pte_offset_kernel(pmdp, virt); - - per_cpu(xen_mm32_scratch_virt, cpu) = virt; - per_cpu(xen_mm32_scratch_ptep, cpu) = ptep; - - return 0; -} - -static int xen_mm32_cpu_notify(struct notifier_block *self, - unsigned long action, void *hcpu) -{ - int cpu = (long)hcpu; - switch (action) { - case CPU_UP_PREPARE: - if (alloc_xen_mm32_scratch_page(cpu)) - return NOTIFY_BAD; - break; - default: - break; - } - return NOTIFY_OK; -} - -static struct notifier_block xen_mm32_cpu_notifier = { - .notifier_call = xen_mm32_cpu_notify, -}; - -static void* xen_mm32_remap_page(dma_addr_t handle) -{ - unsigned long virt = get_cpu_var(xen_mm32_scratch_virt); - pte_t *ptep = __get_cpu_var(xen_mm32_scratch_ptep); - - *ptep = pfn_pte(handle >> PAGE_SHIFT, PAGE_KERNEL); - local_flush_tlb_kernel_page(virt); - - return (void*)virt; -} - -static void xen_mm32_unmap(void *vaddr) -{ - put_cpu_var(xen_mm32_scratch_virt); -} - /* functions called by SWIOTLB */ @@ -88,13 +24,7 @@ static void dma_cache_maint(dma_addr_t handle, unsigned long offset, if (!pfn_valid(pfn)) { - /* Cannot map the page, we don't know its physical address. - * Return and hope for the best */ - if (!xen_feature(XENFEAT_grant_map_identity)) - return; - vaddr = xen_mm32_remap_page(handle) + offset; - op(vaddr, len, dir); - xen_mm32_unmap(vaddr - offset); + /* TODO: cache flush */ } else { struct page *page = pfn_to_page(pfn); @@ -181,22 +111,9 @@ void xen_dma_sync_single_for_device(struct device *hwdev, int __init xen_mm32_init(void) { - int cpu; - if (!xen_initial_domain()) return 0; - register_cpu_notifier(&xen_mm32_cpu_notifier); - get_online_cpus(); - for_each_online_cpu(cpu) { - if (alloc_xen_mm32_scratch_page(cpu)) { - put_online_cpus(); - unregister_cpu_notifier(&xen_mm32_cpu_notifier); - return -ENOMEM; - } - } - put_online_cpus(); - return 0; } arch_initcall(xen_mm32_init); -- cgit v1.1 From 35e0be88c426e8d2d6a4ed91d80ea25d73d127aa Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 11 Nov 2014 11:14:51 +0000 Subject: xen/arm: remove outer_*_range call Dom0 is not actually capable of issuing outer_inv_range or outer_clean_range calls. Signed-off-by: Stefano Stabellini Acked-by: Ian Campbell Reviewed-by: Catalin Marinas --- arch/arm/xen/mm32.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'arch') diff --git a/arch/arm/xen/mm32.c b/arch/arm/xen/mm32.c index a5a93fc..6153d61 100644 --- a/arch/arm/xen/mm32.c +++ b/arch/arm/xen/mm32.c @@ -61,9 +61,6 @@ static void __xen_dma_page_dev_to_cpu(struct device *hwdev, dma_addr_t handle, /* Cannot use __dma_page_dev_to_cpu because we don't have a * struct page for handle */ - if (dir != DMA_TO_DEVICE) - outer_inv_range(handle, handle + size); - dma_cache_maint(handle & PAGE_MASK, handle & ~PAGE_MASK, size, dir, dmac_unmap_area); } @@ -72,12 +69,6 @@ static void __xen_dma_page_cpu_to_dev(struct device *hwdev, dma_addr_t handle, { dma_cache_maint(handle & PAGE_MASK, handle & ~PAGE_MASK, size, dir, dmac_map_area); - - if (dir == DMA_FROM_DEVICE) { - outer_inv_range(handle, handle + size); - } else { - outer_clean_range(handle, handle + size); - } } void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, -- cgit v1.1 From 2f91fc331ad495a3e602f3d0e7fd6211b1d91204 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 11 Nov 2014 14:31:56 +0000 Subject: xen/arm: if(pfn_valid(pfn)) call native dma_ops Remove code duplication in mm32.c by calling the native dma_ops if the page is a local page (not a foreign page). Use a simple pfn_valid(pfn) check to figure out if the page is local, exploiting the fact that dom0 is mapped 1:1, therefore pfn_valid always returns false when called on a foreign mfn. Suggested-by: Catalin Marinas Signed-off-by: Stefano Stabellini Reviewed-by: Catalin Marinas --- arch/arm/include/asm/xen/page-coherent.h | 49 +++++++++++++++++++++++++++---- arch/arm/xen/mm32.c | 50 +++++++------------------------- 2 files changed, 54 insertions(+), 45 deletions(-) (limited to 'arch') diff --git a/arch/arm/include/asm/xen/page-coherent.h b/arch/arm/include/asm/xen/page-coherent.h index e8275ea..9cfd895 100644 --- a/arch/arm/include/asm/xen/page-coherent.h +++ b/arch/arm/include/asm/xen/page-coherent.h @@ -5,6 +5,15 @@ #include #include +void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs); +void __xen_dma_sync_single_for_cpu(struct device *hwdev, + dma_addr_t handle, size_t size, enum dma_data_direction dir); + +void __xen_dma_sync_single_for_device(struct device *hwdev, + dma_addr_t handle, size_t size, enum dma_data_direction dir); + static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size, dma_addr_t *dma_handle, gfp_t flags, struct dma_attrs *attrs) @@ -26,14 +35,42 @@ static inline void xen_dma_map_page(struct device *hwdev, struct page *page, __generic_dma_ops(hwdev)->map_page(hwdev, page, offset, size, dir, attrs); } -void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, +static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, size_t size, enum dma_data_direction dir, - struct dma_attrs *attrs); + struct dma_attrs *attrs) +{ + unsigned long pfn = PFN_DOWN(handle); + /* Dom0 is mapped 1:1, so calling pfn_valid on a foreign mfn will + * always return false. If the page is local we can safely call the + * native dma_ops function, otherwise we call the xen specific + * function. */ + if (pfn_valid(pfn)) { + if (__generic_dma_ops(hwdev)->unmap_page) + __generic_dma_ops(hwdev)->unmap_page(hwdev, handle, size, dir, attrs); + } else + __xen_dma_unmap_page(hwdev, handle, size, dir, attrs); +} -void xen_dma_sync_single_for_cpu(struct device *hwdev, - dma_addr_t handle, size_t size, enum dma_data_direction dir); +static inline void xen_dma_sync_single_for_cpu(struct device *hwdev, + dma_addr_t handle, size_t size, enum dma_data_direction dir) +{ + unsigned long pfn = PFN_DOWN(handle); + if (pfn_valid(pfn)) { + if (__generic_dma_ops(hwdev)->sync_single_for_cpu) + __generic_dma_ops(hwdev)->sync_single_for_cpu(hwdev, handle, size, dir); + } else + __xen_dma_sync_single_for_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); +static inline void xen_dma_sync_single_for_device(struct device *hwdev, + dma_addr_t handle, size_t size, enum dma_data_direction dir) +{ + unsigned long pfn = PFN_DOWN(handle); + if (pfn_valid(pfn)) { + if (__generic_dma_ops(hwdev)->sync_single_for_device) + __generic_dma_ops(hwdev)->sync_single_for_device(hwdev, handle, size, dir); + } else + __xen_dma_sync_single_for_device(hwdev, handle, size, dir); +} #endif /* _ASM_ARM_XEN_PAGE_COHERENT_H */ diff --git a/arch/arm/xen/mm32.c b/arch/arm/xen/mm32.c index 6153d61..5bb8391 100644 --- a/arch/arm/xen/mm32.c +++ b/arch/arm/xen/mm32.c @@ -4,13 +4,15 @@ #include #include - +enum dma_cache_op { + DMA_UNMAP, + DMA_MAP, +}; /* functions called by SWIOTLB */ static void dma_cache_maint(dma_addr_t handle, unsigned long offset, - size_t size, enum dma_data_direction dir, - void (*op)(const void *, size_t, int)) + size_t size, enum dma_data_direction dir, enum dma_cache_op op) { unsigned long pfn; size_t left = size; @@ -20,34 +22,8 @@ static void dma_cache_maint(dma_addr_t handle, unsigned long offset, do { size_t len = left; - void *vaddr; - if (!pfn_valid(pfn)) - { - /* TODO: cache flush */ - } else { - struct page *page = pfn_to_page(pfn); - - if (PageHighMem(page)) { - if (len + offset > PAGE_SIZE) - len = PAGE_SIZE - offset; - - if (cache_is_vipt_nonaliasing()) { - vaddr = kmap_atomic(page); - op(vaddr + offset, len, dir); - kunmap_atomic(vaddr); - } else { - vaddr = kmap_high_get(page); - if (vaddr) { - op(vaddr + offset, len, dir); - kunmap_high(page); - } - } - } else { - vaddr = page_address(page) + offset; - op(vaddr, len, dir); - } - } + /* TODO: cache flush */ offset = 0; pfn++; @@ -58,20 +34,16 @@ static void dma_cache_maint(dma_addr_t handle, unsigned long offset, static void __xen_dma_page_dev_to_cpu(struct device *hwdev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { - /* Cannot use __dma_page_dev_to_cpu because we don't have a - * struct page for handle */ - - dma_cache_maint(handle & PAGE_MASK, handle & ~PAGE_MASK, size, dir, dmac_unmap_area); + dma_cache_maint(handle & PAGE_MASK, handle & ~PAGE_MASK, size, dir, DMA_UNMAP); } static void __xen_dma_page_cpu_to_dev(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, dmac_map_area); + dma_cache_maint(handle & PAGE_MASK, handle & ~PAGE_MASK, size, dir, DMA_MAP); } -void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, +void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs) @@ -84,7 +56,7 @@ void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, __xen_dma_page_dev_to_cpu(hwdev, handle, size, dir); } -void xen_dma_sync_single_for_cpu(struct device *hwdev, +void __xen_dma_sync_single_for_cpu(struct device *hwdev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { if (!__generic_dma_ops(hwdev)->sync_single_for_cpu) @@ -92,7 +64,7 @@ void xen_dma_sync_single_for_cpu(struct device *hwdev, __xen_dma_page_dev_to_cpu(hwdev, handle, size, dir); } -void xen_dma_sync_single_for_device(struct device *hwdev, +void __xen_dma_sync_single_for_device(struct device *hwdev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { if (!__generic_dma_ops(hwdev)->sync_single_for_device) -- cgit v1.1 From de7ee503f279c1b454941802972bb9467e3e5749 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Thu, 20 Nov 2014 10:41:35 +0000 Subject: arm64: introduce is_device_dma_coherent Introduce a boolean flag and an accessor function to check whether a device is dma_coherent. Set the flag from set_arch_dma_coherent_ops. Signed-off-by: Stefano Stabellini Signed-off-by: Catalin Marinas Reviewed-by: Catalin Marinas --- arch/arm64/include/asm/device.h | 1 + arch/arm64/include/asm/dma-mapping.h | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'arch') diff --git a/arch/arm64/include/asm/device.h b/arch/arm64/include/asm/device.h index cf98b36..243ef25 100644 --- a/arch/arm64/include/asm/device.h +++ b/arch/arm64/include/asm/device.h @@ -21,6 +21,7 @@ struct dev_archdata { #ifdef CONFIG_IOMMU_API void *iommu; /* private IOMMU data */ #endif + bool dma_coherent; }; struct pdev_archdata { diff --git a/arch/arm64/include/asm/dma-mapping.h b/arch/arm64/include/asm/dma-mapping.h index adeae3f..d34189b 100644 --- a/arch/arm64/include/asm/dma-mapping.h +++ b/arch/arm64/include/asm/dma-mapping.h @@ -54,11 +54,18 @@ static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) static inline int set_arch_dma_coherent_ops(struct device *dev) { + dev->archdata.dma_coherent = true; set_dma_ops(dev, &coherent_swiotlb_dma_ops); return 0; } #define set_arch_dma_coherent_ops set_arch_dma_coherent_ops +/* do not use this function in a driver */ +static inline bool is_device_dma_coherent(struct device *dev) +{ + return dev->archdata.dma_coherent; +} + #include static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) -- cgit v1.1 From 3d5391ac6f5e3cb1ab01e23851140d3f6e9ab573 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Thu, 20 Nov 2014 10:41:40 +0000 Subject: arm: introduce is_device_dma_coherent Introduce a boolean flag and an accessor function to check whether a device is dma_coherent. Set the flag from set_arch_dma_coherent_ops. Signed-off-by: Stefano Stabellini Signed-off-by: Catalin Marinas Reviewed-by: Catalin Marinas Acked-by: Russell King --- arch/arm/include/asm/device.h | 1 + arch/arm/include/asm/dma-mapping.h | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'arch') diff --git a/arch/arm/include/asm/device.h b/arch/arm/include/asm/device.h index dc662fc..4111592 100644 --- a/arch/arm/include/asm/device.h +++ b/arch/arm/include/asm/device.h @@ -17,6 +17,7 @@ struct dev_archdata { #ifdef CONFIG_ARM_DMA_USE_IOMMU struct dma_iommu_mapping *mapping; #endif + bool dma_coherent; }; struct omap_device; diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 85738b2..e6e3446 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -123,11 +123,18 @@ static inline unsigned long dma_max_pfn(struct device *dev) static inline int set_arch_dma_coherent_ops(struct device *dev) { + dev->archdata.dma_coherent = true; set_dma_ops(dev, &arm_coherent_dma_ops); return 0; } #define set_arch_dma_coherent_ops(dev) set_arch_dma_coherent_ops(dev) +/* do not use this function in a driver */ +static inline bool is_device_dma_coherent(struct device *dev) +{ + return dev->archdata.dma_coherent; +} + static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) { unsigned int offset = paddr & ~PAGE_MASK; -- cgit v1.1 From 2e2a7817fdb6304f8c95c29dae0c93e2727240f4 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Thu, 20 Nov 2014 10:42:40 +0000 Subject: xen/arm: use is_device_dma_coherent Use is_device_dma_coherent to check whether we need to issue cache maintenance operations rather than checking on the existence of a particular dma_ops function for the device. This is correct because coherent devices don't need cache maintenance operations - arm_coherent_dma_ops does not set the hooks that we were previously checking for existance. Signed-off-by: Stefano Stabellini Reviewed-by: Catalin Marinas Acked-by: Ian Campbell --- arch/arm/xen/mm32.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/arm/xen/mm32.c b/arch/arm/xen/mm32.c index 5bb8391..3ce9dc1 100644 --- a/arch/arm/xen/mm32.c +++ b/arch/arm/xen/mm32.c @@ -48,7 +48,7 @@ void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, struct dma_attrs *attrs) { - if (!__generic_dma_ops(hwdev)->unmap_page) + if (is_device_dma_coherent(hwdev)) return; if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) return; @@ -59,7 +59,7 @@ void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, void __xen_dma_sync_single_for_cpu(struct device *hwdev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { - if (!__generic_dma_ops(hwdev)->sync_single_for_cpu) + if (is_device_dma_coherent(hwdev)) return; __xen_dma_page_dev_to_cpu(hwdev, handle, size, dir); } @@ -67,7 +67,7 @@ void __xen_dma_sync_single_for_cpu(struct device *hwdev, void __xen_dma_sync_single_for_device(struct device *hwdev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { - if (!__generic_dma_ops(hwdev)->sync_single_for_device) + if (is_device_dma_coherent(hwdev)) return; __xen_dma_page_cpu_to_dev(hwdev, handle, size, dir); } -- cgit v1.1 From a0f2dee0cd651efb5fac6a1d35b0a14460ebcdd4 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 21 Nov 2014 11:04:39 +0000 Subject: xen: add a dma_addr_t dev_addr argument to xen_dma_map_page dev_addr is the machine address of the page. The new parameter can be used by the ARM and ARM64 implementations of xen_dma_map_page to find out if the page is a local page (pfn == mfn) or a foreign page (pfn != mfn). dev_addr could be retrieved again from the physical address, using pfn_to_mfn, but it requires accessing an rbtree. Since we already have the dev_addr in our hands at the call site there is no need to get the mfn twice. Signed-off-by: Stefano Stabellini Reviewed-by: Catalin Marinas Acked-by: Konrad Rzeszutek Wilk --- arch/arm/include/asm/xen/page-coherent.h | 4 ++-- arch/arm64/include/asm/xen/page-coherent.h | 4 ++-- arch/x86/include/asm/xen/page-coherent.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'arch') diff --git a/arch/arm/include/asm/xen/page-coherent.h b/arch/arm/include/asm/xen/page-coherent.h index 9cfd895..a309f42 100644 --- a/arch/arm/include/asm/xen/page-coherent.h +++ b/arch/arm/include/asm/xen/page-coherent.h @@ -29,8 +29,8 @@ static inline void xen_free_coherent_pages(struct device *hwdev, size_t size, } static inline void xen_dma_map_page(struct device *hwdev, struct page *page, - unsigned long offset, size_t size, enum dma_data_direction dir, - struct dma_attrs *attrs) + dma_addr_t dev_addr, unsigned long offset, size_t size, + enum dma_data_direction dir, struct dma_attrs *attrs) { __generic_dma_ops(hwdev)->map_page(hwdev, page, offset, size, dir, attrs); } diff --git a/arch/arm64/include/asm/xen/page-coherent.h b/arch/arm64/include/asm/xen/page-coherent.h index dde3fc9..d7cd4c2 100644 --- a/arch/arm64/include/asm/xen/page-coherent.h +++ b/arch/arm64/include/asm/xen/page-coherent.h @@ -20,8 +20,8 @@ static inline void xen_free_coherent_pages(struct device *hwdev, size_t size, } static inline void xen_dma_map_page(struct device *hwdev, struct page *page, - unsigned long offset, size_t size, enum dma_data_direction dir, - struct dma_attrs *attrs) + dma_addr_t dev_addr, unsigned long offset, size_t size, + enum dma_data_direction dir, struct dma_attrs *attrs) { } diff --git a/arch/x86/include/asm/xen/page-coherent.h b/arch/x86/include/asm/xen/page-coherent.h index 7f02fe4..acd844c 100644 --- a/arch/x86/include/asm/xen/page-coherent.h +++ b/arch/x86/include/asm/xen/page-coherent.h @@ -22,8 +22,8 @@ static inline void xen_free_coherent_pages(struct device *hwdev, size_t size, } static inline void xen_dma_map_page(struct device *hwdev, struct page *page, - unsigned long offset, size_t size, enum dma_data_direction dir, - struct dma_attrs *attrs) { } + dma_addr_t dev_addr, unsigned long offset, size_t size, + enum dma_data_direction dir, struct dma_attrs *attrs) { } static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, size_t size, enum dma_data_direction dir, -- cgit v1.1 From 3567258d281b5b515d5165ed23851d9f84087e7d Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 21 Nov 2014 11:05:39 +0000 Subject: xen/arm: use hypercall to flush caches in map_page In xen_dma_map_page, if the page is a local page, call the native map_page dma_ops. If the page is foreign, call __xen_dma_map_page that issues any required cache maintenane operations via hypercall. The reason for doing this is that the native dma_ops map_page could allocate buffers than need to be freed. If the page is foreign we don't call the native unmap_page dma_ops function, resulting in a memory leak. Suggested-by: Catalin Marinas Signed-off-by: Stefano Stabellini Reviewed-by: Catalin Marinas --- arch/arm/include/asm/xen/page-coherent.h | 13 ++++++++++++- arch/arm/xen/mm32.c | 12 ++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/include/asm/xen/page-coherent.h b/arch/arm/include/asm/xen/page-coherent.h index a309f42..efd5624 100644 --- a/arch/arm/include/asm/xen/page-coherent.h +++ b/arch/arm/include/asm/xen/page-coherent.h @@ -5,6 +5,9 @@ #include #include +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, struct dma_attrs *attrs); void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs); @@ -32,7 +35,15 @@ static inline 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, struct dma_attrs *attrs) { - __generic_dma_ops(hwdev)->map_page(hwdev, page, offset, size, dir, attrs); + bool local = PFN_DOWN(dev_addr) == page_to_pfn(page); + /* Dom0 is mapped 1:1, so if pfn == mfn the page is local otherwise + * is a foreign page grant-mapped in dom0. If the page is local we + * can safely call the native dma_ops function, otherwise we call + * the xen specific function. */ + if (local) + __generic_dma_ops(hwdev)->map_page(hwdev, page, offset, size, dir, attrs); + else + __xen_dma_map_page(hwdev, page, dev_addr, offset, size, dir, attrs); } static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, diff --git a/arch/arm/xen/mm32.c b/arch/arm/xen/mm32.c index 3ce9dc1..c86919b 100644 --- a/arch/arm/xen/mm32.c +++ b/arch/arm/xen/mm32.c @@ -43,6 +43,18 @@ static void __xen_dma_page_cpu_to_dev(struct device *hwdev, dma_addr_t handle, dma_cache_maint(handle & PAGE_MASK, handle & ~PAGE_MASK, size, dir, DMA_MAP); } +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, struct dma_attrs *attrs) +{ + if (is_device_dma_coherent(hwdev)) + return; + if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) + 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, struct dma_attrs *attrs) -- cgit v1.1 From 5121872afe0e6470bc6b41637b258fa6e314be4f Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 21 Nov 2014 11:06:39 +0000 Subject: xen/arm/arm64: merge xen/mm32.c into xen/mm.c Merge xen/mm32.c into xen/mm.c. As a consequence the code gets compiled on arm64 too. Signed-off-by: Stefano Stabellini Reviewed-by: Catalin Marinas --- arch/arm/xen/Makefile | 2 +- arch/arm/xen/mm.c | 84 ++++++++++++++++++++++++++ arch/arm/xen/mm32.c | 94 ------------------------------ arch/arm64/include/asm/xen/page-coherent.h | 44 +------------- 4 files changed, 86 insertions(+), 138 deletions(-) delete mode 100644 arch/arm/xen/mm32.c (limited to 'arch') diff --git a/arch/arm/xen/Makefile b/arch/arm/xen/Makefile index 1f85bfe..1296952 100644 --- a/arch/arm/xen/Makefile +++ b/arch/arm/xen/Makefile @@ -1 +1 @@ -obj-y := enlighten.o hypercall.o grant-table.o p2m.o mm.o mm32.o +obj-y := enlighten.o hypercall.o grant-table.o p2m.o mm.o diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index b0e77de..ab700e1 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -1,6 +1,10 @@ +#include +#include #include #include +#include #include +#include #include #include #include @@ -16,6 +20,86 @@ #include #include +enum dma_cache_op { + DMA_UNMAP, + DMA_MAP, +}; + +/* 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) +{ + unsigned long pfn; + size_t left = size; + + pfn = (handle >> PAGE_SHIFT) + offset / PAGE_SIZE; + offset %= PAGE_SIZE; + + do { + size_t len = left; + + /* TODO: cache flush */ + + offset = 0; + pfn++; + left -= len; + } while (left); +} + +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); +} + +static void __xen_dma_page_cpu_to_dev(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_MAP); +} + +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, struct dma_attrs *attrs) +{ + if (is_device_dma_coherent(hwdev)) + return; + if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) + 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, + struct dma_attrs *attrs) + +{ + if (is_device_dma_coherent(hwdev)) + return; + if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) + 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); +} + int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order, unsigned int address_bits, dma_addr_t *dma_handle) diff --git a/arch/arm/xen/mm32.c b/arch/arm/xen/mm32.c deleted file mode 100644 index c86919b..0000000 --- a/arch/arm/xen/mm32.c +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include -#include -#include - -#include -enum dma_cache_op { - DMA_UNMAP, - DMA_MAP, -}; - -/* 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) -{ - unsigned long pfn; - size_t left = size; - - pfn = (handle >> PAGE_SHIFT) + offset / PAGE_SIZE; - offset %= PAGE_SIZE; - - do { - size_t len = left; - - /* TODO: cache flush */ - - offset = 0; - pfn++; - left -= len; - } while (left); -} - -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); -} - -static void __xen_dma_page_cpu_to_dev(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_MAP); -} - -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, struct dma_attrs *attrs) -{ - if (is_device_dma_coherent(hwdev)) - return; - if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) - 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, - struct dma_attrs *attrs) - -{ - if (is_device_dma_coherent(hwdev)) - return; - if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) - 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); -} - -int __init xen_mm32_init(void) -{ - if (!xen_initial_domain()) - return 0; - - return 0; -} -arch_initcall(xen_mm32_init); diff --git a/arch/arm64/include/asm/xen/page-coherent.h b/arch/arm64/include/asm/xen/page-coherent.h index d7cd4c2..2052102 100644 --- a/arch/arm64/include/asm/xen/page-coherent.h +++ b/arch/arm64/include/asm/xen/page-coherent.h @@ -1,43 +1 @@ -#ifndef _ASM_ARM64_XEN_PAGE_COHERENT_H -#define _ASM_ARM64_XEN_PAGE_COHERENT_H - -#include -#include -#include - -static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size, - dma_addr_t *dma_handle, gfp_t flags, - struct dma_attrs *attrs) -{ - return __generic_dma_ops(hwdev)->alloc(hwdev, size, dma_handle, flags, attrs); -} - -static inline void xen_free_coherent_pages(struct device *hwdev, size_t size, - void *cpu_addr, dma_addr_t dma_handle, - struct dma_attrs *attrs) -{ - __generic_dma_ops(hwdev)->free(hwdev, size, cpu_addr, dma_handle, attrs); -} - -static inline 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, struct dma_attrs *attrs) -{ -} - -static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, - size_t size, enum dma_data_direction dir, - struct dma_attrs *attrs) -{ -} - -static inline void xen_dma_sync_single_for_cpu(struct device *hwdev, - dma_addr_t handle, size_t size, enum dma_data_direction dir) -{ -} - -static inline void xen_dma_sync_single_for_device(struct device *hwdev, - dma_addr_t handle, size_t size, enum dma_data_direction dir) -{ -} -#endif /* _ASM_ARM64_XEN_PAGE_COHERENT_H */ +#include <../../arm/include/asm/xen/page-coherent.h> -- cgit v1.1 From a4dba130891271084344c12537731542ec77cb85 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 21 Nov 2014 11:07:39 +0000 Subject: xen/arm/arm64: introduce xen_arch_need_swiotlb Introduce an arch specific function to find out whether a particular dma mapping operation needs to bounce on the swiotlb buffer. On ARM and ARM64, if the page involved is a foreign page and the device is not coherent, we need to bounce because at unmap time we cannot execute any required cache maintenance operations (we don't know how to find the pfn from the mfn). No change of behaviour for x86. Signed-off-by: Stefano Stabellini Reviewed-by: David Vrabel Reviewed-by: Catalin Marinas Acked-by: Ian Campbell Acked-by: Konrad Rzeszutek Wilk --- arch/arm/include/asm/xen/page.h | 4 ++++ arch/arm/xen/mm.c | 7 +++++++ arch/x86/include/asm/xen/page.h | 7 +++++++ 3 files changed, 18 insertions(+) (limited to 'arch') diff --git a/arch/arm/include/asm/xen/page.h b/arch/arm/include/asm/xen/page.h index 135c24a..68c739b 100644 --- a/arch/arm/include/asm/xen/page.h +++ b/arch/arm/include/asm/xen/page.h @@ -107,4 +107,8 @@ static inline bool set_phys_to_machine(unsigned long pfn, unsigned long mfn) #define xen_remap(cookie, size) ioremap_cache((cookie), (size)) #define xen_unmap(cookie) iounmap((cookie)) +bool xen_arch_need_swiotlb(struct device *dev, + unsigned long pfn, + unsigned long mfn); + #endif /* _ASM_ARM_XEN_PAGE_H */ diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index ab700e1..28ebf3e 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -100,6 +100,13 @@ void __xen_dma_sync_single_for_device(struct device *hwdev, __xen_dma_page_cpu_to_dev(hwdev, handle, size, dir); } +bool xen_arch_need_swiotlb(struct device *dev, + unsigned long pfn, + unsigned long mfn) +{ + return ((pfn != mfn) && !is_device_dma_coherent(dev)); +} + int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order, unsigned int address_bits, dma_addr_t *dma_handle) diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h index c949923..f58ef6c 100644 --- a/arch/x86/include/asm/xen/page.h +++ b/arch/x86/include/asm/xen/page.h @@ -236,4 +236,11 @@ void make_lowmem_page_readwrite(void *vaddr); #define xen_remap(cookie, size) ioremap((cookie), (size)); #define xen_unmap(cookie) iounmap((cookie)) +static inline bool xen_arch_need_swiotlb(struct device *dev, + unsigned long pfn, + unsigned long mfn) +{ + return false; +} + #endif /* _ASM_X86_XEN_PAGE_H */ -- cgit v1.1 From da095a996090a412c3b6292c7a8680661dca157d Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 21 Nov 2014 11:08:39 +0000 Subject: xen/arm: introduce GNTTABOP_cache_flush Introduce support for new hypercall GNTTABOP_cache_flush. Use it to perform cache flashing on pages used for dma when necessary. If GNTTABOP_cache_flush is supported by the hypervisor, we don't need to bounce dma map operations that involve foreign grants and non-coherent devices. Signed-off-by: Stefano Stabellini Reviewed-by: Catalin Marinas Acked-by: Ian Campbell --- arch/arm/xen/mm.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index 28ebf3e..351b24a 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -24,12 +25,14 @@ 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) { + struct gnttab_cache_flush cflush; unsigned long pfn; size_t left = size; @@ -39,7 +42,26 @@ static void dma_cache_maint(dma_addr_t handle, unsigned long offset, do { size_t len = left; - /* TODO: cache flush */ + /* buffers in highmem or foreign pages cannot cross page + * boundaries */ + if (len + offset > PAGE_SIZE) + len = PAGE_SIZE - offset; + + cflush.op = 0; + cflush.a.dev_bus_addr = pfn << 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); offset = 0; pfn++; @@ -104,7 +126,7 @@ bool xen_arch_need_swiotlb(struct device *dev, unsigned long pfn, unsigned long mfn) { - return ((pfn != mfn) && !is_device_dma_coherent(dev)); + return (!hypercall_cflush && (pfn != mfn) && !is_device_dma_coherent(dev)); } int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order, @@ -147,10 +169,18 @@ static struct dma_map_ops xen_swiotlb_dma_ops = { int __init xen_mm_init(void) { + struct gnttab_cache_flush cflush; 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; + cflush.offset = 0; + cflush.length = 0; + if (HYPERVISOR_grant_table_op(GNTTABOP_cache_flush, &cflush, 1) != -ENOSYS) + hypercall_cflush = true; return 0; } arch_initcall(xen_mm_init); -- cgit v1.1 From 066d79e4e2a41fbd0da62bd994f9b77ef8fa040a Mon Sep 17 00:00:00 2001 From: Boris Ostrovsky Date: Tue, 2 Dec 2014 15:19:12 -0500 Subject: xen/pci: Defer initialization of MSI ops on HVM guests If the hardware supports APIC virtualization we may decide not to use pirqs and instead use APIC/x2APIC directly, meaning that we don't want to set x86_msi.setup_msi_irqs and x86_msi.teardown_msi_irq to Xen-specific routines. However, x2APIC is not set up by the time pci_xen_hvm_init() is called so we need to postpone setting these ops until later, when we know which APIC mode is used. (Note that currently x2APIC is never initialized on HVM guests. This may change in the future) Signed-off-by: Boris Ostrovsky Acked-by: Stefano Stabellini Signed-off-by: David Vrabel --- arch/x86/pci/xen.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index 093f5f4..1370716 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -431,6 +431,14 @@ int __init pci_xen_init(void) return 0; } +#ifdef CONFIG_PCI_MSI +void __init xen_msi_init(void) +{ + x86_msi.setup_msi_irqs = xen_hvm_setup_msi_irqs; + x86_msi.teardown_msi_irq = xen_teardown_msi_irq; +} +#endif + int __init pci_xen_hvm_init(void) { if (!xen_have_vector_callback || !xen_feature(XENFEAT_hvm_pirqs)) @@ -445,8 +453,11 @@ int __init pci_xen_hvm_init(void) #endif #ifdef CONFIG_PCI_MSI - x86_msi.setup_msi_irqs = xen_hvm_setup_msi_irqs; - x86_msi.teardown_msi_irq = xen_teardown_msi_irq; + /* + * We need to wait until after x2apic is initialized + * before we can set MSI IRQ ops. + */ + x86_platform.apic_post_init = xen_msi_init; #endif return 0; } -- cgit v1.1 From 14520c92cbd7619ae097a3087b140c992b5c270b Mon Sep 17 00:00:00 2001 From: Boris Ostrovsky Date: Tue, 2 Dec 2014 15:19:13 -0500 Subject: xen/pci: Use APIC directly when APIC virtualization hardware is available When hardware supports APIC/x2APIC virtualization we don't need to use pirqs for MSI handling and instead use APIC since most APIC accesses (MMIO or MSR) will now be processed without VMEXITs. As an example, netperf on the original code produces this profile (collected wih 'xentrace -e 0x0008ffff -T 5'): 342 cpu_change 260 CPUID 34638 HLT 64067 INJ_VIRQ 28374 INTR 82733 INTR_WINDOW 10 NPF 24337 TRAP 370610 vlapic_accept_pic_intr 307528 VMENTRY 307527 VMEXIT 140998 VMMCALL 127 wrap_buffer After applying this patch the same test shows 230 cpu_change 260 CPUID 36542 HLT 174 INJ_VIRQ 27250 INTR 222 INTR_WINDOW 20 NPF 24999 TRAP 381812 vlapic_accept_pic_intr 166480 VMENTRY 166479 VMEXIT 77208 VMMCALL 81 wrap_buffer ApacheBench results (ab -n 10000 -c 200) improve by about 10% Signed-off-by: Boris Ostrovsky Reviewed-by: Konrad Rzeszutek Wilk Reviewed-by: Andrew Cooper Signed-off-by: David Vrabel --- arch/x86/include/asm/xen/cpuid.h | 91 ++++++++++++++++++++++++++++++++++++++++ arch/x86/pci/xen.c | 16 +++++++ 2 files changed, 107 insertions(+) create mode 100644 arch/x86/include/asm/xen/cpuid.h (limited to 'arch') diff --git a/arch/x86/include/asm/xen/cpuid.h b/arch/x86/include/asm/xen/cpuid.h new file mode 100644 index 0000000..0d809e9 --- /dev/null +++ b/arch/x86/include/asm/xen/cpuid.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * arch-x86/cpuid.h + * + * CPUID interface to Xen. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2007 Citrix Systems, Inc. + * + * Authors: + * Keir Fraser + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_CPUID_H__ +#define __XEN_PUBLIC_ARCH_X86_CPUID_H__ + +/* + * For compatibility with other hypervisor interfaces, the Xen cpuid leaves + * can be found at the first otherwise unused 0x100 aligned boundary starting + * from 0x40000000. + * + * e.g If viridian extensions are enabled for an HVM domain, the Xen cpuid + * leaves will start at 0x40000100 + */ + +#define XEN_CPUID_FIRST_LEAF 0x40000000 +#define XEN_CPUID_LEAF(i) (XEN_CPUID_FIRST_LEAF + (i)) + +/* + * Leaf 1 (0x40000x00) + * EAX: Largest Xen-information leaf. All leaves up to an including @EAX + * are supported by the Xen host. + * EBX-EDX: "XenVMMXenVMM" signature, allowing positive identification + * of a Xen host. + */ +#define XEN_CPUID_SIGNATURE_EBX 0x566e6558 /* "XenV" */ +#define XEN_CPUID_SIGNATURE_ECX 0x65584d4d /* "MMXe" */ +#define XEN_CPUID_SIGNATURE_EDX 0x4d4d566e /* "nVMM" */ + +/* + * Leaf 2 (0x40000x01) + * EAX[31:16]: Xen major version. + * EAX[15: 0]: Xen minor version. + * EBX-EDX: Reserved (currently all zeroes). + */ + +/* + * Leaf 3 (0x40000x02) + * EAX: Number of hypercall transfer pages. This register is always guaranteed + * to specify one hypercall page. + * EBX: Base address of Xen-specific MSRs. + * ECX: Features 1. Unused bits are set to zero. + * EDX: Features 2. Unused bits are set to zero. + */ + +/* Does the host support MMU_PT_UPDATE_PRESERVE_AD for this guest? */ +#define _XEN_CPUID_FEAT1_MMU_PT_UPDATE_PRESERVE_AD 0 +#define XEN_CPUID_FEAT1_MMU_PT_UPDATE_PRESERVE_AD (1u<<0) + +/* + * Leaf 5 (0x40000x04) + * HVM-specific features + */ + +/* EAX Features */ +/* Virtualized APIC registers */ +#define XEN_HVM_CPUID_APIC_ACCESS_VIRT (1u << 0) +/* Virtualized x2APIC accesses */ +#define XEN_HVM_CPUID_X2APIC_VIRT (1u << 1) +/* Memory mapped from other domains has valid IOMMU entries */ +#define XEN_HVM_CPUID_IOMMU_MAPPINGS (1u << 2) + +#define XEN_CPUID_MAX_NUM_LEAVES 4 + +#endif /* __XEN_PUBLIC_ARCH_X86_CPUID_H__ */ diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index 1370716..37914ef 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include static int xen_pcifront_enable_irq(struct pci_dev *dev) @@ -434,6 +436,20 @@ int __init pci_xen_init(void) #ifdef CONFIG_PCI_MSI void __init xen_msi_init(void) { + if (!disable_apic) { + /* + * If hardware supports (x2)APIC virtualization (as indicated + * by hypervisor's leaf 4) then we don't need to use pirqs/ + * event channels for MSI handling and instead use regular + * APIC processing + */ + uint32_t eax = cpuid_eax(xen_cpuid_base() + 4); + + if (((eax & XEN_HVM_CPUID_X2APIC_VIRT) && x2apic_mode) || + ((eax & XEN_HVM_CPUID_APIC_ACCESS_VIRT) && cpu_has_apic)) + return; + } + x86_msi.setup_msi_irqs = xen_hvm_setup_msi_irqs; x86_msi.teardown_msi_irq = xen_teardown_msi_irq; } -- cgit v1.1