mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-01 00:58:39 +02:00 
			
		
		
		
	dma-mapping: add a new dma_alloc_pages API
This API is the equivalent of alloc_pages, except that the returned memory is guaranteed to be DMA addressable by the passed in device. The implementation will also be used to provide a more sensible replacement for DMA_ATTR_NON_CONSISTENT flag. Additionally dma_alloc_noncoherent is switched over to use dma_alloc_pages as its backend. Signed-off-by: Christoph Hellwig <hch@lst.de> Acked-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> (MIPS part)
This commit is contained in:
		
							parent
							
								
									5a84292271
								
							
						
					
					
						commit
						efa70f2fdc
					
				
					 23 changed files with 206 additions and 37 deletions
				
			
		|  | @ -25,14 +25,6 @@ Since it is optional for platforms to implement DMA_ATTR_WRITE_COMBINE, | |||
| those that do not will simply ignore the attribute and exhibit default | ||||
| behavior. | ||||
| 
 | ||||
| DMA_ATTR_NON_CONSISTENT | ||||
| ----------------------- | ||||
| 
 | ||||
| DMA_ATTR_NON_CONSISTENT lets the platform to choose to return either | ||||
| consistent or non-consistent memory as it sees fit.  By using this API, | ||||
| you are guaranteeing to the platform that you have all the correct and | ||||
| necessary sync points for this memory in the driver. | ||||
| 
 | ||||
| DMA_ATTR_NO_KERNEL_MAPPING | ||||
| -------------------------- | ||||
| 
 | ||||
|  |  | |||
|  | @ -952,5 +952,7 @@ const struct dma_map_ops alpha_pci_ops = { | |||
| 	.dma_supported		= alpha_pci_supported, | ||||
| 	.mmap			= dma_common_mmap, | ||||
| 	.get_sgtable		= dma_common_get_sgtable, | ||||
| 	.alloc_pages		= dma_common_alloc_pages, | ||||
| 	.free_pages		= dma_common_free_pages, | ||||
| }; | ||||
| EXPORT_SYMBOL(alpha_pci_ops); | ||||
|  |  | |||
|  | @ -176,6 +176,8 @@ static void arm_nommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist | |||
| const struct dma_map_ops arm_nommu_dma_ops = { | ||||
| 	.alloc			= arm_nommu_dma_alloc, | ||||
| 	.free			= arm_nommu_dma_free, | ||||
| 	.alloc_pages		= dma_direct_alloc_pages, | ||||
| 	.free_pages		= dma_direct_free_pages, | ||||
| 	.mmap			= arm_nommu_dma_mmap, | ||||
| 	.map_page		= arm_nommu_dma_map_page, | ||||
| 	.unmap_page		= arm_nommu_dma_unmap_page, | ||||
|  |  | |||
|  | @ -199,6 +199,8 @@ static int arm_dma_supported(struct device *dev, u64 mask) | |||
| const struct dma_map_ops arm_dma_ops = { | ||||
| 	.alloc			= arm_dma_alloc, | ||||
| 	.free			= arm_dma_free, | ||||
| 	.alloc_pages		= dma_direct_alloc_pages, | ||||
| 	.free_pages		= dma_direct_free_pages, | ||||
| 	.mmap			= arm_dma_mmap, | ||||
| 	.get_sgtable		= arm_dma_get_sgtable, | ||||
| 	.map_page		= arm_dma_map_page, | ||||
|  | @ -226,6 +228,8 @@ static int arm_coherent_dma_mmap(struct device *dev, struct vm_area_struct *vma, | |||
| const struct dma_map_ops arm_coherent_dma_ops = { | ||||
| 	.alloc			= arm_coherent_dma_alloc, | ||||
| 	.free			= arm_coherent_dma_free, | ||||
| 	.alloc_pages		= dma_direct_alloc_pages, | ||||
| 	.free_pages		= dma_direct_free_pages, | ||||
| 	.mmap			= arm_coherent_dma_mmap, | ||||
| 	.get_sgtable		= arm_dma_get_sgtable, | ||||
| 	.map_page		= arm_coherent_dma_map_page, | ||||
|  |  | |||
|  | @ -2070,6 +2070,8 @@ static const struct dma_map_ops sba_dma_ops = { | |||
| 	.dma_supported		= sba_dma_supported, | ||||
| 	.mmap			= dma_common_mmap, | ||||
| 	.get_sgtable		= dma_common_get_sgtable, | ||||
| 	.alloc_pages		= dma_common_alloc_pages, | ||||
| 	.free_pages		= dma_common_free_pages, | ||||
| }; | ||||
| 
 | ||||
| static int __init | ||||
|  |  | |||
|  | @ -506,9 +506,6 @@ static void *jazz_dma_alloc(struct device *dev, size_t size, | |||
| 	*dma_handle = vdma_alloc(virt_to_phys(ret), size); | ||||
| 	if (*dma_handle == DMA_MAPPING_ERROR) | ||||
| 		goto out_free_pages; | ||||
| 
 | ||||
| 	if (attrs & DMA_ATTR_NON_CONSISTENT) | ||||
| 		return ret; | ||||
| 	arch_dma_prep_coherent(page, size); | ||||
| 	return (void *)(UNCAC_BASE + __pa(ret)); | ||||
| 
 | ||||
|  | @ -521,8 +518,6 @@ static void jazz_dma_free(struct device *dev, size_t size, void *vaddr, | |||
| 		dma_addr_t dma_handle, unsigned long attrs) | ||||
| { | ||||
| 	vdma_free(dma_handle); | ||||
| 	if (!(attrs & DMA_ATTR_NON_CONSISTENT)) | ||||
| 		vaddr = __va(vaddr - UNCAC_BASE); | ||||
| 	__free_pages(virt_to_page(vaddr), get_order(size)); | ||||
| } | ||||
| 
 | ||||
|  | @ -622,5 +617,7 @@ const struct dma_map_ops jazz_dma_ops = { | |||
| 	.sync_sg_for_device	= jazz_dma_sync_sg_for_device, | ||||
| 	.mmap			= dma_common_mmap, | ||||
| 	.get_sgtable		= dma_common_get_sgtable, | ||||
| 	.alloc_pages		= dma_common_alloc_pages, | ||||
| 	.free_pages		= dma_common_free_pages, | ||||
| }; | ||||
| EXPORT_SYMBOL(jazz_dma_ops); | ||||
|  |  | |||
|  | @ -138,4 +138,6 @@ const struct dma_map_ops dma_iommu_ops = { | |||
| 	.get_required_mask	= dma_iommu_get_required_mask, | ||||
| 	.mmap			= dma_common_mmap, | ||||
| 	.get_sgtable		= dma_common_get_sgtable, | ||||
| 	.alloc_pages		= dma_common_alloc_pages, | ||||
| 	.free_pages		= dma_common_free_pages, | ||||
| }; | ||||
|  |  | |||
|  | @ -696,6 +696,8 @@ static const struct dma_map_ops ps3_sb_dma_ops = { | |||
| 	.unmap_page = ps3_unmap_page, | ||||
| 	.mmap = dma_common_mmap, | ||||
| 	.get_sgtable = dma_common_get_sgtable, | ||||
| 	.alloc_pages = dma_common_alloc_pages, | ||||
| 	.free_pages = dma_common_free_pages, | ||||
| }; | ||||
| 
 | ||||
| static const struct dma_map_ops ps3_ioc0_dma_ops = { | ||||
|  | @ -708,6 +710,8 @@ static const struct dma_map_ops ps3_ioc0_dma_ops = { | |||
| 	.unmap_page = ps3_unmap_page, | ||||
| 	.mmap = dma_common_mmap, | ||||
| 	.get_sgtable = dma_common_get_sgtable, | ||||
| 	.alloc_pages = dma_common_alloc_pages, | ||||
| 	.free_pages = dma_common_free_pages, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  |  | |||
|  | @ -608,6 +608,8 @@ static const struct dma_map_ops vio_dma_mapping_ops = { | |||
| 	.get_required_mask = dma_iommu_get_required_mask, | ||||
| 	.mmap		   = dma_common_mmap, | ||||
| 	.get_sgtable	   = dma_common_get_sgtable, | ||||
| 	.alloc_pages	   = dma_common_alloc_pages, | ||||
| 	.free_pages	   = dma_common_free_pages, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  |  | |||
|  | @ -668,6 +668,8 @@ const struct dma_map_ops s390_pci_dma_ops = { | |||
| 	.unmap_page	= s390_dma_unmap_pages, | ||||
| 	.mmap		= dma_common_mmap, | ||||
| 	.get_sgtable	= dma_common_get_sgtable, | ||||
| 	.alloc_pages	= dma_common_alloc_pages, | ||||
| 	.free_pages	= dma_common_free_pages, | ||||
| 	/* dma_supported is unconditionally true without a callback */ | ||||
| }; | ||||
| EXPORT_SYMBOL_GPL(s390_pci_dma_ops); | ||||
|  |  | |||
|  | @ -677,6 +677,8 @@ static const struct dma_map_ops gart_dma_ops = { | |||
| 	.get_sgtable			= dma_common_get_sgtable, | ||||
| 	.dma_supported			= dma_direct_supported, | ||||
| 	.get_required_mask		= dma_direct_get_required_mask, | ||||
| 	.alloc_pages			= dma_direct_alloc_pages, | ||||
| 	.free_pages			= dma_direct_free_pages, | ||||
| }; | ||||
| 
 | ||||
| static void gart_iommu_shutdown(void) | ||||
|  |  | |||
|  | @ -1120,6 +1120,8 @@ static unsigned long iommu_dma_get_merge_boundary(struct device *dev) | |||
| static const struct dma_map_ops iommu_dma_ops = { | ||||
| 	.alloc			= iommu_dma_alloc, | ||||
| 	.free			= iommu_dma_free, | ||||
| 	.alloc_pages		= dma_common_alloc_pages, | ||||
| 	.free_pages		= dma_common_free_pages, | ||||
| 	.mmap			= iommu_dma_mmap, | ||||
| 	.get_sgtable		= iommu_dma_get_sgtable, | ||||
| 	.map_page		= iommu_dma_map_page, | ||||
|  |  | |||
|  | @ -3712,6 +3712,8 @@ static const struct dma_map_ops intel_dma_ops = { | |||
| 	.dma_supported = dma_direct_supported, | ||||
| 	.mmap = dma_common_mmap, | ||||
| 	.get_sgtable = dma_common_get_sgtable, | ||||
| 	.alloc_pages = dma_common_alloc_pages, | ||||
| 	.free_pages = dma_common_free_pages, | ||||
| 	.get_required_mask = intel_get_required_mask, | ||||
| }; | ||||
| 
 | ||||
|  | @ -3965,6 +3967,8 @@ static const struct dma_map_ops bounce_dma_ops = { | |||
| 	.sync_sg_for_device	= bounce_sync_sg_for_device, | ||||
| 	.map_resource		= bounce_map_resource, | ||||
| 	.unmap_resource		= bounce_unmap_resource, | ||||
| 	.alloc_pages		= dma_common_alloc_pages, | ||||
| 	.free_pages		= dma_common_free_pages, | ||||
| 	.dma_supported		= dma_direct_supported, | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1024,6 +1024,8 @@ static const struct dma_map_ops ccio_ops = { | |||
| 	.map_sg = 		ccio_map_sg, | ||||
| 	.unmap_sg = 		ccio_unmap_sg, | ||||
| 	.get_sgtable =		dma_common_get_sgtable, | ||||
| 	.alloc_pages =		dma_common_alloc_pages, | ||||
| 	.free_pages =		dma_common_free_pages, | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_PROC_FS | ||||
|  |  | |||
|  | @ -1076,6 +1076,8 @@ static const struct dma_map_ops sba_ops = { | |||
| 	.map_sg =		sba_map_sg, | ||||
| 	.unmap_sg =		sba_unmap_sg, | ||||
| 	.get_sgtable =		dma_common_get_sgtable, | ||||
| 	.alloc_pages =		dma_common_alloc_pages, | ||||
| 	.free_pages =		dma_common_free_pages, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -578,4 +578,6 @@ const struct dma_map_ops xen_swiotlb_dma_ops = { | |||
| 	.dma_supported = xen_swiotlb_dma_supported, | ||||
| 	.mmap = dma_common_mmap, | ||||
| 	.get_sgtable = dma_common_get_sgtable, | ||||
| 	.alloc_pages = dma_common_alloc_pages, | ||||
| 	.free_pages = dma_common_free_pages, | ||||
| }; | ||||
|  |  | |||
|  | @ -115,6 +115,11 @@ void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, | |||
| 		gfp_t gfp, unsigned long attrs); | ||||
| void dma_direct_free(struct device *dev, size_t size, void *cpu_addr, | ||||
| 		dma_addr_t dma_addr, unsigned long attrs); | ||||
| struct page *dma_direct_alloc_pages(struct device *dev, size_t size, | ||||
| 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp); | ||||
| void dma_direct_free_pages(struct device *dev, size_t size, | ||||
| 		struct page *page, dma_addr_t dma_addr, | ||||
| 		enum dma_data_direction dir); | ||||
| int dma_direct_get_sgtable(struct device *dev, struct sg_table *sgt, | ||||
| 		void *cpu_addr, dma_addr_t dma_addr, size_t size, | ||||
| 		unsigned long attrs); | ||||
|  |  | |||
|  | @ -27,11 +27,6 @@ | |||
|  * buffered to improve performance. | ||||
|  */ | ||||
| #define DMA_ATTR_WRITE_COMBINE		(1UL << 2) | ||||
| /*
 | ||||
|  * DMA_ATTR_NON_CONSISTENT: Lets the platform to choose to return either | ||||
|  * consistent or non-consistent memory as it sees fit. | ||||
|  */ | ||||
| #define DMA_ATTR_NON_CONSISTENT		(1UL << 3) | ||||
| /*
 | ||||
|  * DMA_ATTR_NO_KERNEL_MAPPING: Lets the platform to avoid creating a kernel | ||||
|  * virtual mapping for the allocated buffer. | ||||
|  | @ -74,6 +69,11 @@ struct dma_map_ops { | |||
| 	void (*free)(struct device *dev, size_t size, | ||||
| 			      void *vaddr, dma_addr_t dma_handle, | ||||
| 			      unsigned long attrs); | ||||
| 	struct page *(*alloc_pages)(struct device *dev, size_t size, | ||||
| 			dma_addr_t *dma_handle, enum dma_data_direction dir, | ||||
| 			gfp_t gfp); | ||||
| 	void (*free_pages)(struct device *dev, size_t size, struct page *vaddr, | ||||
| 			dma_addr_t dma_handle, enum dma_data_direction dir); | ||||
| 	int (*mmap)(struct device *, struct vm_area_struct *, | ||||
| 			  void *, dma_addr_t, size_t, | ||||
| 			  unsigned long attrs); | ||||
|  | @ -376,17 +376,14 @@ static inline unsigned long dma_get_merge_boundary(struct device *dev) | |||
| } | ||||
| #endif /* CONFIG_HAS_DMA */ | ||||
| 
 | ||||
| static inline void *dma_alloc_noncoherent(struct device *dev, size_t size, | ||||
| 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp) | ||||
| { | ||||
| 	return dma_alloc_attrs(dev, size, dma_handle, gfp, | ||||
| 			DMA_ATTR_NON_CONSISTENT); | ||||
| } | ||||
| static inline void dma_free_noncoherent(struct device *dev, size_t size, | ||||
| 		void *vaddr, dma_addr_t dma_handle, enum dma_data_direction dir) | ||||
| { | ||||
| 	dma_free_attrs(dev, size, vaddr, dma_handle, DMA_ATTR_NON_CONSISTENT); | ||||
| } | ||||
| struct page *dma_alloc_pages(struct device *dev, size_t size, | ||||
| 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp); | ||||
| void dma_free_pages(struct device *dev, size_t size, struct page *page, | ||||
| 		dma_addr_t dma_handle, enum dma_data_direction dir); | ||||
| void *dma_alloc_noncoherent(struct device *dev, size_t size, | ||||
| 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp); | ||||
| void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr, | ||||
| 		dma_addr_t dma_handle, enum dma_data_direction dir); | ||||
| 
 | ||||
| static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr, | ||||
| 		size_t size, enum dma_data_direction dir, unsigned long attrs) | ||||
|  | @ -512,7 +509,10 @@ static inline void dma_sync_sgtable_for_device(struct device *dev, | |||
| extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, | ||||
| 		void *cpu_addr, dma_addr_t dma_addr, size_t size, | ||||
| 		unsigned long attrs); | ||||
| 
 | ||||
| struct page *dma_common_alloc_pages(struct device *dev, size_t size, | ||||
| 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp); | ||||
| void dma_common_free_pages(struct device *dev, size_t size, struct page *vaddr, | ||||
| 		dma_addr_t dma_handle, enum dma_data_direction dir); | ||||
| struct page **dma_common_find_pages(void *cpu_addr); | ||||
| void *dma_common_contiguous_remap(struct page *page, size_t size, | ||||
| 			pgprot_t prot, const void *caller); | ||||
|  |  | |||
|  | @ -31,9 +31,6 @@ static __always_inline bool dma_alloc_need_uncached(struct device *dev, | |||
| 		return false; | ||||
| 	if (attrs & DMA_ATTR_NO_KERNEL_MAPPING) | ||||
| 		return false; | ||||
| 	if (IS_ENABLED(CONFIG_DMA_NONCOHERENT_CACHE_SYNC) && | ||||
| 	    (attrs & DMA_ATTR_NON_CONSISTENT)) | ||||
| 		return false; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| /*
 | ||||
|  * Copyright (C) 2018 Christoph Hellwig. | ||||
|  * Copyright (C) 2018-2020 Christoph Hellwig. | ||||
|  * | ||||
|  * DMA operations that map physical memory directly without using an IOMMU. | ||||
|  */ | ||||
|  | @ -292,6 +292,56 @@ void dma_direct_free(struct device *dev, size_t size, | |||
| 	dma_free_contiguous(dev, dma_direct_to_page(dev, dma_addr), size); | ||||
| } | ||||
| 
 | ||||
| struct page *dma_direct_alloc_pages(struct device *dev, size_t size, | ||||
| 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp) | ||||
| { | ||||
| 	struct page *page; | ||||
| 	void *ret; | ||||
| 
 | ||||
| 	if (dma_should_alloc_from_pool(dev, gfp, 0)) { | ||||
| 		page = dma_alloc_from_pool(dev, size, &ret, gfp, | ||||
| 				dma_coherent_ok); | ||||
| 		if (!page) | ||||
| 			return NULL; | ||||
| 		goto done; | ||||
| 	} | ||||
| 
 | ||||
| 	page = __dma_direct_alloc_pages(dev, size, gfp); | ||||
| 	if (!page) | ||||
| 		return NULL; | ||||
| 	ret = page_address(page); | ||||
| 	if (force_dma_unencrypted(dev)) { | ||||
| 		if (set_memory_decrypted((unsigned long)ret, | ||||
| 				1 << get_order(size))) | ||||
| 			goto out_free_pages; | ||||
| 	} | ||||
| 	memset(ret, 0, size); | ||||
| done: | ||||
| 	*dma_handle = phys_to_dma_direct(dev, page_to_phys(page)); | ||||
| 	return page; | ||||
| out_free_pages: | ||||
| 	dma_free_contiguous(dev, page, size); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| void dma_direct_free_pages(struct device *dev, size_t size, | ||||
| 		struct page *page, dma_addr_t dma_addr, | ||||
| 		enum dma_data_direction dir) | ||||
| { | ||||
| 	unsigned int page_order = get_order(size); | ||||
| 	void *vaddr = page_address(page); | ||||
| 
 | ||||
| 	/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */ | ||||
| 	if (dma_should_free_from_pool(dev, 0) && | ||||
| 	    dma_free_from_pool(dev, vaddr, size)) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (force_dma_unencrypted(dev)) | ||||
| 		set_memory_encrypted((unsigned long)vaddr, 1 << page_order); | ||||
| 
 | ||||
| 	dma_free_contiguous(dev, page, size); | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ | ||||
|     defined(CONFIG_SWIOTLB) | ||||
| void dma_direct_sync_sg_for_device(struct device *dev, | ||||
|  |  | |||
|  | @ -341,9 +341,7 @@ pgprot_t dma_pgprot(struct device *dev, pgprot_t prot, unsigned long attrs) | |||
| { | ||||
| 	if (force_dma_unencrypted(dev)) | ||||
| 		prot = pgprot_decrypted(prot); | ||||
| 	if (dev_is_dma_coherent(dev) || | ||||
| 	    (IS_ENABLED(CONFIG_DMA_NONCOHERENT_CACHE_SYNC) && | ||||
|              (attrs & DMA_ATTR_NON_CONSISTENT))) | ||||
| 	if (dev_is_dma_coherent(dev)) | ||||
| 		return prot; | ||||
| #ifdef CONFIG_ARCH_HAS_DMA_WRITE_COMBINE | ||||
| 	if (attrs & DMA_ATTR_WRITE_COMBINE) | ||||
|  | @ -472,6 +470,65 @@ void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr, | |||
| } | ||||
| EXPORT_SYMBOL(dma_free_attrs); | ||||
| 
 | ||||
| struct page *dma_alloc_pages(struct device *dev, size_t size, | ||||
| 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp) | ||||
| { | ||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| 	struct page *page; | ||||
| 
 | ||||
| 	if (WARN_ON_ONCE(!dev->coherent_dma_mask)) | ||||
| 		return NULL; | ||||
| 	if (WARN_ON_ONCE(gfp & (__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM))) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	size = PAGE_ALIGN(size); | ||||
| 	if (dma_alloc_direct(dev, ops)) | ||||
| 		page = dma_direct_alloc_pages(dev, size, dma_handle, dir, gfp); | ||||
| 	else if (ops->alloc_pages) | ||||
| 		page = ops->alloc_pages(dev, size, dma_handle, dir, gfp); | ||||
| 	else | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	debug_dma_map_page(dev, page, 0, size, dir, *dma_handle); | ||||
| 
 | ||||
| 	return page; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(dma_alloc_pages); | ||||
| 
 | ||||
| void dma_free_pages(struct device *dev, size_t size, struct page *page, | ||||
| 		dma_addr_t dma_handle, enum dma_data_direction dir) | ||||
| { | ||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| 
 | ||||
| 	size = PAGE_ALIGN(size); | ||||
| 	debug_dma_unmap_page(dev, dma_handle, size, dir); | ||||
| 
 | ||||
| 	if (dma_alloc_direct(dev, ops)) | ||||
| 		dma_direct_free_pages(dev, size, page, dma_handle, dir); | ||||
| 	else if (ops->free_pages) | ||||
| 		ops->free_pages(dev, size, page, dma_handle, dir); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(dma_free_pages); | ||||
| 
 | ||||
| void *dma_alloc_noncoherent(struct device *dev, size_t size, | ||||
| 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp) | ||||
| { | ||||
| 	struct page *page; | ||||
| 
 | ||||
| 	page = dma_alloc_pages(dev, size, dma_handle, dir, gfp); | ||||
| 	if (!page) | ||||
| 		return NULL; | ||||
| 	return page_address(page); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(dma_alloc_noncoherent); | ||||
| 
 | ||||
| void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr, | ||||
| 		dma_addr_t dma_handle, enum dma_data_direction dir) | ||||
| { | ||||
| 	dma_free_pages(dev, size, virt_to_page(vaddr), dma_handle, dir); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(dma_free_noncoherent); | ||||
| 
 | ||||
| int dma_supported(struct device *dev, u64 mask) | ||||
| { | ||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
|  * Helpers for DMA ops implementations.  These generally rely on the fact that | ||||
|  * the allocated memory contains normal pages in the direct kernel mapping. | ||||
|  */ | ||||
| #include <linux/dma-contiguous.h> | ||||
| #include <linux/dma-noncoherent.h> | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -49,3 +50,37 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, | |||
| 	return -ENXIO; | ||||
| #endif /* CONFIG_MMU */ | ||||
| } | ||||
| 
 | ||||
| struct page *dma_common_alloc_pages(struct device *dev, size_t size, | ||||
| 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp) | ||||
| { | ||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| 	struct page *page; | ||||
| 
 | ||||
| 	page = dma_alloc_contiguous(dev, size, gfp); | ||||
| 	if (!page) | ||||
| 		page = alloc_pages_node(dev_to_node(dev), gfp, get_order(size)); | ||||
| 	if (!page) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	*dma_handle = ops->map_page(dev, page, 0, size, dir, | ||||
| 				    DMA_ATTR_SKIP_CPU_SYNC); | ||||
| 	if (*dma_handle == DMA_MAPPING_ERROR) { | ||||
| 		dma_free_contiguous(dev, page, size); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	memset(page_address(page), 0, size); | ||||
| 	return page; | ||||
| } | ||||
| 
 | ||||
| void dma_common_free_pages(struct device *dev, size_t size, struct page *page, | ||||
| 		dma_addr_t dma_handle, enum dma_data_direction dir) | ||||
| { | ||||
| 	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| 
 | ||||
| 	if (ops->unmap_page) | ||||
| 		ops->unmap_page(dev, dma_handle, size, dir, | ||||
| 				DMA_ATTR_SKIP_CPU_SYNC); | ||||
| 	dma_free_contiguous(dev, page, size); | ||||
| } | ||||
|  |  | |||
|  | @ -55,5 +55,7 @@ const struct dma_map_ops dma_virt_ops = { | |||
| 	.free			= dma_virt_free, | ||||
| 	.map_page		= dma_virt_map_page, | ||||
| 	.map_sg			= dma_virt_map_sg, | ||||
| 	.alloc_pages		= dma_common_alloc_pages, | ||||
| 	.free_pages		= dma_common_free_pages, | ||||
| }; | ||||
| EXPORT_SYMBOL(dma_virt_ops); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Christoph Hellwig
						Christoph Hellwig