diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2009-06-16 18:32:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-16 22:47:40 -0400 |
commit | 7f33d49a2ed546e01f7b1d0607661810f2421859 (patch) | |
tree | 8b05ac7ec2cd123efb266ecaa1bf0bd1487158f8 | |
parent | 75927af8bcb940dad4fe281713d526cb520869ff (diff) |
mm, PM/Freezer: Disable OOM killer when tasks are frozen
Currently, the following scenario appears to be possible in theory:
* Tasks are frozen for hibernation or suspend.
* Free pages are almost exhausted.
* Certain piece of code in the suspend code path attempts to allocate
some memory using GFP_KERNEL and allocation order less than or
equal to PAGE_ALLOC_COSTLY_ORDER.
* __alloc_pages_internal() cannot find a free page so it invokes the
OOM killer.
* The OOM killer attempts to kill a task, but the task is frozen, so
it doesn't die immediately.
* __alloc_pages_internal() jumps to 'restart', unsuccessfully tries
to find a free page and invokes the OOM killer.
* No progress can be made.
Although it is now hard to trigger during hibernation due to the memory
shrinking carried out by the hibernation code, it is theoretically
possible to trigger during suspend after the memory shrinking has been
removed from that code path. Moreover, since memory allocations are
going to be used for the hibernation memory shrinking, it will be even
more likely to happen during hibernation.
To prevent it from happening, introduce the oom_killer_disabled switch
that will cause __alloc_pages_internal() to fail in the situations in
which the OOM killer would have been called and make the freezer set
this switch after tasks have been successfully frozen.
[akpm@linux-foundation.org: be nicer to the namespace]
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Fengguang Wu <fengguang.wu@gmail.com>
Cc: David Rientjes <rientjes@google.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Cc: Mel Gorman <mel@csn.ul.ie>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/gfp.h | 12 | ||||
-rw-r--r-- | kernel/power/process.c | 5 | ||||
-rw-r--r-- | mm/page_alloc.c | 4 |
3 files changed, 21 insertions, 0 deletions
diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 4efa33088a82..06b7e8cc80ac 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h | |||
@@ -243,4 +243,16 @@ void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp); | |||
243 | void drain_all_pages(void); | 243 | void drain_all_pages(void); |
244 | void drain_local_pages(void *dummy); | 244 | void drain_local_pages(void *dummy); |
245 | 245 | ||
246 | extern bool oom_killer_disabled; | ||
247 | |||
248 | static inline void oom_killer_disable(void) | ||
249 | { | ||
250 | oom_killer_disabled = true; | ||
251 | } | ||
252 | |||
253 | static inline void oom_killer_enable(void) | ||
254 | { | ||
255 | oom_killer_disabled = false; | ||
256 | } | ||
257 | |||
246 | #endif /* __LINUX_GFP_H */ | 258 | #endif /* __LINUX_GFP_H */ |
diff --git a/kernel/power/process.c b/kernel/power/process.c index ca634019497a..da2072d73811 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c | |||
@@ -117,9 +117,12 @@ int freeze_processes(void) | |||
117 | if (error) | 117 | if (error) |
118 | goto Exit; | 118 | goto Exit; |
119 | printk("done."); | 119 | printk("done."); |
120 | |||
121 | oom_killer_disable(); | ||
120 | Exit: | 122 | Exit: |
121 | BUG_ON(in_atomic()); | 123 | BUG_ON(in_atomic()); |
122 | printk("\n"); | 124 | printk("\n"); |
125 | |||
123 | return error; | 126 | return error; |
124 | } | 127 | } |
125 | 128 | ||
@@ -145,6 +148,8 @@ static void thaw_tasks(bool nosig_only) | |||
145 | 148 | ||
146 | void thaw_processes(void) | 149 | void thaw_processes(void) |
147 | { | 150 | { |
151 | oom_killer_enable(); | ||
152 | |||
148 | printk("Restarting tasks ... "); | 153 | printk("Restarting tasks ... "); |
149 | thaw_tasks(true); | 154 | thaw_tasks(true); |
150 | thaw_tasks(false); | 155 | thaw_tasks(false); |
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 61290ea721c8..5b09488d0f55 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
@@ -178,6 +178,8 @@ static void set_pageblock_migratetype(struct page *page, int migratetype) | |||
178 | PB_migrate, PB_migrate_end); | 178 | PB_migrate, PB_migrate_end); |
179 | } | 179 | } |
180 | 180 | ||
181 | bool oom_killer_disabled __read_mostly; | ||
182 | |||
181 | #ifdef CONFIG_DEBUG_VM | 183 | #ifdef CONFIG_DEBUG_VM |
182 | static int page_outside_zone_boundaries(struct zone *zone, struct page *page) | 184 | static int page_outside_zone_boundaries(struct zone *zone, struct page *page) |
183 | { | 185 | { |
@@ -1769,6 +1771,8 @@ rebalance: | |||
1769 | */ | 1771 | */ |
1770 | if (!did_some_progress) { | 1772 | if (!did_some_progress) { |
1771 | if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) { | 1773 | if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) { |
1774 | if (oom_killer_disabled) | ||
1775 | goto nopage; | ||
1772 | page = __alloc_pages_may_oom(gfp_mask, order, | 1776 | page = __alloc_pages_may_oom(gfp_mask, order, |
1773 | zonelist, high_zoneidx, | 1777 | zonelist, high_zoneidx, |
1774 | nodemask, preferred_zone, | 1778 | nodemask, preferred_zone, |