From 387870f2d6d679746020fa8e25ef786ff338dc98 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 7 Nov 2012 15:37:07 +0100 Subject: [PATCH 1/2] mm: dmapool: use provided gfp flags for all dma_alloc_coherent() calls dmapool always calls dma_alloc_coherent() with GFP_ATOMIC flag, regardless the flags provided by the caller. This causes excessive pruning of emergency memory pools without any good reason. Additionaly, on ARM architecture any driver which is using dmapools will sooner or later trigger the following error: "ERROR: 256 KiB atomic DMA coherent pool is too small! Please increase it with coherent_pool= kernel parameter!". Increasing the coherent pool size usually doesn't help much and only delays such error, because all GFP_ATOMIC DMA allocations are always served from the special, very limited memory pool. This patch changes the dmapool code to correctly use gfp flags provided by the dmapool caller. Reported-by: Soeren Moch Reported-by: Thomas Petazzoni Signed-off-by: Marek Szyprowski Tested-by: Andrew Lunn Tested-by: Soeren Moch Cc: stable@vger.kernel.org --- mm/dmapool.c | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/mm/dmapool.c b/mm/dmapool.c index c5ab33bca0a8..da1b0f0b8709 100644 --- a/mm/dmapool.c +++ b/mm/dmapool.c @@ -50,7 +50,6 @@ struct dma_pool { /* the pool */ size_t allocation; size_t boundary; char name[32]; - wait_queue_head_t waitq; struct list_head pools; }; @@ -62,8 +61,6 @@ struct dma_page { /* cacheable header for 'allocation' bytes */ unsigned int offset; }; -#define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000) - static DEFINE_MUTEX(pools_lock); static ssize_t @@ -172,7 +169,6 @@ struct dma_pool *dma_pool_create(const char *name, struct device *dev, retval->size = size; retval->boundary = boundary; retval->allocation = allocation; - init_waitqueue_head(&retval->waitq); if (dev) { int ret; @@ -227,7 +223,6 @@ static struct dma_page *pool_alloc_page(struct dma_pool *pool, gfp_t mem_flags) memset(page->vaddr, POOL_POISON_FREED, pool->allocation); #endif pool_initialise_page(pool, page); - list_add(&page->page_list, &pool->page_list); page->in_use = 0; page->offset = 0; } else { @@ -315,30 +310,21 @@ void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags, might_sleep_if(mem_flags & __GFP_WAIT); spin_lock_irqsave(&pool->lock, flags); - restart: list_for_each_entry(page, &pool->page_list, page_list) { if (page->offset < pool->allocation) goto ready; } - page = pool_alloc_page(pool, GFP_ATOMIC); - if (!page) { - if (mem_flags & __GFP_WAIT) { - DECLARE_WAITQUEUE(wait, current); - __set_current_state(TASK_UNINTERRUPTIBLE); - __add_wait_queue(&pool->waitq, &wait); - spin_unlock_irqrestore(&pool->lock, flags); + /* pool_alloc_page() might sleep, so temporarily drop &pool->lock */ + spin_unlock_irqrestore(&pool->lock, flags); - schedule_timeout(POOL_TIMEOUT_JIFFIES); + page = pool_alloc_page(pool, mem_flags); + if (!page) + return NULL; - spin_lock_irqsave(&pool->lock, flags); - __remove_wait_queue(&pool->waitq, &wait); - goto restart; - } - retval = NULL; - goto done; - } + spin_lock_irqsave(&pool->lock, flags); + list_add(&page->page_list, &pool->page_list); ready: page->in_use++; offset = page->offset; @@ -348,7 +334,6 @@ void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags, #ifdef DMAPOOL_DEBUG memset(retval, POOL_POISON_ALLOCATED, pool->size); #endif - done: spin_unlock_irqrestore(&pool->lock, flags); return retval; } @@ -435,8 +420,6 @@ void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma) page->in_use--; *(int *)vaddr = page->offset; page->offset = offset; - if (waitqueue_active(&pool->waitq)) - wake_up_locked(&pool->waitq); /* * Resist a temptation to do * if (!is_page_busy(page)) pool_free_page(pool, page); From 4009793e15d44469da1547a46ab129cc08ffa503 Mon Sep 17 00:00:00 2001 From: Vitaly Andrianov Date: Wed, 5 Dec 2012 09:29:25 -0500 Subject: [PATCH 2/2] drivers: cma: represent physical addresses as phys_addr_t This commit changes the CMA early initialization code to use phys_addr_t for representing physical addresses instead of unsigned long. Without this change, among other things, dma_declare_contiguous() simply discards any memory regions whose address is not representable as unsigned long. This is a problem on 32-bit PAE machines where unsigned long is 32-bit but physical address space is larger. Signed-off-by: Vitaly Andrianov Signed-off-by: Cyril Chemparathy Acked-by: Michal Nazarewicz Signed-off-by: Marek Szyprowski --- drivers/base/dma-contiguous.c | 24 ++++++++++-------------- include/linux/dma-contiguous.h | 4 ++-- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 612afcc5a938..0ca54421ce97 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -57,8 +57,8 @@ struct cma *dma_contiguous_default_area; * Users, who want to set the size of global CMA area for their system * should use cma= kernel parameter. */ -static const unsigned long size_bytes = CMA_SIZE_MBYTES * SZ_1M; -static long size_cmdline = -1; +static const phys_addr_t size_bytes = CMA_SIZE_MBYTES * SZ_1M; +static phys_addr_t size_cmdline = -1; static int __init early_cma(char *p) { @@ -70,7 +70,7 @@ early_param("cma", early_cma); #ifdef CONFIG_CMA_SIZE_PERCENTAGE -static unsigned long __init __maybe_unused cma_early_percent_memory(void) +static phys_addr_t __init __maybe_unused cma_early_percent_memory(void) { struct memblock_region *reg; unsigned long total_pages = 0; @@ -88,7 +88,7 @@ static unsigned long __init __maybe_unused cma_early_percent_memory(void) #else -static inline __maybe_unused unsigned long cma_early_percent_memory(void) +static inline __maybe_unused phys_addr_t cma_early_percent_memory(void) { return 0; } @@ -106,7 +106,7 @@ static inline __maybe_unused unsigned long cma_early_percent_memory(void) */ void __init dma_contiguous_reserve(phys_addr_t limit) { - unsigned long selected_size = 0; + phys_addr_t selected_size = 0; pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit); @@ -126,7 +126,7 @@ void __init dma_contiguous_reserve(phys_addr_t limit) if (selected_size) { pr_debug("%s: reserving %ld MiB for global area\n", __func__, - selected_size / SZ_1M); + (unsigned long)selected_size / SZ_1M); dma_declare_contiguous(NULL, selected_size, 0, limit); } @@ -227,11 +227,11 @@ core_initcall(cma_init_reserved_areas); * called by board specific code when early allocator (memblock or bootmem) * is still activate. */ -int __init dma_declare_contiguous(struct device *dev, unsigned long size, +int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, phys_addr_t base, phys_addr_t limit) { struct cma_reserved *r = &cma_reserved[cma_reserved_count]; - unsigned long alignment; + phys_addr_t alignment; pr_debug("%s(size %lx, base %08lx, limit %08lx)\n", __func__, (unsigned long)size, (unsigned long)base, @@ -268,10 +268,6 @@ int __init dma_declare_contiguous(struct device *dev, unsigned long size, if (!addr) { base = -ENOMEM; goto err; - } else if (addr + size > ~(unsigned long)0) { - memblock_free(addr, size); - base = -EINVAL; - goto err; } else { base = addr; } @@ -285,14 +281,14 @@ int __init dma_declare_contiguous(struct device *dev, unsigned long size, r->size = size; r->dev = dev; cma_reserved_count++; - pr_info("CMA: reserved %ld MiB at %08lx\n", size / SZ_1M, + pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M, (unsigned long)base); /* Architecture specific contiguous memory fixup. */ dma_contiguous_early_fixup(base, size); return 0; err: - pr_err("CMA: failed to reserve %ld MiB\n", size / SZ_1M); + pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M); return base; } diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h index 2f303e4b7ed3..01b5c84be828 100644 --- a/include/linux/dma-contiguous.h +++ b/include/linux/dma-contiguous.h @@ -68,7 +68,7 @@ struct device; extern struct cma *dma_contiguous_default_area; void dma_contiguous_reserve(phys_addr_t addr_limit); -int dma_declare_contiguous(struct device *dev, unsigned long size, +int dma_declare_contiguous(struct device *dev, phys_addr_t size, phys_addr_t base, phys_addr_t limit); struct page *dma_alloc_from_contiguous(struct device *dev, int count, @@ -83,7 +83,7 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages, static inline void dma_contiguous_reserve(phys_addr_t limit) { } static inline -int dma_declare_contiguous(struct device *dev, unsigned long size, +int dma_declare_contiguous(struct device *dev, phys_addr_t size, phys_addr_t base, phys_addr_t limit) { return -ENOSYS;