dma-mapping: add a new dma_alloc_pages API
commit efa70f2fdc
upstream.
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)
Signed-off-by: Chen Zhuo <sagazchen@tencent.com>
Signed-off-by: Xinghui Li <korantli@tencent.com>
This commit is contained in:
parent
fbacfa4030
commit
0764432a5a
|
@ -43,14 +43,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
|
||||
--------------------------
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -182,6 +182,8 @@ static void arm_dma_sync_single_for_device(struct device *dev,
|
|||
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,
|
||||
|
@ -209,6 +211,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,
|
||||
|
|
|
@ -681,6 +681,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)
|
||||
|
|
|
@ -1104,6 +1104,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,
|
||||
|
|
|
@ -1025,6 +1025,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
|
||||
|
|
|
@ -1077,6 +1077,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,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -79,6 +79,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);
|
||||
void *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs);
|
||||
void dma_direct_free_pages(struct device *dev, size_t size, void *cpu_addr,
|
||||
|
|
|
@ -31,11 +31,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.
|
||||
|
@ -84,6 +79,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);
|
||||
|
@ -388,6 +388,11 @@ static inline unsigned long dma_get_merge_boundary(struct device *dev)
|
|||
}
|
||||
#endif /* CONFIG_HAS_DMA */
|
||||
|
||||
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);
|
||||
|
||||
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 +517,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.
|
||||
*/
|
||||
|
@ -303,6 +303,56 @@ void dma_direct_free(struct device *dev, size_t size,
|
|||
dma_direct_free_pages(dev, size, cpu_addr, dma_addr, 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)
|
||||
{
|
||||
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,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/memblock.h> /* for max_pfn */
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gfp.h>
|
||||
|
@ -346,9 +347,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)
|
||||
|
@ -387,6 +386,40 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
|
|||
#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);
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_can_mmap - check if a given device supports dma_mmap_*
|
||||
* @dev: device to check
|
||||
|
@ -506,6 +539,46 @@ 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);
|
||||
|
||||
int dma_supported(struct device *dev, u64 mask)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
|
|
@ -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 New Issue