diff --git a/mm/cma.c b/mm/cma.c index 8a2d1dc68b85..878dbccee619 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include "cma.h" @@ -420,6 +422,8 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align, size_t i; struct page *page = NULL; int ret = -ENOMEM; + int num_attempts = 0; + int max_retries = 5; if (!cma || !cma->count || !cma->bitmap) return NULL; @@ -444,8 +448,28 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align, bitmap_maxno, start, bitmap_count, mask, offset); if (bitmap_no >= bitmap_maxno) { - mutex_unlock(&cma->lock); - break; + if ((num_attempts < max_retries) && (ret == -EBUSY)) { + mutex_unlock(&cma->lock); + + if (fatal_signal_pending(current)) + break; + + /* + * Page may be momentarily pinned by some other + * process which has been scheduled out, e.g. + * in exit path, during unmap call, or process + * fork and so cannot be freed there. Sleep + * for 100ms and retry the allocation. + */ + start = 0; + ret = -ENOMEM; + schedule_timeout_killable(msecs_to_jiffies(100)); + num_attempts++; + continue; + } else { + mutex_unlock(&cma->lock); + break; + } } bitmap_set(cma->bitmap, bitmap_no, bitmap_count); /*