lkml.org 
[lkml]   [2011]   [Jul]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[27/35] PM / Hibernate: Avoid hitting OOM during preallocation of memory
    2.6.32-longterm review patch.  If anyone has any objections, please let us know.

    ------------------

    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(&copy_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



    \
     
     \ /
      Last update: 2011-07-09 08:03    [W:0.027 / U:0.336 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site