Per Olofsson wrote:

> Here is the final patch which fixes the bug:

Thanks, Pelle.  This is e9cbc5a6270b from the linux-next branch of
Rafael's linux-pm tree[1] and should be part of linux-next when
Stephen next rebuilds it.  It applies without change to the 3.2.y
tree, so I'm attaching a patch for the packaging repo that applies the
fix for convenience.

Hope that helps,
Jonathan

[1] git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Index: debian/changelog
===================================================================
--- debian/changelog    (revision 18944)
+++ debian/changelog    (working copy)
@@ -4,6 +4,10 @@
   * [ia64] Fix futex_atomic_cmpxchg_inatomic() (Closes: #659485)
   * rt2x00: Identify rt2800usb chipsets. (Closes: #658067)
 
+  [ Jonathan Nieder ]
+  * PM / Hibernate: fix the number of pages used for hibernate/thaw
+    buffering (Closes: #659363)
+
  -- Ben Hutchings <b...@decadent.org.uk>  Mon, 16 Apr 2012 02:27:29 +0100
 
 linux-2.6 (3.2.15-1) unstable; urgency=high
Index: 
debian/patches/bugfix/all/PM-Hibernate-fix-the-number-of-pages-used-for-hibern.patch
===================================================================
--- 
debian/patches/bugfix/all/PM-Hibernate-fix-the-number-of-pages-used-for-hibern.patch
        (revision 0)
+++ 
debian/patches/bugfix/all/PM-Hibernate-fix-the-number-of-pages-used-for-hibern.patch
        (working copy)
@@ -0,0 +1,221 @@
+From: Bojan Smojver <bo...@rexursive.com>
+Date: Sun, 22 Apr 2012 22:32:32 +0200
+Subject: PM / Hibernate: fix the number of pages used for hibernate/thaw 
buffering
+
+commit e9cbc5a6270be7aa9c42d9b15293ba9ac7161262 upstream.
+
+Hibernation/thaw fixes/improvements:
+
+1. Calculate the number of required free pages based on non-high memory
+pages only, because that is where the buffers will come from.
+
+2. Do not allocate memory for buffers from emergency pools, unless
+absolutely required. Do not warn about and do not retry non-essential
+failed allocations.
+
+3. Do not check the amount of free pages left on every single page
+write, but wait until one map is completely populated and then check.
+
+4. Set maximum number of pages for read buffering consistently, instead
+of inadvertently depending on the size of the sector type.
+
+5. Fix copyright line, which I missed when I submitted the hibernation
+threading patch.
+
+6. Dispense with bit shifting arithmetic to improve readability.
+
+Signed-off-by: Bojan Smojver <bo...@rexursive.com>
+Reviewed-by: Per Olofsson <pe...@debian.org>
+Cc: sta...@vger.kernel.org
+Signed-off-by: Rafael J. Wysocki <r...@sisk.pl>
+Signed-off-by: Jonathan Nieder <jrnie...@gmail.com>
+---
+ kernel/power/swap.c |   84 +++++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 58 insertions(+), 26 deletions(-)
+
+diff --git a/kernel/power/swap.c b/kernel/power/swap.c
+index 11a594c4ba25..64f8f973674b 100644
+--- a/kernel/power/swap.c
++++ b/kernel/power/swap.c
+@@ -6,7 +6,7 @@
+  *
+  * Copyright (C) 1998,2001-2005 Pavel Machek <pa...@ucw.cz>
+  * Copyright (C) 2006 Rafael J. Wysocki <r...@sisk.pl>
+- * Copyright (C) 2010 Bojan Smojver <bo...@rexursive.com>
++ * Copyright (C) 2010-2012 Bojan Smojver <bo...@rexursive.com>
+  *
+  * This file is released under the GPLv2.
+  *
+@@ -52,6 +52,23 @@
+ 
+ #define MAP_PAGE_ENTRIES      (PAGE_SIZE / sizeof(sector_t) - 1)
+ 
++/*
++ * Number of free pages that are not high.
++ */
++static inline unsigned long low_free_pages(void)
++{
++      return nr_free_pages() - nr_free_highpages();
++}
++
++/*
++ * Number of pages required to be kept free while writing the image. Always
++ * half of all available low pages before the writing starts.
++ */
++static inline unsigned long reqd_free_pages(void)
++{
++      return low_free_pages() / 2;
++}
++
+ struct swap_map_page {
+       sector_t entries[MAP_PAGE_ENTRIES];
+       sector_t next_swap;
+@@ -73,7 +90,7 @@ struct swap_map_handle {
+       sector_t cur_swap;
+       sector_t first_sector;
+       unsigned int k;
+-      unsigned long nr_free_pages, written;
++      unsigned long reqd_free_pages;
+       u32 crc32;
+ };
+ 
+@@ -266,14 +283,17 @@ static int write_page(void *buf, sector_t offset, struct 
bio **bio_chain)
+               return -ENOSPC;
+ 
+       if (bio_chain) {
+-              src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
++              src = (void *)__get_free_page(__GFP_WAIT | __GFP_NOWARN |
++                                            __GFP_NORETRY);
+               if (src) {
+                       copy_page(src, buf);
+               } else {
+                       ret = hib_wait_on_bio_chain(bio_chain); /* Free pages */
+                       if (ret)
+                               return ret;
+-                      src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
++                      src = (void *)__get_free_page(__GFP_WAIT |
++                                                    __GFP_NOWARN |
++                                                    __GFP_NORETRY);
+                       if (src) {
+                               copy_page(src, buf);
+                       } else {
+@@ -317,8 +337,7 @@ static int get_swap_writer(struct swap_map_handle *handle)
+               goto err_rel;
+       }
+       handle->k = 0;
+-      handle->nr_free_pages = nr_free_pages() >> 1;
+-      handle->written = 0;
++      handle->reqd_free_pages = reqd_free_pages();
+       handle->first_sector = handle->cur_swap;
+       return 0;
+ err_rel:
+@@ -352,12 +371,17 @@ static int swap_write_page(struct swap_map_handle 
*handle, void *buf,
+               clear_page(handle->cur);
+               handle->cur_swap = offset;
+               handle->k = 0;
+-      }
+-      if (bio_chain && ++handle->written > handle->nr_free_pages) {
+-              error = hib_wait_on_bio_chain(bio_chain);
+-              if (error)
+-                      goto out;
+-              handle->written = 0;
++
++              if (bio_chain && low_free_pages() <= handle->reqd_free_pages) {
++                      error = hib_wait_on_bio_chain(bio_chain);
++                      if (error)
++                              goto out;
++                      /*
++                       * Recalculate the number of required free pages, to
++                       * make sure we never take more than half.
++                       */
++                      handle->reqd_free_pages = reqd_free_pages();
++              }
+       }
+  out:
+       return error;
+@@ -404,8 +428,9 @@ static int swap_writer_finish(struct swap_map_handle 
*handle,
+ /* Maximum number of threads for compression/decompression. */
+ #define LZO_THREADS   3
+ 
+-/* Maximum number of pages for read buffering. */
+-#define LZO_READ_PAGES        (MAP_PAGE_ENTRIES * 8)
++/* Minimum/maximum number of pages for read buffering. */
++#define LZO_MIN_RD_PAGES      1024
++#define LZO_MAX_RD_PAGES      8192
+ 
+ 
+ /**
+@@ -616,12 +641,6 @@ static int save_image_lzo(struct swap_map_handle *handle,
+       }
+ 
+       /*
+-       * Adjust number of free pages after all allocations have been done.
+-       * We don't want to run out of pages when writing.
+-       */
+-      handle->nr_free_pages = nr_free_pages() >> 1;
+-
+-      /*
+        * Start the CRC32 thread.
+        */
+       init_waitqueue_head(&crc->go);
+@@ -642,6 +661,12 @@ static int save_image_lzo(struct swap_map_handle *handle,
+               goto out_clean;
+       }
+ 
++      /*
++       * Adjust the number of required free pages after all allocations have
++       * been done. We don't want to run out of pages when writing.
++       */
++      handle->reqd_free_pages = reqd_free_pages();
++
+       printk(KERN_INFO
+               "PM: Using %u thread(s) for compression.\n"
+               "PM: Compressing and saving image data (%u pages) ...     ",
+@@ -1051,7 +1076,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
+       unsigned i, thr, run_threads, nr_threads;
+       unsigned ring = 0, pg = 0, ring_size = 0,
+                have = 0, want, need, asked = 0;
+-      unsigned long read_pages;
++      unsigned long read_pages = 0;
+       unsigned char **page = NULL;
+       struct dec_data *data = NULL;
+       struct crc_data *crc = NULL;
+@@ -1063,7 +1088,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
+       nr_threads = num_online_cpus() - 1;
+       nr_threads = clamp_val(nr_threads, 1, LZO_THREADS);
+ 
+-      page = vmalloc(sizeof(*page) * LZO_READ_PAGES);
++      page = vmalloc(sizeof(*page) * LZO_MAX_RD_PAGES);
+       if (!page) {
+               printk(KERN_ERR "PM: Failed to allocate LZO page\n");
+               ret = -ENOMEM;
+@@ -1128,15 +1153,22 @@ static int load_image_lzo(struct swap_map_handle 
*handle,
+       }
+ 
+       /*
+-       * Adjust number of pages for read buffering, in case we are short.
++       * Set the number of pages for read buffering.
++       * This is complete guesswork, because we'll only know the real
++       * picture once prepare_image() is called, which is much later on
++       * during the image load phase. We'll assume the worst case and
++       * say that none of the image pages are from high memory.
+        */
+-      read_pages = (nr_free_pages() - snapshot_get_image_size()) >> 1;
+-      read_pages = clamp_val(read_pages, LZO_CMP_PAGES, LZO_READ_PAGES);
++      if (low_free_pages() > snapshot_get_image_size())
++              read_pages = (low_free_pages() - snapshot_get_image_size()) / 2;
++      read_pages = clamp_val(read_pages, LZO_MIN_RD_PAGES, LZO_MAX_RD_PAGES);
+ 
+       for (i = 0; i < read_pages; i++) {
+               page[i] = (void *)__get_free_page(i < LZO_CMP_PAGES ?
+                                                 __GFP_WAIT | __GFP_HIGH :
+-                                                __GFP_WAIT);
++                                                __GFP_WAIT | __GFP_NOWARN |
++                                                __GFP_NORETRY);
++
+               if (!page[i]) {
+                       if (i < LZO_CMP_PAGES) {
+                               ring_size = i;
+-- 
+1.7.10
+
Index: debian/patches/series/base
===================================================================
--- debian/patches/series/base  (revision 18944)
+++ debian/patches/series/base  (working copy)
@@ -174,3 +174,4 @@
 + bugfix/all/hugetlb-fix-race-condition-in-hugetlb_fault.patch
 + bugfix/ia64/IA64-Fix-futex_atomic_cmpxchg_inatomic.patch
 + features/all/rt2x00-Identify-rt2800usb-chipsets.patch
++ bugfix/all/PM-Hibernate-fix-the-number-of-pages-used-for-hibern.patch

Reply via email to