| From 6715045ddc7472a22be5e49d4047d2d89b391f45 Mon Sep 17 00:00:00 2001 |
| From: "Rafael J. Wysocki" <rjw@sisk.pl> |
| Date: Sat, 11 Sep 2010 20:58:27 +0200 |
| Subject: PM / Hibernate: Avoid hitting OOM during preallocation of memory |
| |
| From: "Rafael J. Wysocki" <rjw@sisk.pl> |
| |
| commit 6715045ddc7472a22be5e49d4047d2d89b391f45 upstream. |
| |
| There is a problem in hibernate_preallocate_memory() that it calls |
| preallocate_image_memory() with an argument that may be greater than |
| the total number of available non-highmem memory pages. If that's |
| the case, the OOM condition is guaranteed to trigger, which in turn |
| can cause significant slowdown to occur during hibernation. |
| |
| To avoid that, make preallocate_image_memory() adjust its argument |
| before calling preallocate_image_pages(), so that the total number of |
| saveable non-highem pages left is not less than the minimum size of |
| a hibernation image. Change hibernate_preallocate_memory() to try to |
| allocate from highmem if the number of pages allocated by |
| preallocate_image_memory() is too low. |
| |
| Modify free_unnecessary_pages() to take all possible memory |
| allocation patterns into account. |
| |
| Reported-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> |
| Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> |
| Tested-by: M. Vefa Bicakci <bicave@superonline.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| kernel/power/snapshot.c | 85 ++++++++++++++++++++++++++++++++++++------------ |
| 1 file changed, 65 insertions(+), 20 deletions(-) |
| |
| --- a/kernel/power/snapshot.c |
| +++ b/kernel/power/snapshot.c |
| @@ -1120,9 +1120,19 @@ static unsigned long preallocate_image_p |
| return nr_alloc; |
| } |
| |
| -static unsigned long preallocate_image_memory(unsigned long nr_pages) |
| +static unsigned long preallocate_image_memory(unsigned long nr_pages, |
| + unsigned long avail_normal) |
| { |
| - return preallocate_image_pages(nr_pages, GFP_IMAGE); |
| + unsigned long alloc; |
| + |
| + if (avail_normal <= alloc_normal) |
| + return 0; |
| + |
| + alloc = avail_normal - alloc_normal; |
| + if (nr_pages < alloc) |
| + alloc = nr_pages; |
| + |
| + return preallocate_image_pages(alloc, GFP_IMAGE); |
| } |
| |
| #ifdef CONFIG_HIGHMEM |
| @@ -1168,15 +1178,22 @@ static inline unsigned long preallocate_ |
| */ |
| static void free_unnecessary_pages(void) |
| { |
| - unsigned long save_highmem, to_free_normal, to_free_highmem; |
| + unsigned long save, to_free_normal, to_free_highmem; |
| |
| - to_free_normal = alloc_normal - count_data_pages(); |
| - save_highmem = count_highmem_pages(); |
| - if (alloc_highmem > save_highmem) { |
| - to_free_highmem = alloc_highmem - save_highmem; |
| + save = count_data_pages(); |
| + if (alloc_normal >= save) { |
| + to_free_normal = alloc_normal - save; |
| + save = 0; |
| + } else { |
| + to_free_normal = 0; |
| + save -= alloc_normal; |
| + } |
| + save += count_highmem_pages(); |
| + if (alloc_highmem >= save) { |
| + to_free_highmem = alloc_highmem - save; |
| } else { |
| to_free_highmem = 0; |
| - to_free_normal -= save_highmem - alloc_highmem; |
| + to_free_normal -= save - alloc_highmem; |
| } |
| |
| memory_bm_position_reset(©_bm); |
| @@ -1257,7 +1274,7 @@ int hibernate_preallocate_memory(void) |
| { |
| struct zone *zone; |
| unsigned long saveable, size, max_size, count, highmem, pages = 0; |
| - unsigned long alloc, save_highmem, pages_highmem; |
| + unsigned long alloc, save_highmem, pages_highmem, avail_normal; |
| struct timeval start, stop; |
| int error; |
| |
| @@ -1294,6 +1311,7 @@ int hibernate_preallocate_memory(void) |
| else |
| count += zone_page_state(zone, NR_FREE_PAGES); |
| } |
| + avail_normal = count; |
| count += highmem; |
| count -= totalreserve_pages; |
| |
| @@ -1308,12 +1326,21 @@ int hibernate_preallocate_memory(void) |
| */ |
| if (size >= saveable) { |
| pages = preallocate_image_highmem(save_highmem); |
| - pages += preallocate_image_memory(saveable - pages); |
| + pages += preallocate_image_memory(saveable - pages, avail_normal); |
| goto out; |
| } |
| |
| /* Estimate the minimum size of the image. */ |
| pages = minimum_image_size(saveable); |
| + /* |
| + * To avoid excessive pressure on the normal zone, leave room in it to |
| + * accommodate an image of the minimum size (unless it's already too |
| + * small, in which case don't preallocate pages from it at all). |
| + */ |
| + if (avail_normal > pages) |
| + avail_normal -= pages; |
| + else |
| + avail_normal = 0; |
| if (size < pages) |
| size = min_t(unsigned long, pages, max_size); |
| |
| @@ -1334,16 +1361,34 @@ int hibernate_preallocate_memory(void) |
| */ |
| pages_highmem = preallocate_image_highmem(highmem / 2); |
| alloc = (count - max_size) - pages_highmem; |
| - pages = preallocate_image_memory(alloc); |
| - if (pages < alloc) |
| - goto err_out; |
| - size = max_size - size; |
| - alloc = size; |
| - size = preallocate_highmem_fraction(size, highmem, count); |
| - pages_highmem += size; |
| - alloc -= size; |
| - pages += preallocate_image_memory(alloc); |
| - pages += pages_highmem; |
| + pages = preallocate_image_memory(alloc, avail_normal); |
| + if (pages < alloc) { |
| + /* We have exhausted non-highmem pages, try highmem. */ |
| + alloc -= pages; |
| + pages += pages_highmem; |
| + pages_highmem = preallocate_image_highmem(alloc); |
| + if (pages_highmem < alloc) |
| + goto err_out; |
| + pages += pages_highmem; |
| + /* |
| + * size is the desired number of saveable pages to leave in |
| + * memory, so try to preallocate (all memory - size) pages. |
| + */ |
| + alloc = (count - pages) - size; |
| + pages += preallocate_image_highmem(alloc); |
| + } else { |
| + /* |
| + * There are approximately max_size saveable pages at this point |
| + * and we want to reduce this number down to size. |
| + */ |
| + alloc = max_size - size; |
| + size = preallocate_highmem_fraction(alloc, highmem, count); |
| + pages_highmem += size; |
| + alloc -= size; |
| + size = preallocate_image_memory(alloc, avail_normal); |
| + pages_highmem += preallocate_image_highmem(alloc - size); |
| + pages += pages_highmem + size; |
| + } |
| |
| /* |
| * We only need as many page frames for the image as there are saveable |