aboutsummaryrefslogtreecommitdiffstats
path: root/mm/swapfile.c
diff options
context:
space:
mode:
authorMel Gorman <mgorman@suse.de>2012-01-10 18:07:15 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-01-10 19:30:42 -0500
commitf90ac3982a78d36f894824636beeef13361d7c59 (patch)
tree64bbe3b415bdfc151dc44f6b4c216c65351eb53c /mm/swapfile.c
parent938929f14cb595f43cd1a4e63e22d36cab1e4a1f (diff)
mm: avoid livelock on !__GFP_FS allocations
Colin Cross reported; Under the following conditions, __alloc_pages_slowpath can loop forever: gfp_mask & __GFP_WAIT is true gfp_mask & __GFP_FS is false reclaim and compaction make no progress order <= PAGE_ALLOC_COSTLY_ORDER These conditions happen very often during suspend and resume, when pm_restrict_gfp_mask() effectively converts all GFP_KERNEL allocations into __GFP_WAIT. The oom killer is not run because gfp_mask & __GFP_FS is false, but should_alloc_retry will always return true when order is less than PAGE_ALLOC_COSTLY_ORDER. In his fix, he avoided retrying the allocation if reclaim made no progress and __GFP_FS was not set. The problem is that this would result in GFP_NOIO allocations failing that previously succeeded which would be very unfortunate. The big difference between GFP_NOIO and suspend converting GFP_KERNEL to behave like GFP_NOIO is that normally flushers will be cleaning pages and kswapd reclaims pages allowing GFP_NOIO to succeed after a short delay. The same does not necessarily apply during suspend as the storage device may be suspended. This patch special cases the suspend case to fail the page allocation if reclaim cannot make progress and adds some documentation on how gfp_allowed_mask is currently used. Failing allocations like this may cause suspend to abort but that is better than a livelock. [mgorman@suse.de: Rework fix to be suspend specific] [rientjes@google.com: Move suspended device check to should_alloc_retry] Reported-by: Colin Cross <ccross@android.com> Signed-off-by: Mel Gorman <mgorman@suse.de> Acked-by: David Rientjes <rientjes@google.com> Cc: Minchan Kim <minchan.kim@gmail.com> Cc: Pekka Enberg <penberg@cs.helsinki.fi> Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Andrea Arcangeli <aarcange@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/swapfile.c')
-rw-r--r--mm/swapfile.c6
1 files changed, 3 insertions, 3 deletions
diff --git a/mm/swapfile.c b/mm/swapfile.c
index b1cd12060723..9520592d4231 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -667,10 +667,10 @@ int try_to_free_swap(struct page *page)
667 * original page might be freed under memory pressure, then 667 * original page might be freed under memory pressure, then
668 * later read back in from swap, now with the wrong data. 668 * later read back in from swap, now with the wrong data.
669 * 669 *
670 * Hibernation clears bits from gfp_allowed_mask to prevent 670 * Hibration suspends storage while it is writing the image
671 * memory reclaim from writing to disk, so check that here. 671 * to disk so check that here.
672 */ 672 */
673 if (!(gfp_allowed_mask & __GFP_IO)) 673 if (pm_suspended_storage())
674 return 0; 674 return 0;
675 675
676 delete_from_swap_cache(page); 676 delete_from_swap_cache(page);