diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-27 12:18:05 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-27 12:18:05 -0500 |
| commit | c180f1b04be01d025aa1e6e53e1a8b6626bd90c4 (patch) | |
| tree | 77abc4c9d95a153aff653b470a642aa921c1fa7f | |
| parent | 6a2651b55bcf5b56177d4ccaeb52adeeadb142ea (diff) | |
| parent | 60d8cd572f655aac6107a2330dced004ad1fe3d7 (diff) | |
Merge tag 'dma-mapping-5.0-2' of git://git.infradead.org/users/hch/dma-mapping
Pull dma-mapping fix from Christoph Hellwig:
"Fix a xen-swiotlb regression on arm64"
* tag 'dma-mapping-5.0-2' of git://git.infradead.org/users/hch/dma-mapping:
arm64/xen: fix xen-swiotlb cache flushing
| -rw-r--r-- | arch/arm/include/asm/xen/page-coherent.h | 94 | ||||
| -rw-r--r-- | arch/arm64/include/asm/device.h | 3 | ||||
| -rw-r--r-- | arch/arm64/include/asm/xen/page-coherent.h | 76 | ||||
| -rw-r--r-- | arch/arm64/mm/dma-mapping.c | 4 | ||||
| -rw-r--r-- | drivers/xen/swiotlb-xen.c | 4 | ||||
| -rw-r--r-- | include/xen/arm/page-coherent.h | 97 |
6 files changed, 176 insertions, 102 deletions
diff --git a/arch/arm/include/asm/xen/page-coherent.h b/arch/arm/include/asm/xen/page-coherent.h index b3ef061d8b74..2c403e7c782d 100644 --- a/arch/arm/include/asm/xen/page-coherent.h +++ b/arch/arm/include/asm/xen/page-coherent.h | |||
| @@ -1 +1,95 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | #ifndef _ASM_ARM_XEN_PAGE_COHERENT_H | ||
| 3 | #define _ASM_ARM_XEN_PAGE_COHERENT_H | ||
| 4 | |||
| 5 | #include <linux/dma-mapping.h> | ||
| 6 | #include <asm/page.h> | ||
| 1 | #include <xen/arm/page-coherent.h> | 7 | #include <xen/arm/page-coherent.h> |
| 8 | |||
| 9 | static inline const struct dma_map_ops *xen_get_dma_ops(struct device *dev) | ||
| 10 | { | ||
| 11 | if (dev && dev->archdata.dev_dma_ops) | ||
| 12 | return dev->archdata.dev_dma_ops; | ||
| 13 | return get_arch_dma_ops(NULL); | ||
| 14 | } | ||
| 15 | |||
| 16 | static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size, | ||
| 17 | dma_addr_t *dma_handle, gfp_t flags, unsigned long attrs) | ||
| 18 | { | ||
| 19 | return xen_get_dma_ops(hwdev)->alloc(hwdev, size, dma_handle, flags, attrs); | ||
| 20 | } | ||
| 21 | |||
| 22 | static inline void xen_free_coherent_pages(struct device *hwdev, size_t size, | ||
| 23 | void *cpu_addr, dma_addr_t dma_handle, unsigned long attrs) | ||
| 24 | { | ||
| 25 | xen_get_dma_ops(hwdev)->free(hwdev, size, cpu_addr, dma_handle, attrs); | ||
| 26 | } | ||
| 27 | |||
| 28 | static inline void xen_dma_map_page(struct device *hwdev, struct page *page, | ||
| 29 | dma_addr_t dev_addr, unsigned long offset, size_t size, | ||
| 30 | enum dma_data_direction dir, unsigned long attrs) | ||
| 31 | { | ||
| 32 | unsigned long page_pfn = page_to_xen_pfn(page); | ||
| 33 | unsigned long dev_pfn = XEN_PFN_DOWN(dev_addr); | ||
| 34 | unsigned long compound_pages = | ||
| 35 | (1<<compound_order(page)) * XEN_PFN_PER_PAGE; | ||
| 36 | bool local = (page_pfn <= dev_pfn) && | ||
| 37 | (dev_pfn - page_pfn < compound_pages); | ||
| 38 | |||
| 39 | /* | ||
| 40 | * Dom0 is mapped 1:1, while the Linux page can span across | ||
| 41 | * multiple Xen pages, it's not possible for it to contain a | ||
| 42 | * mix of local and foreign Xen pages. So if the first xen_pfn | ||
| 43 | * == mfn the page is local otherwise it's a foreign page | ||
| 44 | * grant-mapped in dom0. If the page is local we can safely | ||
| 45 | * call the native dma_ops function, otherwise we call the xen | ||
| 46 | * specific function. | ||
| 47 | */ | ||
| 48 | if (local) | ||
| 49 | xen_get_dma_ops(hwdev)->map_page(hwdev, page, offset, size, dir, attrs); | ||
| 50 | else | ||
| 51 | __xen_dma_map_page(hwdev, page, dev_addr, offset, size, dir, attrs); | ||
| 52 | } | ||
| 53 | |||
| 54 | static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, | ||
| 55 | size_t size, enum dma_data_direction dir, unsigned long attrs) | ||
| 56 | { | ||
| 57 | unsigned long pfn = PFN_DOWN(handle); | ||
| 58 | /* | ||
| 59 | * Dom0 is mapped 1:1, while the Linux page can be spanned accross | ||
| 60 | * multiple Xen page, it's not possible to have a mix of local and | ||
| 61 | * foreign Xen page. Dom0 is mapped 1:1, so calling pfn_valid on a | ||
| 62 | * foreign mfn will always return false. If the page is local we can | ||
| 63 | * safely call the native dma_ops function, otherwise we call the xen | ||
| 64 | * specific function. | ||
| 65 | */ | ||
| 66 | if (pfn_valid(pfn)) { | ||
| 67 | if (xen_get_dma_ops(hwdev)->unmap_page) | ||
| 68 | xen_get_dma_ops(hwdev)->unmap_page(hwdev, handle, size, dir, attrs); | ||
| 69 | } else | ||
| 70 | __xen_dma_unmap_page(hwdev, handle, size, dir, attrs); | ||
| 71 | } | ||
| 72 | |||
| 73 | static inline void xen_dma_sync_single_for_cpu(struct device *hwdev, | ||
| 74 | dma_addr_t handle, size_t size, enum dma_data_direction dir) | ||
| 75 | { | ||
| 76 | unsigned long pfn = PFN_DOWN(handle); | ||
| 77 | if (pfn_valid(pfn)) { | ||
| 78 | if (xen_get_dma_ops(hwdev)->sync_single_for_cpu) | ||
| 79 | xen_get_dma_ops(hwdev)->sync_single_for_cpu(hwdev, handle, size, dir); | ||
| 80 | } else | ||
| 81 | __xen_dma_sync_single_for_cpu(hwdev, handle, size, dir); | ||
| 82 | } | ||
| 83 | |||
| 84 | static inline void xen_dma_sync_single_for_device(struct device *hwdev, | ||
| 85 | dma_addr_t handle, size_t size, enum dma_data_direction dir) | ||
| 86 | { | ||
| 87 | unsigned long pfn = PFN_DOWN(handle); | ||
| 88 | if (pfn_valid(pfn)) { | ||
| 89 | if (xen_get_dma_ops(hwdev)->sync_single_for_device) | ||
| 90 | xen_get_dma_ops(hwdev)->sync_single_for_device(hwdev, handle, size, dir); | ||
| 91 | } else | ||
| 92 | __xen_dma_sync_single_for_device(hwdev, handle, size, dir); | ||
| 93 | } | ||
| 94 | |||
| 95 | #endif /* _ASM_ARM_XEN_PAGE_COHERENT_H */ | ||
diff --git a/arch/arm64/include/asm/device.h b/arch/arm64/include/asm/device.h index 3dd3d664c5c5..4658c937e173 100644 --- a/arch/arm64/include/asm/device.h +++ b/arch/arm64/include/asm/device.h | |||
| @@ -20,9 +20,6 @@ struct dev_archdata { | |||
| 20 | #ifdef CONFIG_IOMMU_API | 20 | #ifdef CONFIG_IOMMU_API |
| 21 | void *iommu; /* private IOMMU data */ | 21 | void *iommu; /* private IOMMU data */ |
| 22 | #endif | 22 | #endif |
| 23 | #ifdef CONFIG_XEN | ||
| 24 | const struct dma_map_ops *dev_dma_ops; | ||
| 25 | #endif | ||
| 26 | }; | 23 | }; |
| 27 | 24 | ||
| 28 | struct pdev_archdata { | 25 | struct pdev_archdata { |
diff --git a/arch/arm64/include/asm/xen/page-coherent.h b/arch/arm64/include/asm/xen/page-coherent.h index b3ef061d8b74..d88e56b90b93 100644 --- a/arch/arm64/include/asm/xen/page-coherent.h +++ b/arch/arm64/include/asm/xen/page-coherent.h | |||
| @@ -1 +1,77 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | #ifndef _ASM_ARM64_XEN_PAGE_COHERENT_H | ||
| 3 | #define _ASM_ARM64_XEN_PAGE_COHERENT_H | ||
| 4 | |||
| 5 | #include <linux/dma-mapping.h> | ||
| 6 | #include <asm/page.h> | ||
| 1 | #include <xen/arm/page-coherent.h> | 7 | #include <xen/arm/page-coherent.h> |
| 8 | |||
| 9 | static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size, | ||
| 10 | dma_addr_t *dma_handle, gfp_t flags, unsigned long attrs) | ||
| 11 | { | ||
| 12 | return dma_direct_alloc(hwdev, size, dma_handle, flags, attrs); | ||
| 13 | } | ||
| 14 | |||
| 15 | static inline void xen_free_coherent_pages(struct device *hwdev, size_t size, | ||
| 16 | void *cpu_addr, dma_addr_t dma_handle, unsigned long attrs) | ||
| 17 | { | ||
| 18 | dma_direct_free(hwdev, size, cpu_addr, dma_handle, attrs); | ||
| 19 | } | ||
| 20 | |||
| 21 | static inline void xen_dma_sync_single_for_cpu(struct device *hwdev, | ||
| 22 | dma_addr_t handle, size_t size, enum dma_data_direction dir) | ||
| 23 | { | ||
| 24 | unsigned long pfn = PFN_DOWN(handle); | ||
| 25 | |||
| 26 | if (pfn_valid(pfn)) | ||
| 27 | dma_direct_sync_single_for_cpu(hwdev, handle, size, dir); | ||
| 28 | else | ||
| 29 | __xen_dma_sync_single_for_cpu(hwdev, handle, size, dir); | ||
| 30 | } | ||
| 31 | |||
| 32 | static inline void xen_dma_sync_single_for_device(struct device *hwdev, | ||
| 33 | dma_addr_t handle, size_t size, enum dma_data_direction dir) | ||
| 34 | { | ||
| 35 | unsigned long pfn = PFN_DOWN(handle); | ||
| 36 | if (pfn_valid(pfn)) | ||
| 37 | dma_direct_sync_single_for_device(hwdev, handle, size, dir); | ||
| 38 | else | ||
| 39 | __xen_dma_sync_single_for_device(hwdev, handle, size, dir); | ||
| 40 | } | ||
| 41 | |||
| 42 | static inline void xen_dma_map_page(struct device *hwdev, struct page *page, | ||
| 43 | dma_addr_t dev_addr, unsigned long offset, size_t size, | ||
| 44 | enum dma_data_direction dir, unsigned long attrs) | ||
| 45 | { | ||
| 46 | unsigned long page_pfn = page_to_xen_pfn(page); | ||
| 47 | unsigned long dev_pfn = XEN_PFN_DOWN(dev_addr); | ||
| 48 | unsigned long compound_pages = | ||
| 49 | (1<<compound_order(page)) * XEN_PFN_PER_PAGE; | ||
| 50 | bool local = (page_pfn <= dev_pfn) && | ||
| 51 | (dev_pfn - page_pfn < compound_pages); | ||
| 52 | |||
| 53 | if (local) | ||
| 54 | dma_direct_map_page(hwdev, page, offset, size, dir, attrs); | ||
| 55 | else | ||
| 56 | __xen_dma_map_page(hwdev, page, dev_addr, offset, size, dir, attrs); | ||
| 57 | } | ||
| 58 | |||
| 59 | static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, | ||
| 60 | size_t size, enum dma_data_direction dir, unsigned long attrs) | ||
| 61 | { | ||
| 62 | unsigned long pfn = PFN_DOWN(handle); | ||
| 63 | /* | ||
| 64 | * Dom0 is mapped 1:1, while the Linux page can be spanned accross | ||
| 65 | * multiple Xen page, it's not possible to have a mix of local and | ||
| 66 | * foreign Xen page. Dom0 is mapped 1:1, so calling pfn_valid on a | ||
| 67 | * foreign mfn will always return false. If the page is local we can | ||
| 68 | * safely call the native dma_ops function, otherwise we call the xen | ||
| 69 | * specific function. | ||
| 70 | */ | ||
| 71 | if (pfn_valid(pfn)) | ||
| 72 | dma_direct_unmap_page(hwdev, handle, size, dir, attrs); | ||
| 73 | else | ||
| 74 | __xen_dma_unmap_page(hwdev, handle, size, dir, attrs); | ||
| 75 | } | ||
| 76 | |||
| 77 | #endif /* _ASM_ARM64_XEN_PAGE_COHERENT_H */ | ||
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index fb0908456a1f..78c0a72f822c 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c | |||
| @@ -466,9 +466,7 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, | |||
| 466 | __iommu_setup_dma_ops(dev, dma_base, size, iommu); | 466 | __iommu_setup_dma_ops(dev, dma_base, size, iommu); |
| 467 | 467 | ||
| 468 | #ifdef CONFIG_XEN | 468 | #ifdef CONFIG_XEN |
| 469 | if (xen_initial_domain()) { | 469 | if (xen_initial_domain()) |
| 470 | dev->archdata.dev_dma_ops = dev->dma_ops; | ||
| 471 | dev->dma_ops = xen_dma_ops; | 470 | dev->dma_ops = xen_dma_ops; |
| 472 | } | ||
| 473 | #endif | 471 | #endif |
| 474 | } | 472 | } |
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 989cf872b98c..bb7888429be6 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c | |||
| @@ -645,7 +645,7 @@ xen_swiotlb_dma_mmap(struct device *dev, struct vm_area_struct *vma, | |||
| 645 | void *cpu_addr, dma_addr_t dma_addr, size_t size, | 645 | void *cpu_addr, dma_addr_t dma_addr, size_t size, |
| 646 | unsigned long attrs) | 646 | unsigned long attrs) |
| 647 | { | 647 | { |
| 648 | #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) | 648 | #ifdef CONFIG_ARM |
| 649 | if (xen_get_dma_ops(dev)->mmap) | 649 | if (xen_get_dma_ops(dev)->mmap) |
| 650 | return xen_get_dma_ops(dev)->mmap(dev, vma, cpu_addr, | 650 | return xen_get_dma_ops(dev)->mmap(dev, vma, cpu_addr, |
| 651 | dma_addr, size, attrs); | 651 | dma_addr, size, attrs); |
| @@ -662,7 +662,7 @@ xen_swiotlb_get_sgtable(struct device *dev, struct sg_table *sgt, | |||
| 662 | void *cpu_addr, dma_addr_t handle, size_t size, | 662 | void *cpu_addr, dma_addr_t handle, size_t size, |
| 663 | unsigned long attrs) | 663 | unsigned long attrs) |
| 664 | { | 664 | { |
| 665 | #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) | 665 | #ifdef CONFIG_ARM |
| 666 | if (xen_get_dma_ops(dev)->get_sgtable) { | 666 | if (xen_get_dma_ops(dev)->get_sgtable) { |
| 667 | #if 0 | 667 | #if 0 |
| 668 | /* | 668 | /* |
diff --git a/include/xen/arm/page-coherent.h b/include/xen/arm/page-coherent.h index 59a260712a56..2ca9164a79bf 100644 --- a/include/xen/arm/page-coherent.h +++ b/include/xen/arm/page-coherent.h | |||
| @@ -1,17 +1,6 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | #ifndef _ASM_ARM_XEN_PAGE_COHERENT_H | 2 | #ifndef _XEN_ARM_PAGE_COHERENT_H |
| 3 | #define _ASM_ARM_XEN_PAGE_COHERENT_H | 3 | #define _XEN_ARM_PAGE_COHERENT_H |
| 4 | |||
| 5 | #include <asm/page.h> | ||
| 6 | #include <asm/dma-mapping.h> | ||
| 7 | #include <linux/dma-mapping.h> | ||
| 8 | |||
| 9 | static inline const struct dma_map_ops *xen_get_dma_ops(struct device *dev) | ||
| 10 | { | ||
| 11 | if (dev && dev->archdata.dev_dma_ops) | ||
| 12 | return dev->archdata.dev_dma_ops; | ||
| 13 | return get_arch_dma_ops(NULL); | ||
| 14 | } | ||
| 15 | 4 | ||
| 16 | void __xen_dma_map_page(struct device *hwdev, struct page *page, | 5 | void __xen_dma_map_page(struct device *hwdev, struct page *page, |
| 17 | dma_addr_t dev_addr, unsigned long offset, size_t size, | 6 | dma_addr_t dev_addr, unsigned long offset, size_t size, |
| @@ -21,87 +10,7 @@ void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, | |||
| 21 | unsigned long attrs); | 10 | unsigned long attrs); |
| 22 | void __xen_dma_sync_single_for_cpu(struct device *hwdev, | 11 | void __xen_dma_sync_single_for_cpu(struct device *hwdev, |
| 23 | dma_addr_t handle, size_t size, enum dma_data_direction dir); | 12 | dma_addr_t handle, size_t size, enum dma_data_direction dir); |
| 24 | |||
| 25 | void __xen_dma_sync_single_for_device(struct device *hwdev, | 13 | void __xen_dma_sync_single_for_device(struct device *hwdev, |
| 26 | dma_addr_t handle, size_t size, enum dma_data_direction dir); | 14 | dma_addr_t handle, size_t size, enum dma_data_direction dir); |
| 27 | 15 | ||
| 28 | static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size, | 16 | #endif /* _XEN_ARM_PAGE_COHERENT_H */ |
| 29 | dma_addr_t *dma_handle, gfp_t flags, unsigned long attrs) | ||
| 30 | { | ||
| 31 | return xen_get_dma_ops(hwdev)->alloc(hwdev, size, dma_handle, flags, attrs); | ||
| 32 | } | ||
| 33 | |||
| 34 | static inline void xen_free_coherent_pages(struct device *hwdev, size_t size, | ||
| 35 | void *cpu_addr, dma_addr_t dma_handle, unsigned long attrs) | ||
| 36 | { | ||
| 37 | xen_get_dma_ops(hwdev)->free(hwdev, size, cpu_addr, dma_handle, attrs); | ||
| 38 | } | ||
| 39 | |||
| 40 | static inline void xen_dma_map_page(struct device *hwdev, struct page *page, | ||
| 41 | dma_addr_t dev_addr, unsigned long offset, size_t size, | ||
| 42 | enum dma_data_direction dir, unsigned long attrs) | ||
| 43 | { | ||
| 44 | unsigned long page_pfn = page_to_xen_pfn(page); | ||
| 45 | unsigned long dev_pfn = XEN_PFN_DOWN(dev_addr); | ||
| 46 | unsigned long compound_pages = | ||
| 47 | (1<<compound_order(page)) * XEN_PFN_PER_PAGE; | ||
| 48 | bool local = (page_pfn <= dev_pfn) && | ||
| 49 | (dev_pfn - page_pfn < compound_pages); | ||
| 50 | |||
| 51 | /* | ||
| 52 | * Dom0 is mapped 1:1, while the Linux page can span across | ||
| 53 | * multiple Xen pages, it's not possible for it to contain a | ||
| 54 | * mix of local and foreign Xen pages. So if the first xen_pfn | ||
| 55 | * == mfn the page is local otherwise it's a foreign page | ||
| 56 | * grant-mapped in dom0. If the page is local we can safely | ||
| 57 | * call the native dma_ops function, otherwise we call the xen | ||
| 58 | * specific function. | ||
| 59 | */ | ||
| 60 | if (local) | ||
| 61 | xen_get_dma_ops(hwdev)->map_page(hwdev, page, offset, size, dir, attrs); | ||
| 62 | else | ||
| 63 | __xen_dma_map_page(hwdev, page, dev_addr, offset, size, dir, attrs); | ||
| 64 | } | ||
| 65 | |||
| 66 | static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, | ||
| 67 | size_t size, enum dma_data_direction dir, unsigned long attrs) | ||
| 68 | { | ||
| 69 | unsigned long pfn = PFN_DOWN(handle); | ||
| 70 | /* | ||
| 71 | * Dom0 is mapped 1:1, while the Linux page can be spanned accross | ||
| 72 | * multiple Xen page, it's not possible to have a mix of local and | ||
| 73 | * foreign Xen page. Dom0 is mapped 1:1, so calling pfn_valid on a | ||
| 74 | * foreign mfn will always return false. If the page is local we can | ||
| 75 | * safely call the native dma_ops function, otherwise we call the xen | ||
| 76 | * specific function. | ||
| 77 | */ | ||
| 78 | if (pfn_valid(pfn)) { | ||
| 79 | if (xen_get_dma_ops(hwdev)->unmap_page) | ||
| 80 | xen_get_dma_ops(hwdev)->unmap_page(hwdev, handle, size, dir, attrs); | ||
| 81 | } else | ||
| 82 | __xen_dma_unmap_page(hwdev, handle, size, dir, attrs); | ||
| 83 | } | ||
| 84 | |||
| 85 | static inline void xen_dma_sync_single_for_cpu(struct device *hwdev, | ||
| 86 | dma_addr_t handle, size_t size, enum dma_data_direction dir) | ||
| 87 | { | ||
| 88 | unsigned long pfn = PFN_DOWN(handle); | ||
| 89 | if (pfn_valid(pfn)) { | ||
| 90 | if (xen_get_dma_ops(hwdev)->sync_single_for_cpu) | ||
| 91 | xen_get_dma_ops(hwdev)->sync_single_for_cpu(hwdev, handle, size, dir); | ||
| 92 | } else | ||
| 93 | __xen_dma_sync_single_for_cpu(hwdev, handle, size, dir); | ||
| 94 | } | ||
| 95 | |||
| 96 | static inline void xen_dma_sync_single_for_device(struct device *hwdev, | ||
| 97 | dma_addr_t handle, size_t size, enum dma_data_direction dir) | ||
| 98 | { | ||
| 99 | unsigned long pfn = PFN_DOWN(handle); | ||
| 100 | if (pfn_valid(pfn)) { | ||
| 101 | if (xen_get_dma_ops(hwdev)->sync_single_for_device) | ||
| 102 | xen_get_dma_ops(hwdev)->sync_single_for_device(hwdev, handle, size, dir); | ||
| 103 | } else | ||
| 104 | __xen_dma_sync_single_for_device(hwdev, handle, size, dir); | ||
| 105 | } | ||
| 106 | |||
| 107 | #endif /* _ASM_ARM_XEN_PAGE_COHERENT_H */ | ||
