mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	dma-mapping: direct calls for dma-iommu
Directly call into dma-iommu just like we have been doing for dma-direct for a while. This avoids the indirect call overhead for IOMMU ops and removes the need to have DMA ops entirely for many common configurations. Signed-off-by: Leon Romanovsky <leonro@nvidia.com> Signed-off-by: Leon Romanovsky <leon@kernel.org> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
		
							parent
							
								
									f69e342eec
								
							
						
					
					
						commit
						b5c58b2fdc
					
				
					 10 changed files with 269 additions and 93 deletions
				
			
		|  | @ -11722,6 +11722,7 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git | ||||||
| F:	drivers/iommu/dma-iommu.c | F:	drivers/iommu/dma-iommu.c | ||||||
| F:	drivers/iommu/dma-iommu.h | F:	drivers/iommu/dma-iommu.h | ||||||
| F:	drivers/iommu/iova.c | F:	drivers/iommu/iova.c | ||||||
|  | F:	include/linux/iommu-dma.h | ||||||
| F:	include/linux/iova.h | F:	include/linux/iova.h | ||||||
| 
 | 
 | ||||||
| IOMMU SUBSYSTEM | IOMMU SUBSYSTEM | ||||||
|  |  | ||||||
|  | @ -151,7 +151,7 @@ config OF_IOMMU | ||||||
| # IOMMU-agnostic DMA-mapping layer | # IOMMU-agnostic DMA-mapping layer | ||||||
| config IOMMU_DMA | config IOMMU_DMA | ||||||
| 	def_bool ARM64 || X86 || S390 | 	def_bool ARM64 || X86 || S390 | ||||||
| 	select DMA_OPS | 	select DMA_OPS_HELPERS | ||||||
| 	select IOMMU_API | 	select IOMMU_API | ||||||
| 	select IOMMU_IOVA | 	select IOMMU_IOVA | ||||||
| 	select IRQ_MSI_IOMMU | 	select IRQ_MSI_IOMMU | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| #include <linux/gfp.h> | #include <linux/gfp.h> | ||||||
| #include <linux/huge_mm.h> | #include <linux/huge_mm.h> | ||||||
| #include <linux/iommu.h> | #include <linux/iommu.h> | ||||||
|  | #include <linux/iommu-dma.h> | ||||||
| #include <linux/iova.h> | #include <linux/iova.h> | ||||||
| #include <linux/irq.h> | #include <linux/irq.h> | ||||||
| #include <linux/list_sort.h> | #include <linux/list_sort.h> | ||||||
|  | @ -1037,9 +1038,8 @@ static void *iommu_dma_alloc_remap(struct device *dev, size_t size, | ||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct sg_table *iommu_dma_alloc_noncontiguous(struct device *dev, | struct sg_table *iommu_dma_alloc_noncontiguous(struct device *dev, size_t size, | ||||||
| 		size_t size, enum dma_data_direction dir, gfp_t gfp, | 	       enum dma_data_direction dir, gfp_t gfp, unsigned long attrs) | ||||||
| 		unsigned long attrs) |  | ||||||
| { | { | ||||||
| 	struct dma_sgt_handle *sh; | 	struct dma_sgt_handle *sh; | ||||||
| 
 | 
 | ||||||
|  | @ -1055,7 +1055,7 @@ static struct sg_table *iommu_dma_alloc_noncontiguous(struct device *dev, | ||||||
| 	return &sh->sgt; | 	return &sh->sgt; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void iommu_dma_free_noncontiguous(struct device *dev, size_t size, | void iommu_dma_free_noncontiguous(struct device *dev, size_t size, | ||||||
| 		struct sg_table *sgt, enum dma_data_direction dir) | 		struct sg_table *sgt, enum dma_data_direction dir) | ||||||
| { | { | ||||||
| 	struct dma_sgt_handle *sh = sgt_handle(sgt); | 	struct dma_sgt_handle *sh = sgt_handle(sgt); | ||||||
|  | @ -1066,8 +1066,8 @@ static void iommu_dma_free_noncontiguous(struct device *dev, size_t size, | ||||||
| 	kfree(sh); | 	kfree(sh); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void iommu_dma_sync_single_for_cpu(struct device *dev, | void iommu_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, | ||||||
| 		dma_addr_t dma_handle, size_t size, enum dma_data_direction dir) | 		size_t size, enum dma_data_direction dir) | ||||||
| { | { | ||||||
| 	phys_addr_t phys; | 	phys_addr_t phys; | ||||||
| 
 | 
 | ||||||
|  | @ -1081,8 +1081,8 @@ static void iommu_dma_sync_single_for_cpu(struct device *dev, | ||||||
| 	swiotlb_sync_single_for_cpu(dev, phys, size, dir); | 	swiotlb_sync_single_for_cpu(dev, phys, size, dir); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void iommu_dma_sync_single_for_device(struct device *dev, | void iommu_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, | ||||||
| 		dma_addr_t dma_handle, size_t size, enum dma_data_direction dir) | 		size_t size, enum dma_data_direction dir) | ||||||
| { | { | ||||||
| 	phys_addr_t phys; | 	phys_addr_t phys; | ||||||
| 
 | 
 | ||||||
|  | @ -1096,9 +1096,8 @@ static void iommu_dma_sync_single_for_device(struct device *dev, | ||||||
| 		arch_sync_dma_for_device(phys, size, dir); | 		arch_sync_dma_for_device(phys, size, dir); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void iommu_dma_sync_sg_for_cpu(struct device *dev, | void iommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl, | ||||||
| 		struct scatterlist *sgl, int nelems, | 		int nelems, enum dma_data_direction dir) | ||||||
| 		enum dma_data_direction dir) |  | ||||||
| { | { | ||||||
| 	struct scatterlist *sg; | 	struct scatterlist *sg; | ||||||
| 	int i; | 	int i; | ||||||
|  | @ -1112,9 +1111,8 @@ static void iommu_dma_sync_sg_for_cpu(struct device *dev, | ||||||
| 			arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir); | 			arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void iommu_dma_sync_sg_for_device(struct device *dev, | void iommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl, | ||||||
| 		struct scatterlist *sgl, int nelems, | 		int nelems, enum dma_data_direction dir) | ||||||
| 		enum dma_data_direction dir) |  | ||||||
| { | { | ||||||
| 	struct scatterlist *sg; | 	struct scatterlist *sg; | ||||||
| 	int i; | 	int i; | ||||||
|  | @ -1129,9 +1127,9 @@ static void iommu_dma_sync_sg_for_device(struct device *dev, | ||||||
| 			arch_sync_dma_for_device(sg_phys(sg), sg->length, dir); | 			arch_sync_dma_for_device(sg_phys(sg), sg->length, dir); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page, | dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page, | ||||||
| 		unsigned long offset, size_t size, enum dma_data_direction dir, | 	      unsigned long offset, size_t size, enum dma_data_direction dir, | ||||||
| 		unsigned long attrs) | 	      unsigned long attrs) | ||||||
| { | { | ||||||
| 	phys_addr_t phys = page_to_phys(page) + offset; | 	phys_addr_t phys = page_to_phys(page) + offset; | ||||||
| 	bool coherent = dev_is_dma_coherent(dev); | 	bool coherent = dev_is_dma_coherent(dev); | ||||||
|  | @ -1189,7 +1187,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page, | ||||||
| 	return iova; | 	return iova; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void iommu_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, | void iommu_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, | ||||||
| 		size_t size, enum dma_data_direction dir, unsigned long attrs) | 		size_t size, enum dma_data_direction dir, unsigned long attrs) | ||||||
| { | { | ||||||
| 	struct iommu_domain *domain = iommu_get_dma_domain(dev); | 	struct iommu_domain *domain = iommu_get_dma_domain(dev); | ||||||
|  | @ -1342,8 +1340,8 @@ static int iommu_dma_map_sg_swiotlb(struct device *dev, struct scatterlist *sg, | ||||||
|  * impedance-matching, to be able to hand off a suitably-aligned list, |  * impedance-matching, to be able to hand off a suitably-aligned list, | ||||||
|  * but still preserve the original offsets and sizes for the caller. |  * but still preserve the original offsets and sizes for the caller. | ||||||
|  */ |  */ | ||||||
| static int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, | int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||||||
| 		int nents, enum dma_data_direction dir, unsigned long attrs) | 		enum dma_data_direction dir, unsigned long attrs) | ||||||
| { | { | ||||||
| 	struct iommu_domain *domain = iommu_get_dma_domain(dev); | 	struct iommu_domain *domain = iommu_get_dma_domain(dev); | ||||||
| 	struct iommu_dma_cookie *cookie = domain->iova_cookie; | 	struct iommu_dma_cookie *cookie = domain->iova_cookie; | ||||||
|  | @ -1462,8 +1460,8 @@ static int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, | void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, | ||||||
| 		int nents, enum dma_data_direction dir, unsigned long attrs) | 		enum dma_data_direction dir, unsigned long attrs) | ||||||
| { | { | ||||||
| 	dma_addr_t end = 0, start; | 	dma_addr_t end = 0, start; | ||||||
| 	struct scatterlist *tmp; | 	struct scatterlist *tmp; | ||||||
|  | @ -1512,7 +1510,7 @@ static void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, | ||||||
| 		__iommu_dma_unmap(dev, start, end - start); | 		__iommu_dma_unmap(dev, start, end - start); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys, | dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys, | ||||||
| 		size_t size, enum dma_data_direction dir, unsigned long attrs) | 		size_t size, enum dma_data_direction dir, unsigned long attrs) | ||||||
| { | { | ||||||
| 	return __iommu_dma_map(dev, phys, size, | 	return __iommu_dma_map(dev, phys, size, | ||||||
|  | @ -1520,7 +1518,7 @@ static dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys, | ||||||
| 			dma_get_mask(dev)); | 			dma_get_mask(dev)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle, | void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle, | ||||||
| 		size_t size, enum dma_data_direction dir, unsigned long attrs) | 		size_t size, enum dma_data_direction dir, unsigned long attrs) | ||||||
| { | { | ||||||
| 	__iommu_dma_unmap(dev, handle, size); | 	__iommu_dma_unmap(dev, handle, size); | ||||||
|  | @ -1557,7 +1555,7 @@ static void __iommu_dma_free(struct device *dev, size_t size, void *cpu_addr) | ||||||
| 		dma_free_contiguous(dev, page, alloc_size); | 		dma_free_contiguous(dev, page, alloc_size); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void iommu_dma_free(struct device *dev, size_t size, void *cpu_addr, | void iommu_dma_free(struct device *dev, size_t size, void *cpu_addr, | ||||||
| 		dma_addr_t handle, unsigned long attrs) | 		dma_addr_t handle, unsigned long attrs) | ||||||
| { | { | ||||||
| 	__iommu_dma_unmap(dev, handle, size); | 	__iommu_dma_unmap(dev, handle, size); | ||||||
|  | @ -1601,8 +1599,8 @@ static void *iommu_dma_alloc_pages(struct device *dev, size_t size, | ||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void *iommu_dma_alloc(struct device *dev, size_t size, | void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, | ||||||
| 		dma_addr_t *handle, gfp_t gfp, unsigned long attrs) | 		gfp_t gfp, unsigned long attrs) | ||||||
| { | { | ||||||
| 	bool coherent = dev_is_dma_coherent(dev); | 	bool coherent = dev_is_dma_coherent(dev); | ||||||
| 	int ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs); | 	int ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs); | ||||||
|  | @ -1635,7 +1633,7 @@ static void *iommu_dma_alloc(struct device *dev, size_t size, | ||||||
| 	return cpu_addr; | 	return cpu_addr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma, | int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma, | ||||||
| 		void *cpu_addr, dma_addr_t dma_addr, size_t size, | 		void *cpu_addr, dma_addr_t dma_addr, size_t size, | ||||||
| 		unsigned long attrs) | 		unsigned long attrs) | ||||||
| { | { | ||||||
|  | @ -1666,7 +1664,7 @@ static int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma, | ||||||
| 			       vma->vm_page_prot); | 			       vma->vm_page_prot); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int iommu_dma_get_sgtable(struct device *dev, struct sg_table *sgt, | int iommu_dma_get_sgtable(struct device *dev, struct sg_table *sgt, | ||||||
| 		void *cpu_addr, dma_addr_t dma_addr, size_t size, | 		void *cpu_addr, dma_addr_t dma_addr, size_t size, | ||||||
| 		unsigned long attrs) | 		unsigned long attrs) | ||||||
| { | { | ||||||
|  | @ -1693,19 +1691,19 @@ static int iommu_dma_get_sgtable(struct device *dev, struct sg_table *sgt, | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static unsigned long iommu_dma_get_merge_boundary(struct device *dev) | unsigned long iommu_dma_get_merge_boundary(struct device *dev) | ||||||
| { | { | ||||||
| 	struct iommu_domain *domain = iommu_get_dma_domain(dev); | 	struct iommu_domain *domain = iommu_get_dma_domain(dev); | ||||||
| 
 | 
 | ||||||
| 	return (1UL << __ffs(domain->pgsize_bitmap)) - 1; | 	return (1UL << __ffs(domain->pgsize_bitmap)) - 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static size_t iommu_dma_opt_mapping_size(void) | size_t iommu_dma_opt_mapping_size(void) | ||||||
| { | { | ||||||
| 	return iova_rcache_range(); | 	return iova_rcache_range(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static size_t iommu_dma_max_mapping_size(struct device *dev) | size_t iommu_dma_max_mapping_size(struct device *dev) | ||||||
| { | { | ||||||
| 	if (dev_is_untrusted(dev)) | 	if (dev_is_untrusted(dev)) | ||||||
| 		return swiotlb_max_mapping_size(dev); | 		return swiotlb_max_mapping_size(dev); | ||||||
|  | @ -1713,32 +1711,6 @@ static size_t iommu_dma_max_mapping_size(struct device *dev) | ||||||
| 	return SIZE_MAX; | 	return SIZE_MAX; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct dma_map_ops iommu_dma_ops = { |  | ||||||
| 	.flags			= DMA_F_PCI_P2PDMA_SUPPORTED | |  | ||||||
| 				  DMA_F_CAN_SKIP_SYNC, |  | ||||||
| 	.alloc			= iommu_dma_alloc, |  | ||||||
| 	.free			= iommu_dma_free, |  | ||||||
| 	.alloc_pages_op		= dma_common_alloc_pages, |  | ||||||
| 	.free_pages		= dma_common_free_pages, |  | ||||||
| 	.alloc_noncontiguous	= iommu_dma_alloc_noncontiguous, |  | ||||||
| 	.free_noncontiguous	= iommu_dma_free_noncontiguous, |  | ||||||
| 	.mmap			= iommu_dma_mmap, |  | ||||||
| 	.get_sgtable		= iommu_dma_get_sgtable, |  | ||||||
| 	.map_page		= iommu_dma_map_page, |  | ||||||
| 	.unmap_page		= iommu_dma_unmap_page, |  | ||||||
| 	.map_sg			= iommu_dma_map_sg, |  | ||||||
| 	.unmap_sg		= iommu_dma_unmap_sg, |  | ||||||
| 	.sync_single_for_cpu	= iommu_dma_sync_single_for_cpu, |  | ||||||
| 	.sync_single_for_device	= iommu_dma_sync_single_for_device, |  | ||||||
| 	.sync_sg_for_cpu	= iommu_dma_sync_sg_for_cpu, |  | ||||||
| 	.sync_sg_for_device	= iommu_dma_sync_sg_for_device, |  | ||||||
| 	.map_resource		= iommu_dma_map_resource, |  | ||||||
| 	.unmap_resource		= iommu_dma_unmap_resource, |  | ||||||
| 	.get_merge_boundary	= iommu_dma_get_merge_boundary, |  | ||||||
| 	.opt_mapping_size	= iommu_dma_opt_mapping_size, |  | ||||||
| 	.max_mapping_size       = iommu_dma_max_mapping_size, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void iommu_setup_dma_ops(struct device *dev) | void iommu_setup_dma_ops(struct device *dev) | ||||||
| { | { | ||||||
| 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev); | 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev); | ||||||
|  | @ -1746,19 +1718,15 @@ void iommu_setup_dma_ops(struct device *dev) | ||||||
| 	if (dev_is_pci(dev)) | 	if (dev_is_pci(dev)) | ||||||
| 		dev->iommu->pci_32bit_workaround = !iommu_dma_forcedac; | 		dev->iommu->pci_32bit_workaround = !iommu_dma_forcedac; | ||||||
| 
 | 
 | ||||||
| 	if (iommu_is_dma_domain(domain)) { | 	dev->dma_iommu = iommu_is_dma_domain(domain); | ||||||
| 		if (iommu_dma_init_domain(domain, dev)) | 	if (dev->dma_iommu && iommu_dma_init_domain(domain, dev)) | ||||||
| 			goto out_err; | 		goto out_err; | ||||||
| 		dev->dma_ops = &iommu_dma_ops; |  | ||||||
| 	} else if (dev->dma_ops == &iommu_dma_ops) { |  | ||||||
| 		/* Clean up if we've switched *from* a DMA domain */ |  | ||||||
| 		dev->dma_ops = NULL; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return; | 	return; | ||||||
| out_err: | out_err: | ||||||
| 	 pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n", | 	pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n", | ||||||
| 		 dev_name(dev)); | 		dev_name(dev)); | ||||||
|  | 	dev->dma_iommu = false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev, | static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev, | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ config DMAR_DEBUG | ||||||
| config INTEL_IOMMU | config INTEL_IOMMU | ||||||
| 	bool "Support for Intel IOMMU using DMA Remapping Devices" | 	bool "Support for Intel IOMMU using DMA Remapping Devices" | ||||||
| 	depends on PCI_MSI && ACPI && X86 | 	depends on PCI_MSI && ACPI && X86 | ||||||
| 	select DMA_OPS |  | ||||||
| 	select IOMMU_API | 	select IOMMU_API | ||||||
| 	select IOMMU_IOVA | 	select IOMMU_IOVA | ||||||
| 	select IOMMUFD_DRIVER if IOMMUFD | 	select IOMMUFD_DRIVER if IOMMUFD | ||||||
|  |  | ||||||
|  | @ -707,6 +707,8 @@ struct device_physical_location { | ||||||
|  *		for dma allocations.  This flag is managed by the dma ops |  *		for dma allocations.  This flag is managed by the dma ops | ||||||
|  *		instance from ->dma_supported. |  *		instance from ->dma_supported. | ||||||
|  * @dma_skip_sync: DMA sync operations can be skipped for coherent buffers. |  * @dma_skip_sync: DMA sync operations can be skipped for coherent buffers. | ||||||
|  |  * @dma_iommu: Device is using default IOMMU implementation for DMA and | ||||||
|  |  *		doesn't rely on dma_ops structure. | ||||||
|  * |  * | ||||||
|  * At the lowest level, every device in a Linux system is represented by an |  * At the lowest level, every device in a Linux system is represented by an | ||||||
|  * instance of struct device. The device structure contains the information |  * instance of struct device. The device structure contains the information | ||||||
|  | @ -822,6 +824,9 @@ struct device { | ||||||
| #ifdef CONFIG_DMA_NEED_SYNC | #ifdef CONFIG_DMA_NEED_SYNC | ||||||
| 	bool			dma_skip_sync:1; | 	bool			dma_skip_sync:1; | ||||||
| #endif | #endif | ||||||
|  | #ifdef CONFIG_IOMMU_DMA | ||||||
|  | 	bool			dma_iommu:1; | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
|  | @ -13,20 +13,7 @@ | ||||||
| struct cma; | struct cma; | ||||||
| struct iommu_ops; | struct iommu_ops; | ||||||
| 
 | 
 | ||||||
| /*
 |  | ||||||
|  * Values for struct dma_map_ops.flags: |  | ||||||
|  * |  | ||||||
|  * DMA_F_PCI_P2PDMA_SUPPORTED: Indicates the dma_map_ops implementation can |  | ||||||
|  * handle PCI P2PDMA pages in the map_sg/unmap_sg operation. |  | ||||||
|  * DMA_F_CAN_SKIP_SYNC: DMA sync operations can be skipped if the device is |  | ||||||
|  * coherent and it's not an SWIOTLB buffer. |  | ||||||
|  */ |  | ||||||
| #define DMA_F_PCI_P2PDMA_SUPPORTED     (1 << 0) |  | ||||||
| #define DMA_F_CAN_SKIP_SYNC            (1 << 1) |  | ||||||
| 
 |  | ||||||
| struct dma_map_ops { | struct dma_map_ops { | ||||||
| 	unsigned int flags; |  | ||||||
| 
 |  | ||||||
| 	void *(*alloc)(struct device *dev, size_t size, | 	void *(*alloc)(struct device *dev, size_t size, | ||||||
| 			dma_addr_t *dma_handle, gfp_t gfp, | 			dma_addr_t *dma_handle, gfp_t gfp, | ||||||
| 			unsigned long attrs); | 			unsigned long attrs); | ||||||
|  |  | ||||||
							
								
								
									
										147
									
								
								include/linux/iommu-dma.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								include/linux/iommu-dma.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,147 @@ | ||||||
|  | /* SPDX-License-Identifier: GPL-2.0 */ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved | ||||||
|  |  * | ||||||
|  |  * DMA operations that map physical memory through IOMMU. | ||||||
|  |  */ | ||||||
|  | #ifndef _LINUX_IOMMU_DMA_H | ||||||
|  | #define _LINUX_IOMMU_DMA_H | ||||||
|  | 
 | ||||||
|  | #include <linux/dma-direction.h> | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_IOMMU_DMA | ||||||
|  | dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page, | ||||||
|  | 		unsigned long offset, size_t size, enum dma_data_direction dir, | ||||||
|  | 		unsigned long attrs); | ||||||
|  | void iommu_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, | ||||||
|  | 		size_t size, enum dma_data_direction dir, unsigned long attrs); | ||||||
|  | int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||||||
|  | 		enum dma_data_direction dir, unsigned long attrs); | ||||||
|  | void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, | ||||||
|  | 		enum dma_data_direction dir, unsigned long attrs); | ||||||
|  | void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, | ||||||
|  | 		gfp_t gfp, unsigned long attrs); | ||||||
|  | int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma, | ||||||
|  | 		void *cpu_addr, dma_addr_t dma_addr, size_t size, | ||||||
|  | 		unsigned long attrs); | ||||||
|  | int iommu_dma_get_sgtable(struct device *dev, struct sg_table *sgt, | ||||||
|  | 		void *cpu_addr, dma_addr_t dma_addr, size_t size, | ||||||
|  | 		unsigned long attrs); | ||||||
|  | unsigned long iommu_dma_get_merge_boundary(struct device *dev); | ||||||
|  | size_t iommu_dma_opt_mapping_size(void); | ||||||
|  | size_t iommu_dma_max_mapping_size(struct device *dev); | ||||||
|  | void iommu_dma_free(struct device *dev, size_t size, void *cpu_addr, | ||||||
|  | 		dma_addr_t handle, unsigned long attrs); | ||||||
|  | dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys, | ||||||
|  | 		size_t size, enum dma_data_direction dir, unsigned long attrs); | ||||||
|  | void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle, | ||||||
|  | 		size_t size, enum dma_data_direction dir, unsigned long attrs); | ||||||
|  | struct sg_table *iommu_dma_alloc_noncontiguous(struct device *dev, size_t size, | ||||||
|  | 		enum dma_data_direction dir, gfp_t gfp, unsigned long attrs); | ||||||
|  | void iommu_dma_free_noncontiguous(struct device *dev, size_t size, | ||||||
|  | 		struct sg_table *sgt, enum dma_data_direction dir); | ||||||
|  | void iommu_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, | ||||||
|  | 		size_t size, enum dma_data_direction dir); | ||||||
|  | void iommu_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, | ||||||
|  | 		size_t size, enum dma_data_direction dir); | ||||||
|  | void iommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl, | ||||||
|  | 		int nelems, enum dma_data_direction dir); | ||||||
|  | void iommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl, | ||||||
|  | 		int nelems, enum dma_data_direction dir); | ||||||
|  | #else | ||||||
|  | static inline dma_addr_t iommu_dma_map_page(struct device *dev, | ||||||
|  | 		struct page *page, unsigned long offset, size_t size, | ||||||
|  | 		enum dma_data_direction dir, unsigned long attrs) | ||||||
|  | { | ||||||
|  | 	return DMA_MAPPING_ERROR; | ||||||
|  | } | ||||||
|  | static inline void iommu_dma_unmap_page(struct device *dev, | ||||||
|  | 		dma_addr_t dma_handle, size_t size, enum dma_data_direction dir, | ||||||
|  | 		unsigned long attrs) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, | ||||||
|  | 		int nents, enum dma_data_direction dir, unsigned long attrs) | ||||||
|  | { | ||||||
|  | 	return -EINVAL; | ||||||
|  | } | ||||||
|  | static inline void iommu_dma_unmap_sg(struct device *dev, | ||||||
|  | 		struct scatterlist *sg, int nents, enum dma_data_direction dir, | ||||||
|  | 		unsigned long attrs) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline void *iommu_dma_alloc(struct device *dev, size_t size, | ||||||
|  | 		dma_addr_t *handle, gfp_t gfp, unsigned long attrs) | ||||||
|  | { | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | static inline int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma, | ||||||
|  | 		void *cpu_addr, dma_addr_t dma_addr, size_t size, | ||||||
|  | 		unsigned long attrs) | ||||||
|  | { | ||||||
|  | 	return -EINVAL; | ||||||
|  | } | ||||||
|  | static inline int iommu_dma_get_sgtable(struct device *dev, | ||||||
|  | 		struct sg_table *sgt, void *cpu_addr, dma_addr_t dma_addr, | ||||||
|  | 		size_t size, unsigned long attrs) | ||||||
|  | { | ||||||
|  | 	return -EINVAL; | ||||||
|  | } | ||||||
|  | static inline unsigned long iommu_dma_get_merge_boundary(struct device *dev) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | static inline size_t iommu_dma_opt_mapping_size(void) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | static inline size_t iommu_dma_max_mapping_size(struct device *dev) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | static inline void iommu_dma_free(struct device *dev, size_t size, | ||||||
|  | 		void *cpu_addr, dma_addr_t handle, unsigned long attrs) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline dma_addr_t iommu_dma_map_resource(struct device *dev, | ||||||
|  | 		phys_addr_t phys, size_t size, enum dma_data_direction dir, | ||||||
|  | 		unsigned long attrs) | ||||||
|  | { | ||||||
|  | 	return DMA_MAPPING_ERROR; | ||||||
|  | } | ||||||
|  | static inline void iommu_dma_unmap_resource(struct device *dev, | ||||||
|  | 		dma_addr_t handle, size_t size, enum dma_data_direction dir, | ||||||
|  | 		unsigned long attrs) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline struct sg_table * | ||||||
|  | iommu_dma_alloc_noncontiguous(struct device *dev, size_t size, | ||||||
|  | 		enum dma_data_direction dir, gfp_t gfp, unsigned long attrs) | ||||||
|  | { | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | static inline void iommu_dma_free_noncontiguous(struct device *dev, size_t size, | ||||||
|  | 		struct sg_table *sgt, enum dma_data_direction dir) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline void iommu_dma_sync_single_for_cpu(struct device *dev, | ||||||
|  | 		dma_addr_t dma_handle, size_t size, | ||||||
|  | 		enum dma_data_direction dir) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline void iommu_dma_sync_single_for_device(struct device *dev, | ||||||
|  | 		dma_addr_t dma_handle, size_t size, enum dma_data_direction dir) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline void iommu_dma_sync_sg_for_cpu(struct device *dev, | ||||||
|  | 		struct scatterlist *sgl, int nelems, | ||||||
|  | 		enum dma_data_direction dir) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline void iommu_dma_sync_sg_for_device(struct device *dev, | ||||||
|  | 		struct scatterlist *sgl, int nelems, | ||||||
|  | 		enum dma_data_direction dir) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_IOMMU_DMA */ | ||||||
|  | #endif /* _LINUX_IOMMU_DMA_H */ | ||||||
|  | @ -8,8 +8,12 @@ config HAS_DMA | ||||||
| 	depends on !NO_DMA | 	depends on !NO_DMA | ||||||
| 	default y | 	default y | ||||||
| 
 | 
 | ||||||
|  | config DMA_OPS_HELPERS | ||||||
|  | 	bool | ||||||
|  | 
 | ||||||
| config DMA_OPS | config DMA_OPS | ||||||
| 	depends on HAS_DMA | 	depends on HAS_DMA | ||||||
|  | 	select DMA_OPS_HELPERS | ||||||
| 	bool | 	bool | ||||||
| 
 | 
 | ||||||
| # | # | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| # SPDX-License-Identifier: GPL-2.0
 | # SPDX-License-Identifier: GPL-2.0
 | ||||||
| 
 | 
 | ||||||
| obj-$(CONFIG_HAS_DMA)			+= mapping.o direct.o | obj-$(CONFIG_HAS_DMA)			+= mapping.o direct.o | ||||||
| obj-$(CONFIG_DMA_OPS)			+= ops_helpers.o | obj-$(CONFIG_DMA_OPS_HELPERS)		+= ops_helpers.o | ||||||
| obj-$(CONFIG_DMA_OPS)			+= dummy.o | obj-$(CONFIG_DMA_OPS)			+= dummy.o | ||||||
| obj-$(CONFIG_DMA_CMA)			+= contiguous.o | obj-$(CONFIG_DMA_CMA)			+= contiguous.o | ||||||
| obj-$(CONFIG_DMA_DECLARE_COHERENT)	+= coherent.o | obj-$(CONFIG_DMA_DECLARE_COHERENT)	+= coherent.o | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include <linux/dma-map-ops.h> | #include <linux/dma-map-ops.h> | ||||||
| #include <linux/export.h> | #include <linux/export.h> | ||||||
| #include <linux/gfp.h> | #include <linux/gfp.h> | ||||||
|  | #include <linux/iommu-dma.h> | ||||||
| #include <linux/kmsan.h> | #include <linux/kmsan.h> | ||||||
| #include <linux/of_device.h> | #include <linux/of_device.h> | ||||||
| #include <linux/slab.h> | #include <linux/slab.h> | ||||||
|  | @ -113,11 +114,27 @@ void *dmam_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle, | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(dmam_alloc_attrs); | EXPORT_SYMBOL(dmam_alloc_attrs); | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_IOMMU_DMA | ||||||
|  | static bool use_dma_iommu(struct device *dev) | ||||||
|  | { | ||||||
|  | 	return dev->dma_iommu; | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | static bool use_dma_iommu(struct device *dev) | ||||||
|  | { | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| static bool dma_go_direct(struct device *dev, dma_addr_t mask, | static bool dma_go_direct(struct device *dev, dma_addr_t mask, | ||||||
| 		const struct dma_map_ops *ops) | 		const struct dma_map_ops *ops) | ||||||
| { | { | ||||||
|  | 	if (use_dma_iommu(dev)) | ||||||
|  | 		return false; | ||||||
|  | 
 | ||||||
| 	if (likely(!ops)) | 	if (likely(!ops)) | ||||||
| 		return true; | 		return true; | ||||||
|  | 
 | ||||||
| #ifdef CONFIG_DMA_OPS_BYPASS | #ifdef CONFIG_DMA_OPS_BYPASS | ||||||
| 	if (dev->dma_ops_bypass) | 	if (dev->dma_ops_bypass) | ||||||
| 		return min_not_zero(mask, dev->bus_dma_limit) >= | 		return min_not_zero(mask, dev->bus_dma_limit) >= | ||||||
|  | @ -159,6 +176,8 @@ dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page, | ||||||
| 	if (dma_map_direct(dev, ops) || | 	if (dma_map_direct(dev, ops) || | ||||||
| 	    arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size)) | 	    arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size)) | ||||||
| 		addr = dma_direct_map_page(dev, page, offset, size, dir, attrs); | 		addr = dma_direct_map_page(dev, page, offset, size, dir, attrs); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		addr = iommu_dma_map_page(dev, page, offset, size, dir, attrs); | ||||||
| 	else | 	else | ||||||
| 		addr = ops->map_page(dev, page, offset, size, dir, attrs); | 		addr = ops->map_page(dev, page, offset, size, dir, attrs); | ||||||
| 	kmsan_handle_dma(page, offset, size, dir); | 	kmsan_handle_dma(page, offset, size, dir); | ||||||
|  | @ -177,6 +196,8 @@ void dma_unmap_page_attrs(struct device *dev, dma_addr_t addr, size_t size, | ||||||
| 	if (dma_map_direct(dev, ops) || | 	if (dma_map_direct(dev, ops) || | ||||||
| 	    arch_dma_unmap_page_direct(dev, addr + size)) | 	    arch_dma_unmap_page_direct(dev, addr + size)) | ||||||
| 		dma_direct_unmap_page(dev, addr, size, dir, attrs); | 		dma_direct_unmap_page(dev, addr, size, dir, attrs); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		iommu_dma_unmap_page(dev, addr, size, dir, attrs); | ||||||
| 	else | 	else | ||||||
| 		ops->unmap_page(dev, addr, size, dir, attrs); | 		ops->unmap_page(dev, addr, size, dir, attrs); | ||||||
| 	debug_dma_unmap_page(dev, addr, size, dir); | 	debug_dma_unmap_page(dev, addr, size, dir); | ||||||
|  | @ -197,6 +218,8 @@ static int __dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, | ||||||
| 	if (dma_map_direct(dev, ops) || | 	if (dma_map_direct(dev, ops) || | ||||||
| 	    arch_dma_map_sg_direct(dev, sg, nents)) | 	    arch_dma_map_sg_direct(dev, sg, nents)) | ||||||
| 		ents = dma_direct_map_sg(dev, sg, nents, dir, attrs); | 		ents = dma_direct_map_sg(dev, sg, nents, dir, attrs); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		ents = iommu_dma_map_sg(dev, sg, nents, dir, attrs); | ||||||
| 	else | 	else | ||||||
| 		ents = ops->map_sg(dev, sg, nents, dir, attrs); | 		ents = ops->map_sg(dev, sg, nents, dir, attrs); | ||||||
| 
 | 
 | ||||||
|  | @ -291,7 +314,9 @@ void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, | ||||||
| 	if (dma_map_direct(dev, ops) || | 	if (dma_map_direct(dev, ops) || | ||||||
| 	    arch_dma_unmap_sg_direct(dev, sg, nents)) | 	    arch_dma_unmap_sg_direct(dev, sg, nents)) | ||||||
| 		dma_direct_unmap_sg(dev, sg, nents, dir, attrs); | 		dma_direct_unmap_sg(dev, sg, nents, dir, attrs); | ||||||
| 	else | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		iommu_dma_unmap_sg(dev, sg, nents, dir, attrs); | ||||||
|  | 	else if (ops->unmap_sg) | ||||||
| 		ops->unmap_sg(dev, sg, nents, dir, attrs); | 		ops->unmap_sg(dev, sg, nents, dir, attrs); | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(dma_unmap_sg_attrs); | EXPORT_SYMBOL(dma_unmap_sg_attrs); | ||||||
|  | @ -309,6 +334,8 @@ dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr, | ||||||
| 
 | 
 | ||||||
| 	if (dma_map_direct(dev, ops)) | 	if (dma_map_direct(dev, ops)) | ||||||
| 		addr = dma_direct_map_resource(dev, phys_addr, size, dir, attrs); | 		addr = dma_direct_map_resource(dev, phys_addr, size, dir, attrs); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		addr = iommu_dma_map_resource(dev, phys_addr, size, dir, attrs); | ||||||
| 	else if (ops->map_resource) | 	else if (ops->map_resource) | ||||||
| 		addr = ops->map_resource(dev, phys_addr, size, dir, attrs); | 		addr = ops->map_resource(dev, phys_addr, size, dir, attrs); | ||||||
| 
 | 
 | ||||||
|  | @ -323,7 +350,11 @@ void dma_unmap_resource(struct device *dev, dma_addr_t addr, size_t size, | ||||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||||
| 
 | 
 | ||||||
| 	BUG_ON(!valid_dma_direction(dir)); | 	BUG_ON(!valid_dma_direction(dir)); | ||||||
| 	if (!dma_map_direct(dev, ops) && ops->unmap_resource) | 	if (dma_map_direct(dev, ops)) | ||||||
|  | 		; /* nothing to do: uncached and no swiotlb */ | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		iommu_dma_unmap_resource(dev, addr, size, dir, attrs); | ||||||
|  | 	else if (ops->unmap_resource) | ||||||
| 		ops->unmap_resource(dev, addr, size, dir, attrs); | 		ops->unmap_resource(dev, addr, size, dir, attrs); | ||||||
| 	debug_dma_unmap_resource(dev, addr, size, dir); | 	debug_dma_unmap_resource(dev, addr, size, dir); | ||||||
| } | } | ||||||
|  | @ -338,6 +369,8 @@ void __dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, size_t size, | ||||||
| 	BUG_ON(!valid_dma_direction(dir)); | 	BUG_ON(!valid_dma_direction(dir)); | ||||||
| 	if (dma_map_direct(dev, ops)) | 	if (dma_map_direct(dev, ops)) | ||||||
| 		dma_direct_sync_single_for_cpu(dev, addr, size, dir); | 		dma_direct_sync_single_for_cpu(dev, addr, size, dir); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		iommu_dma_sync_single_for_cpu(dev, addr, size, dir); | ||||||
| 	else if (ops->sync_single_for_cpu) | 	else if (ops->sync_single_for_cpu) | ||||||
| 		ops->sync_single_for_cpu(dev, addr, size, dir); | 		ops->sync_single_for_cpu(dev, addr, size, dir); | ||||||
| 	debug_dma_sync_single_for_cpu(dev, addr, size, dir); | 	debug_dma_sync_single_for_cpu(dev, addr, size, dir); | ||||||
|  | @ -352,6 +385,8 @@ void __dma_sync_single_for_device(struct device *dev, dma_addr_t addr, | ||||||
| 	BUG_ON(!valid_dma_direction(dir)); | 	BUG_ON(!valid_dma_direction(dir)); | ||||||
| 	if (dma_map_direct(dev, ops)) | 	if (dma_map_direct(dev, ops)) | ||||||
| 		dma_direct_sync_single_for_device(dev, addr, size, dir); | 		dma_direct_sync_single_for_device(dev, addr, size, dir); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		iommu_dma_sync_single_for_device(dev, addr, size, dir); | ||||||
| 	else if (ops->sync_single_for_device) | 	else if (ops->sync_single_for_device) | ||||||
| 		ops->sync_single_for_device(dev, addr, size, dir); | 		ops->sync_single_for_device(dev, addr, size, dir); | ||||||
| 	debug_dma_sync_single_for_device(dev, addr, size, dir); | 	debug_dma_sync_single_for_device(dev, addr, size, dir); | ||||||
|  | @ -366,6 +401,8 @@ void __dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, | ||||||
| 	BUG_ON(!valid_dma_direction(dir)); | 	BUG_ON(!valid_dma_direction(dir)); | ||||||
| 	if (dma_map_direct(dev, ops)) | 	if (dma_map_direct(dev, ops)) | ||||||
| 		dma_direct_sync_sg_for_cpu(dev, sg, nelems, dir); | 		dma_direct_sync_sg_for_cpu(dev, sg, nelems, dir); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		iommu_dma_sync_sg_for_cpu(dev, sg, nelems, dir); | ||||||
| 	else if (ops->sync_sg_for_cpu) | 	else if (ops->sync_sg_for_cpu) | ||||||
| 		ops->sync_sg_for_cpu(dev, sg, nelems, dir); | 		ops->sync_sg_for_cpu(dev, sg, nelems, dir); | ||||||
| 	debug_dma_sync_sg_for_cpu(dev, sg, nelems, dir); | 	debug_dma_sync_sg_for_cpu(dev, sg, nelems, dir); | ||||||
|  | @ -380,6 +417,8 @@ void __dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, | ||||||
| 	BUG_ON(!valid_dma_direction(dir)); | 	BUG_ON(!valid_dma_direction(dir)); | ||||||
| 	if (dma_map_direct(dev, ops)) | 	if (dma_map_direct(dev, ops)) | ||||||
| 		dma_direct_sync_sg_for_device(dev, sg, nelems, dir); | 		dma_direct_sync_sg_for_device(dev, sg, nelems, dir); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		iommu_dma_sync_sg_for_device(dev, sg, nelems, dir); | ||||||
| 	else if (ops->sync_sg_for_device) | 	else if (ops->sync_sg_for_device) | ||||||
| 		ops->sync_sg_for_device(dev, sg, nelems, dir); | 		ops->sync_sg_for_device(dev, sg, nelems, dir); | ||||||
| 	debug_dma_sync_sg_for_device(dev, sg, nelems, dir); | 	debug_dma_sync_sg_for_device(dev, sg, nelems, dir); | ||||||
|  | @ -405,7 +444,7 @@ static void dma_setup_need_sync(struct device *dev) | ||||||
| { | { | ||||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||||
| 
 | 
 | ||||||
| 	if (dma_map_direct(dev, ops) || (ops->flags & DMA_F_CAN_SKIP_SYNC)) | 	if (dma_map_direct(dev, ops) || use_dma_iommu(dev)) | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * dma_skip_sync will be reset to %false on first SWIOTLB buffer | 		 * dma_skip_sync will be reset to %false on first SWIOTLB buffer | ||||||
| 		 * mapping, if any. During the device initialization, it's | 		 * mapping, if any. During the device initialization, it's | ||||||
|  | @ -446,6 +485,9 @@ int dma_get_sgtable_attrs(struct device *dev, struct sg_table *sgt, | ||||||
| 	if (dma_alloc_direct(dev, ops)) | 	if (dma_alloc_direct(dev, ops)) | ||||||
| 		return dma_direct_get_sgtable(dev, sgt, cpu_addr, dma_addr, | 		return dma_direct_get_sgtable(dev, sgt, cpu_addr, dma_addr, | ||||||
| 				size, attrs); | 				size, attrs); | ||||||
|  | 	if (use_dma_iommu(dev)) | ||||||
|  | 		return iommu_dma_get_sgtable(dev, sgt, cpu_addr, dma_addr, | ||||||
|  | 				size, attrs); | ||||||
| 	if (!ops->get_sgtable) | 	if (!ops->get_sgtable) | ||||||
| 		return -ENXIO; | 		return -ENXIO; | ||||||
| 	return ops->get_sgtable(dev, sgt, cpu_addr, dma_addr, size, attrs); | 	return ops->get_sgtable(dev, sgt, cpu_addr, dma_addr, size, attrs); | ||||||
|  | @ -482,6 +524,8 @@ bool dma_can_mmap(struct device *dev) | ||||||
| 
 | 
 | ||||||
| 	if (dma_alloc_direct(dev, ops)) | 	if (dma_alloc_direct(dev, ops)) | ||||||
| 		return dma_direct_can_mmap(dev); | 		return dma_direct_can_mmap(dev); | ||||||
|  | 	if (use_dma_iommu(dev)) | ||||||
|  | 		return true; | ||||||
| 	return ops->mmap != NULL; | 	return ops->mmap != NULL; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(dma_can_mmap); | EXPORT_SYMBOL_GPL(dma_can_mmap); | ||||||
|  | @ -508,6 +552,9 @@ int dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma, | ||||||
| 	if (dma_alloc_direct(dev, ops)) | 	if (dma_alloc_direct(dev, ops)) | ||||||
| 		return dma_direct_mmap(dev, vma, cpu_addr, dma_addr, size, | 		return dma_direct_mmap(dev, vma, cpu_addr, dma_addr, size, | ||||||
| 				attrs); | 				attrs); | ||||||
|  | 	if (use_dma_iommu(dev)) | ||||||
|  | 		return iommu_dma_mmap(dev, vma, cpu_addr, dma_addr, size, | ||||||
|  | 				      attrs); | ||||||
| 	if (!ops->mmap) | 	if (!ops->mmap) | ||||||
| 		return -ENXIO; | 		return -ENXIO; | ||||||
| 	return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs); | 	return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs); | ||||||
|  | @ -559,6 +606,8 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle, | ||||||
| 
 | 
 | ||||||
| 	if (dma_alloc_direct(dev, ops)) | 	if (dma_alloc_direct(dev, ops)) | ||||||
| 		cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs); | 		cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		cpu_addr = iommu_dma_alloc(dev, size, dma_handle, flag, attrs); | ||||||
| 	else if (ops->alloc) | 	else if (ops->alloc) | ||||||
| 		cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs); | 		cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs); | ||||||
| 	else | 	else | ||||||
|  | @ -591,6 +640,8 @@ void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr, | ||||||
| 	debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); | 	debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); | ||||||
| 	if (dma_alloc_direct(dev, ops)) | 	if (dma_alloc_direct(dev, ops)) | ||||||
| 		dma_direct_free(dev, size, cpu_addr, dma_handle, attrs); | 		dma_direct_free(dev, size, cpu_addr, dma_handle, attrs); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		iommu_dma_free(dev, size, cpu_addr, dma_handle, attrs); | ||||||
| 	else if (ops->free) | 	else if (ops->free) | ||||||
| 		ops->free(dev, size, cpu_addr, dma_handle, attrs); | 		ops->free(dev, size, cpu_addr, dma_handle, attrs); | ||||||
| } | } | ||||||
|  | @ -611,6 +662,8 @@ static struct page *__dma_alloc_pages(struct device *dev, size_t size, | ||||||
| 	size = PAGE_ALIGN(size); | 	size = PAGE_ALIGN(size); | ||||||
| 	if (dma_alloc_direct(dev, ops)) | 	if (dma_alloc_direct(dev, ops)) | ||||||
| 		return dma_direct_alloc_pages(dev, size, dma_handle, dir, gfp); | 		return dma_direct_alloc_pages(dev, size, dma_handle, dir, gfp); | ||||||
|  | 	if (use_dma_iommu(dev)) | ||||||
|  | 		return dma_common_alloc_pages(dev, size, dma_handle, dir, gfp); | ||||||
| 	if (!ops->alloc_pages_op) | 	if (!ops->alloc_pages_op) | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	return ops->alloc_pages_op(dev, size, dma_handle, dir, gfp); | 	return ops->alloc_pages_op(dev, size, dma_handle, dir, gfp); | ||||||
|  | @ -635,6 +688,8 @@ static void __dma_free_pages(struct device *dev, size_t size, struct page *page, | ||||||
| 	size = PAGE_ALIGN(size); | 	size = PAGE_ALIGN(size); | ||||||
| 	if (dma_alloc_direct(dev, ops)) | 	if (dma_alloc_direct(dev, ops)) | ||||||
| 		dma_direct_free_pages(dev, size, page, dma_handle, dir); | 		dma_direct_free_pages(dev, size, page, dma_handle, dir); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		dma_common_free_pages(dev, size, page, dma_handle, dir); | ||||||
| 	else if (ops->free_pages) | 	else if (ops->free_pages) | ||||||
| 		ops->free_pages(dev, size, page, dma_handle, dir); | 		ops->free_pages(dev, size, page, dma_handle, dir); | ||||||
| } | } | ||||||
|  | @ -697,6 +752,8 @@ struct sg_table *dma_alloc_noncontiguous(struct device *dev, size_t size, | ||||||
| 
 | 
 | ||||||
| 	if (ops && ops->alloc_noncontiguous) | 	if (ops && ops->alloc_noncontiguous) | ||||||
| 		sgt = ops->alloc_noncontiguous(dev, size, dir, gfp, attrs); | 		sgt = ops->alloc_noncontiguous(dev, size, dir, gfp, attrs); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		sgt = iommu_dma_alloc_noncontiguous(dev, size, dir, gfp, attrs); | ||||||
| 	else | 	else | ||||||
| 		sgt = alloc_single_sgt(dev, size, dir, gfp); | 		sgt = alloc_single_sgt(dev, size, dir, gfp); | ||||||
| 
 | 
 | ||||||
|  | @ -725,6 +782,8 @@ void dma_free_noncontiguous(struct device *dev, size_t size, | ||||||
| 	debug_dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir); | 	debug_dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir); | ||||||
| 	if (ops && ops->free_noncontiguous) | 	if (ops && ops->free_noncontiguous) | ||||||
| 		ops->free_noncontiguous(dev, size, sgt, dir); | 		ops->free_noncontiguous(dev, size, sgt, dir); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		iommu_dma_free_noncontiguous(dev, size, sgt, dir); | ||||||
| 	else | 	else | ||||||
| 		free_single_sgt(dev, size, sgt, dir); | 		free_single_sgt(dev, size, sgt, dir); | ||||||
| } | } | ||||||
|  | @ -772,6 +831,8 @@ static int dma_supported(struct device *dev, u64 mask) | ||||||
| { | { | ||||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||||
| 
 | 
 | ||||||
|  | 	if (WARN_ON(ops && use_dma_iommu(dev))) | ||||||
|  | 		return false; | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * ->dma_supported sets the bypass flag, so we must always call | 	 * ->dma_supported sets the bypass flag, so we must always call | ||||||
| 	 * into the method here unless the device is truly direct mapped. | 	 * into the method here unless the device is truly direct mapped. | ||||||
|  | @ -787,17 +848,14 @@ bool dma_pci_p2pdma_supported(struct device *dev) | ||||||
| { | { | ||||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||||
| 
 | 
 | ||||||
| 	/* if ops is not set, dma direct will be used which supports P2PDMA */ |  | ||||||
| 	if (!ops) |  | ||||||
| 		return true; |  | ||||||
| 
 |  | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Note: dma_ops_bypass is not checked here because P2PDMA should | 	 * Note: dma_ops_bypass is not checked here because P2PDMA should | ||||||
| 	 * not be used with dma mapping ops that do not have support even | 	 * not be used with dma mapping ops that do not have support even | ||||||
| 	 * if the specific device is bypassing them. | 	 * if the specific device is bypassing them. | ||||||
| 	 */ | 	 */ | ||||||
| 
 | 
 | ||||||
| 	return ops->flags & DMA_F_PCI_P2PDMA_SUPPORTED; | 	/* if ops is not set, dma direct and default IOMMU support P2PDMA */ | ||||||
|  | 	return !ops; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(dma_pci_p2pdma_supported); | EXPORT_SYMBOL_GPL(dma_pci_p2pdma_supported); | ||||||
| 
 | 
 | ||||||
|  | @ -865,6 +923,8 @@ size_t dma_max_mapping_size(struct device *dev) | ||||||
| 
 | 
 | ||||||
| 	if (dma_map_direct(dev, ops)) | 	if (dma_map_direct(dev, ops)) | ||||||
| 		size = dma_direct_max_mapping_size(dev); | 		size = dma_direct_max_mapping_size(dev); | ||||||
|  | 	else if (use_dma_iommu(dev)) | ||||||
|  | 		size = iommu_dma_max_mapping_size(dev); | ||||||
| 	else if (ops && ops->max_mapping_size) | 	else if (ops && ops->max_mapping_size) | ||||||
| 		size = ops->max_mapping_size(dev); | 		size = ops->max_mapping_size(dev); | ||||||
| 
 | 
 | ||||||
|  | @ -877,7 +937,9 @@ size_t dma_opt_mapping_size(struct device *dev) | ||||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||||
| 	size_t size = SIZE_MAX; | 	size_t size = SIZE_MAX; | ||||||
| 
 | 
 | ||||||
| 	if (ops && ops->opt_mapping_size) | 	if (use_dma_iommu(dev)) | ||||||
|  | 		size = iommu_dma_opt_mapping_size(); | ||||||
|  | 	else if (ops && ops->opt_mapping_size) | ||||||
| 		size = ops->opt_mapping_size(); | 		size = ops->opt_mapping_size(); | ||||||
| 
 | 
 | ||||||
| 	return min(dma_max_mapping_size(dev), size); | 	return min(dma_max_mapping_size(dev), size); | ||||||
|  | @ -888,6 +950,9 @@ unsigned long dma_get_merge_boundary(struct device *dev) | ||||||
| { | { | ||||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||||
| 
 | 
 | ||||||
|  | 	if (use_dma_iommu(dev)) | ||||||
|  | 		return iommu_dma_get_merge_boundary(dev); | ||||||
|  | 
 | ||||||
| 	if (!ops || !ops->get_merge_boundary) | 	if (!ops || !ops->get_merge_boundary) | ||||||
| 		return 0;	/* can't merge */ | 		return 0;	/* can't merge */ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Leon Romanovsky
						Leon Romanovsky