Commit 4e390b2b authored by Michal Hocko's avatar Michal Hocko Committed by Linus Torvalds

Revert "mm, mempool: only set __GFP_NOMEMALLOC if there are free elements"

This reverts commit f9054c70 ("mm, mempool: only set __GFP_NOMEMALLOC
if there are free elements").

There has been a report about OOM killer invoked when swapping out to a
dm-crypt device.  The primary reason seems to be that the swapout out IO
managed to completely deplete memory reserves.  Ondrej was able to
bisect and explained the issue by pointing to f9054c70 ("mm,
mempool: only set __GFP_NOMEMALLOC if there are free elements").

The reason is that the swapout path is not throttled properly because
the md-raid layer needs to allocate from the generic_make_request path
which means it allocates from the PF_MEMALLOC context.  dm layer uses
mempool_alloc in order to guarantee a forward progress which used to
inhibit access to memory reserves when using page allocator.  This has
changed by f9054c70 ("mm, mempool: only set __GFP_NOMEMALLOC if
there are free elements") which has dropped the __GFP_NOMEMALLOC
protection when the memory pool is depleted.

If we are running out of memory and the only way forward to free memory
is to perform swapout we just keep consuming memory reserves rather than
throttling the mempool allocations and allowing the pending IO to
complete up to a moment when the memory is depleted completely and there
is no way forward but invoking the OOM killer.  This is less than
optimal.

The original intention of f9054c70 was to help with the OOM
situations where the oom victim depends on mempool allocation to make a
forward progress.  David has mentioned the following backtrace:

  schedule
  schedule_timeout
  io_schedule_timeout
  mempool_alloc
  __split_and_process_bio
  dm_request
  generic_make_request
  submit_bio
  mpage_readpages
  ext4_readpages
  __do_page_cache_readahead
  ra_submit
  filemap_fault
  handle_mm_fault
  __do_page_fault
  do_page_fault
  page_fault

We do not know more about why the mempool is depleted without being
replenished in time, though.  In any case the dm layer shouldn't depend
on any allocations outside of the dedicated pools so a forward progress
should be guaranteed.  If this is not the case then the dm should be
fixed rather than papering over the problem and postponing it to later
by accessing more memory reserves.

mempools are a mechanism to maintain dedicated memory reserves to
guaratee forward progress.  Allowing them an unbounded access to the
page allocator memory reserves is going against the whole purpose of
this mechanism.

Bisected by Ondrej Kozina.

[akpm@linux-foundation.org: coding-style fixes]
Link: http://lkml.kernel.org/r/20160721145309.GR26379@dhcp22.suse.czSigned-off-by: default avatarMichal Hocko <mhocko@suse.com>
Reported-by: default avatarOndrej Kozina <okozina@redhat.com>
Reviewed-by: default avatarJohannes Weiner <hannes@cmpxchg.org>
Acked-by: default avatarNeilBrown <neilb@suse.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Mikulas Patocka <mpatocka@redhat.com>
Cc: Ondrej Kozina <okozina@redhat.com>
Cc: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Cc: Mel Gorman <mgorman@suse.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 1d2047fe
...@@ -306,7 +306,7 @@ EXPORT_SYMBOL(mempool_resize); ...@@ -306,7 +306,7 @@ EXPORT_SYMBOL(mempool_resize);
* returns NULL. Note that due to preallocation, this function * returns NULL. Note that due to preallocation, this function
* *never* fails when called from process contexts. (it might * *never* fails when called from process contexts. (it might
* fail if called from an IRQ context.) * fail if called from an IRQ context.)
* Note: neither __GFP_NOMEMALLOC nor __GFP_ZERO are supported. * Note: using __GFP_ZERO is not supported.
*/ */
void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask) void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
{ {
...@@ -315,27 +315,16 @@ void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask) ...@@ -315,27 +315,16 @@ void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
wait_queue_t wait; wait_queue_t wait;
gfp_t gfp_temp; 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); VM_WARN_ON_ONCE(gfp_mask & __GFP_ZERO);
might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM); 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_NORETRY; /* don't loop in __alloc_pages */
gfp_mask |= __GFP_NOWARN; /* failures are OK */ gfp_mask |= __GFP_NOWARN; /* failures are OK */
gfp_temp = gfp_mask & ~(__GFP_DIRECT_RECLAIM|__GFP_IO); gfp_temp = gfp_mask & ~(__GFP_DIRECT_RECLAIM|__GFP_IO);
repeat_alloc: 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); element = pool->alloc(gfp_temp, pool->pool_data);
if (likely(element != NULL)) if (likely(element != NULL))
...@@ -359,12 +348,11 @@ void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask) ...@@ -359,12 +348,11 @@ void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
* We use gfp mask w/o direct reclaim or IO for the first round. If * 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. * alloc failed with that and @pool was empty, retry immediately.
*/ */
if ((gfp_temp & ~__GFP_NOMEMALLOC) != gfp_mask) { if (gfp_temp != gfp_mask) {
spin_unlock_irqrestore(&pool->lock, flags); spin_unlock_irqrestore(&pool->lock, flags);
gfp_temp = gfp_mask; gfp_temp = gfp_mask;
goto repeat_alloc; goto repeat_alloc;
} }
gfp_temp = gfp_mask;
/* We must not sleep if !__GFP_DIRECT_RECLAIM */ /* We must not sleep if !__GFP_DIRECT_RECLAIM */
if (!(gfp_mask & __GFP_DIRECT_RECLAIM)) { if (!(gfp_mask & __GFP_DIRECT_RECLAIM)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment