diff options
Diffstat (limited to 'mm/cma.c')
-rw-r--r-- | mm/cma.c | 33 |
1 files changed, 31 insertions, 2 deletions
@@ -35,6 +35,7 @@ #include <linux/cma.h> #include <linux/highmem.h> #include <linux/io.h> +#include <linux/delay.h> #include <trace/events/cma.h> #include "cma.h" @@ -131,6 +132,10 @@ static int __init cma_activate_area(struct cma *cma) spin_lock_init(&cma->mem_head_lock); #endif + if (!PageHighMem(pfn_to_page(cma->base_pfn))) + kmemleak_free_part(__va(cma->base_pfn << PAGE_SHIFT), + cma->count << PAGE_SHIFT); + return 0; err: @@ -367,6 +372,7 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align) unsigned long bitmap_maxno, bitmap_no, bitmap_count; struct page *page = NULL; int ret; + int retry_after_sleep = 0; if (!cma || !cma->count) return NULL; @@ -377,19 +383,40 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align) if (!count) return NULL; + trace_cma_alloc_start(count, align); + mask = cma_bitmap_aligned_mask(cma, align); offset = cma_bitmap_aligned_offset(cma, align); bitmap_maxno = cma_bitmap_maxno(cma); bitmap_count = cma_bitmap_pages_to_bits(cma, count); + if (bitmap_count > bitmap_maxno) + return NULL; + for (;;) { mutex_lock(&cma->lock); bitmap_no = bitmap_find_next_zero_area_off(cma->bitmap, bitmap_maxno, start, bitmap_count, mask, offset); if (bitmap_no >= bitmap_maxno) { - mutex_unlock(&cma->lock); - break; + if (retry_after_sleep < 2) { + start = 0; + /* + * Page may be momentarily pinned by some other + * process which has been scheduled out, eg. + * in exit path, during unmap call, or process + * fork and so cannot be freed there. Sleep + * for 100ms and retry twice to see if it has + * been freed later. + */ + mutex_unlock(&cma->lock); + msleep(100); + retry_after_sleep++; + continue; + } else { + mutex_unlock(&cma->lock); + break; + } } bitmap_set(cma->bitmap, bitmap_no, bitmap_count); /* @@ -414,6 +441,8 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align) pr_debug("%s(): memory range at %p is busy, retrying\n", __func__, pfn_to_page(pfn)); + + trace_cma_alloc_busy_retry(pfn, pfn_to_page(pfn), count, align); /* try again with a bit different memory target */ start = bitmap_no + mask + 1; } |