Merge tag 'dma-mapping-5.10' of git://git.infradead.org/users/hch/dma-mapping
Pull dma-mapping updates from Christoph Hellwig: - rework the non-coherent DMA allocator - move private definitions out of <linux/dma-mapping.h> - lower CMA_ALIGNMENT (Paul Cercueil) - remove the omap1 dma address translation in favor of the common code - make dma-direct aware of multiple dma offset ranges (Jim Quinlan) - support per-node DMA CMA areas (Barry Song) - increase the default seg boundary limit (Nicolin Chen) - misc fixes (Robin Murphy, Thomas Tai, Xu Wang) - various cleanups * tag 'dma-mapping-5.10' of git://git.infradead.org/users/hch/dma-mapping: (63 commits) ARM/ixp4xx: add a missing include of dma-map-ops.h dma-direct: simplify the DMA_ATTR_NO_KERNEL_MAPPING handling dma-direct: factor out a dma_direct_alloc_from_pool helper dma-direct check for highmem pages in dma_direct_alloc_pages dma-mapping: merge <linux/dma-noncoherent.h> into <linux/dma-map-ops.h> dma-mapping: move large parts of <linux/dma-direct.h> to kernel/dma dma-mapping: move dma-debug.h to kernel/dma/ dma-mapping: remove <asm/dma-contiguous.h> dma-mapping: merge <linux/dma-contiguous.h> into <linux/dma-map-ops.h> dma-contiguous: remove dma_contiguous_set_default dma-contiguous: remove dev_set_cma_area dma-contiguous: remove dma_declare_contiguous dma-mapping: split <linux/dma-mapping.h> cma: decrease CMA_ALIGNMENT lower limit to 2 firewire-ohci: use dma_alloc_pages dma-iommu: implement ->alloc_noncoherent dma-mapping: add new {alloc,free}_noncoherent dma_map_ops methods dma-mapping: add a new dma_alloc_pages API dma-mapping: remove dma_cache_sync 53c700: convert to dma_alloc_noncoherent ...
This commit is contained in:
@@ -5,6 +5,34 @@
|
||||
* Written by:
|
||||
* Marek Szyprowski <m.szyprowski@samsung.com>
|
||||
* Michal Nazarewicz <mina86@mina86.com>
|
||||
*
|
||||
* Contiguous Memory Allocator
|
||||
*
|
||||
* The Contiguous Memory Allocator (CMA) makes it possible to
|
||||
* allocate big contiguous chunks of memory after the system has
|
||||
* booted.
|
||||
*
|
||||
* Why is it needed?
|
||||
*
|
||||
* Various devices on embedded systems have no scatter-getter and/or
|
||||
* IO map support and require contiguous blocks of memory to
|
||||
* operate. They include devices such as cameras, hardware video
|
||||
* coders, etc.
|
||||
*
|
||||
* Such devices often require big memory buffers (a full HD frame
|
||||
* is, for instance, more then 2 mega pixels large, i.e. more than 6
|
||||
* MB of memory), which makes mechanisms such as kmalloc() or
|
||||
* alloc_page() ineffective.
|
||||
*
|
||||
* At the same time, a solution where a big memory region is
|
||||
* reserved for a device is suboptimal since often more memory is
|
||||
* reserved then strictly required and, moreover, the memory is
|
||||
* inaccessible to page system even if device drivers don't use it.
|
||||
*
|
||||
* CMA tries to solve this issue by operating on memory regions
|
||||
* where only movable pages can be allocated from. This way, kernel
|
||||
* can use the memory for pagecache and when device driver requests
|
||||
* it, allocated pages can be migrated.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "cma: " fmt
|
||||
@@ -16,12 +44,11 @@
|
||||
#endif
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/dma-contiguous.h>
|
||||
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/cma.h>
|
||||
|
||||
#ifdef CONFIG_CMA_SIZE_MBYTES
|
||||
@@ -69,6 +96,19 @@ static int __init early_cma(char *p)
|
||||
}
|
||||
early_param("cma", early_cma);
|
||||
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
|
||||
static struct cma *dma_contiguous_pernuma_area[MAX_NUMNODES];
|
||||
static phys_addr_t pernuma_size_bytes __initdata;
|
||||
|
||||
static int __init early_cma_pernuma(char *p)
|
||||
{
|
||||
pernuma_size_bytes = memparse(p, &p);
|
||||
return 0;
|
||||
}
|
||||
early_param("cma_pernuma", early_cma_pernuma);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CMA_SIZE_PERCENTAGE
|
||||
|
||||
static phys_addr_t __init __maybe_unused cma_early_percent_memory(void)
|
||||
@@ -87,6 +127,34 @@ static inline __maybe_unused phys_addr_t cma_early_percent_memory(void)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
void __init dma_pernuma_cma_reserve(void)
|
||||
{
|
||||
int nid;
|
||||
|
||||
if (!pernuma_size_bytes)
|
||||
return;
|
||||
|
||||
for_each_online_node(nid) {
|
||||
int ret;
|
||||
char name[CMA_MAX_NAME];
|
||||
struct cma **cma = &dma_contiguous_pernuma_area[nid];
|
||||
|
||||
snprintf(name, sizeof(name), "pernuma%d", nid);
|
||||
ret = cma_declare_contiguous_nid(0, pernuma_size_bytes, 0, 0,
|
||||
0, false, name, cma, nid);
|
||||
if (ret) {
|
||||
pr_warn("%s: reservation failed: err %d, node %d", __func__,
|
||||
ret, nid);
|
||||
continue;
|
||||
}
|
||||
|
||||
pr_debug("%s: reserved %llu MiB on node %d\n", __func__,
|
||||
(unsigned long long)pernuma_size_bytes / SZ_1M, nid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* dma_contiguous_reserve() - reserve area(s) for contiguous memory handling
|
||||
* @limit: End address of the reserved memory (optional, 0 for any).
|
||||
@@ -134,6 +202,11 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
|
||||
}
|
||||
}
|
||||
|
||||
void __weak
|
||||
dma_contiguous_early_fixup(phys_addr_t base, unsigned long size)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_contiguous_reserve_area() - reserve custom contiguous area
|
||||
* @size: Size of the reserved area (in bytes),
|
||||
@@ -219,23 +292,44 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
|
||||
* @size: Requested allocation size.
|
||||
* @gfp: Allocation flags.
|
||||
*
|
||||
* This function allocates contiguous memory buffer for specified device. It
|
||||
* tries to use device specific contiguous memory area if available, or the
|
||||
* default global one.
|
||||
* tries to use device specific contiguous memory area if available, or it
|
||||
* tries to use per-numa cma, if the allocation fails, it will fallback to
|
||||
* try default global one.
|
||||
*
|
||||
* Note that it byapss one-page size of allocations from the global area as
|
||||
* the addresses within one page are always contiguous, so there is no need
|
||||
* to waste CMA pages for that kind; it also helps reduce fragmentations.
|
||||
* Note that it bypass one-page size of allocations from the per-numa and
|
||||
* global area as the addresses within one page are always contiguous, so
|
||||
* there is no need to waste CMA pages for that kind; it also helps reduce
|
||||
* fragmentations.
|
||||
*/
|
||||
struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
|
||||
{
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
int nid = dev_to_node(dev);
|
||||
#endif
|
||||
|
||||
/* CMA can be used only in the context which permits sleeping */
|
||||
if (!gfpflags_allow_blocking(gfp))
|
||||
return NULL;
|
||||
if (dev->cma_area)
|
||||
return cma_alloc_aligned(dev->cma_area, size, gfp);
|
||||
if (size <= PAGE_SIZE || !dma_contiguous_default_area)
|
||||
if (size <= PAGE_SIZE)
|
||||
return NULL;
|
||||
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
if (nid != NUMA_NO_NODE && !(gfp & (GFP_DMA | GFP_DMA32))) {
|
||||
struct cma *cma = dma_contiguous_pernuma_area[nid];
|
||||
struct page *page;
|
||||
|
||||
if (cma) {
|
||||
page = cma_alloc_aligned(cma, size, gfp);
|
||||
if (page)
|
||||
return page;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!dma_contiguous_default_area)
|
||||
return NULL;
|
||||
|
||||
return cma_alloc_aligned(dma_contiguous_default_area, size, gfp);
|
||||
}
|
||||
|
||||
@@ -252,9 +346,27 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
|
||||
*/
|
||||
void dma_free_contiguous(struct device *dev, struct page *page, size_t size)
|
||||
{
|
||||
if (!cma_release(dev_get_cma_area(dev), page,
|
||||
PAGE_ALIGN(size) >> PAGE_SHIFT))
|
||||
__free_pages(page, get_order(size));
|
||||
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
|
||||
/* if dev has its own cma, free page from there */
|
||||
if (dev->cma_area) {
|
||||
if (cma_release(dev->cma_area, page, count))
|
||||
return;
|
||||
} else {
|
||||
/*
|
||||
* otherwise, page is from either per-numa cma or default cma
|
||||
*/
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
if (cma_release(dma_contiguous_pernuma_area[page_to_nid(page)],
|
||||
page, count))
|
||||
return;
|
||||
#endif
|
||||
if (cma_release(dma_contiguous_default_area, page, count))
|
||||
return;
|
||||
}
|
||||
|
||||
/* not in any cma, free from buddy */
|
||||
__free_pages(page, get_order(size));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -270,14 +382,14 @@ void dma_free_contiguous(struct device *dev, struct page *page, size_t size)
|
||||
|
||||
static int rmem_cma_device_init(struct reserved_mem *rmem, struct device *dev)
|
||||
{
|
||||
dev_set_cma_area(dev, rmem->priv);
|
||||
dev->cma_area = rmem->priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmem_cma_device_release(struct reserved_mem *rmem,
|
||||
struct device *dev)
|
||||
{
|
||||
dev_set_cma_area(dev, NULL);
|
||||
dev->cma_area = NULL;
|
||||
}
|
||||
|
||||
static const struct reserved_mem_ops rmem_cma_ops = {
|
||||
@@ -318,7 +430,7 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem)
|
||||
dma_contiguous_early_fixup(rmem->base, rmem->size);
|
||||
|
||||
if (default_cma)
|
||||
dma_contiguous_set_default(cma);
|
||||
dma_contiguous_default_area = cma;
|
||||
|
||||
rmem->ops = &rmem_cma_ops;
|
||||
rmem->priv = cma;
|
||||
|
Reference in New Issue
Block a user