Merge branch 'for-4.6-ns' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
[linux-2.6-block.git] / mm / mempool.c
index 7924f4f58a6d48ae5335fdedb1c2e2dd93a56d88..07c383ddbbab75be579e0422181bc4874a3e1c9a 100644 (file)
@@ -310,25 +310,36 @@ EXPORT_SYMBOL(mempool_resize);
  * returns NULL. Note that due to preallocation, this function
  * *never* fails when called from process contexts. (it might
  * fail if called from an IRQ context.)
- * Note: using __GFP_ZERO is not supported.
+ * Note: neither __GFP_NOMEMALLOC nor __GFP_ZERO are supported.
  */
-void * mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
+void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
 {
        void *element;
        unsigned long flags;
        wait_queue_t wait;
        gfp_t gfp_temp;
 
+       /* If oom killed, memory reserves are essential to prevent livelock */
+       VM_WARN_ON_ONCE(gfp_mask & __GFP_NOMEMALLOC);
+       /* No element size to zero on allocation */
        VM_WARN_ON_ONCE(gfp_mask & __GFP_ZERO);
+
        might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM);
 
-       gfp_mask |= __GFP_NOMEMALLOC;   /* don't allocate emergency reserves */
        gfp_mask |= __GFP_NORETRY;      /* don't loop in __alloc_pages */
        gfp_mask |= __GFP_NOWARN;       /* failures are OK */
 
        gfp_temp = gfp_mask & ~(__GFP_DIRECT_RECLAIM|__GFP_IO);
 
 repeat_alloc:
+       if (likely(pool->curr_nr)) {
+               /*
+                * Don't allocate from emergency reserves if there are
+                * elements available.  This check is racy, but it will
+                * be rechecked each loop.
+                */
+               gfp_temp |= __GFP_NOMEMALLOC;
+       }
 
        element = pool->alloc(gfp_temp, pool->pool_data);
        if (likely(element != NULL))
@@ -352,11 +363,12 @@ repeat_alloc:
         * We use gfp mask w/o direct reclaim or IO for the first round.  If
         * alloc failed with that and @pool was empty, retry immediately.
         */
-       if (gfp_temp != gfp_mask) {
+       if ((gfp_temp & ~__GFP_NOMEMALLOC) != gfp_mask) {
                spin_unlock_irqrestore(&pool->lock, flags);
                gfp_temp = gfp_mask;
                goto repeat_alloc;
        }
+       gfp_temp = gfp_mask;
 
        /* We must not sleep if !__GFP_DIRECT_RECLAIM */
        if (!(gfp_mask & __GFP_DIRECT_RECLAIM)) {