lkml.org 
[lkml]   [2005]   [Nov]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFT][PATCH 3/3] swsusp: improve freeing of memory
    Date
    This patch makes swsusp free only as much memory as needed and not as much
    as possible.

    Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

    include/linux/suspend.h | 2 -
    kernel/power/disk.c | 30 ++--------------------
    kernel/power/power.h | 20 ++++++++++++--
    kernel/power/snapshot.c | 65 ++++++++++++++++++++++++++++++++++++++++++++----
    kernel/power/swsusp.c | 45 ++++++++++++++++++++++++++++++++-
    5 files changed, 125 insertions(+), 37 deletions(-)

    Index: linux-2.6.14-mm2/kernel/power/disk.c
    ===================================================================
    --- linux-2.6.14-mm2.orig/kernel/power/disk.c 2005-11-12 12:07:47.000000000 +0100
    +++ linux-2.6.14-mm2/kernel/power/disk.c 2005-11-12 12:10:54.000000000 +0100
    @@ -24,6 +24,7 @@

    extern suspend_disk_method_t pm_disk_mode;

    +extern int swsusp_shrink_memory(void);
    extern int swsusp_suspend(void);
    extern int swsusp_write(struct pbe *pblist, unsigned int nr_pages);
    extern int swsusp_check(void);
    @@ -73,31 +74,6 @@
    static int in_suspend __nosavedata = 0;


    -/**
    - * free_some_memory - Try to free as much memory as possible
    - *
    - * ... but do not OOM-kill anyone
    - *
    - * Notice: all userland should be stopped at this point, or
    - * livelock is possible.
    - */
    -
    -static void free_some_memory(void)
    -{
    - unsigned int i = 0;
    - unsigned int tmp;
    - unsigned long pages = 0;
    - char *p = "-\\|/";
    -
    - printk("Freeing memory... ");
    - while ((tmp = shrink_all_memory(10000))) {
    - pages += tmp;
    - printk("\b%c", p[i++ % 4]);
    - }
    - printk("\bdone (%li pages freed)\n", pages);
    -}
    -
    -
    static inline void platform_finish(void)
    {
    if (pm_disk_mode == PM_DISK_PLATFORM) {
    @@ -127,8 +103,8 @@
    }

    /* Free memory before shutting down devices. */
    - free_some_memory();
    - return 0;
    + if (!(error = swsusp_shrink_memory()))
    + return 0;
    thaw:
    thaw_processes();
    enable_nonboot_cpus();
    Index: linux-2.6.14-mm2/kernel/power/power.h
    ===================================================================
    --- linux-2.6.14-mm2.orig/kernel/power/power.h 2005-11-12 12:07:47.000000000 +0100
    +++ linux-2.6.14-mm2/kernel/power/power.h 2005-11-12 12:10:54.000000000 +0100
    @@ -49,18 +49,32 @@
    extern int pm_prepare_console(void);
    extern void pm_restore_console(void);

    -
    /* References to section boundaries */
    extern const void __nosave_begin, __nosave_end;

    extern unsigned int nr_copy_pages;
    -extern suspend_pagedir_t *pagedir_nosave;
    -extern suspend_pagedir_t *pagedir_save;
    +extern struct pbe *pagedir_nosave;
    +
    +/*
    + * This compilation switch determines the way in which memory will be freed
    + * during suspend. If defined, only as much memory will be freed as needed
    + * to complete the suspend. Otherwise, the largest possible amount of memory
    + * will be freed.
    + */
    +#define OPPORTUNISTIC_SHRINKING 1
    +
    +/*
    + * During suspend, on each attempt to free some more memory SHRINK_BITE
    + * is used as the number of pages to free
    + */
    +#define SHRINK_BITE 10000

    extern asmlinkage int swsusp_arch_suspend(void);
    extern asmlinkage int swsusp_arch_resume(void);

    +extern unsigned int count_data_pages(void);
    extern void free_pagedir(struct pbe *pblist);
    +extern void release_eaten_pages(void);
    extern struct pbe *alloc_pagedir(unsigned nr_pages, gfp_t gfp_mask, int safe_needed);
    extern void swsusp_free(void);
    extern int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed);
    Index: linux-2.6.14-mm2/kernel/power/snapshot.c
    ===================================================================
    --- linux-2.6.14-mm2.orig/kernel/power/snapshot.c 2005-11-12 12:07:47.000000000 +0100
    +++ linux-2.6.14-mm2/kernel/power/snapshot.c 2005-11-12 12:10:54.000000000 +0100
    @@ -37,6 +37,31 @@
    unsigned int nr_copy_pages = 0;

    #ifdef CONFIG_HIGHMEM
    +unsigned int count_highmem_pages(void)
    +{
    + struct zone *zone;
    + unsigned long zone_pfn;
    + unsigned int n = 0;
    +
    + for_each_zone (zone)
    + if (is_highmem(zone)) {
    + mark_free_pages(zone);
    + for (zone_pfn = 0; zone_pfn < zone->spanned_pages; zone_pfn++) {
    + struct page *page;
    + unsigned long pfn = zone_pfn + zone->zone_start_pfn;
    + if (!pfn_valid(pfn))
    + continue;
    + page = pfn_to_page(pfn);
    + if (PageReserved(page))
    + continue;
    + if (PageNosaveFree(page))
    + continue;
    + n++;
    + }
    + }
    + return n;
    +}
    +
    struct highmem_page {
    char *data;
    struct page *page;
    @@ -152,17 +177,15 @@
    BUG_ON(PageReserved(page) && PageNosave(page));
    if (PageNosave(page))
    return 0;
    - if (PageReserved(page) && pfn_is_nosave(pfn)) {
    - pr_debug("[nosave pfn 0x%lx]", pfn);
    + if (PageReserved(page) && pfn_is_nosave(pfn))
    return 0;
    - }
    if (PageNosaveFree(page))
    return 0;

    return 1;
    }

    -static unsigned count_data_pages(void)
    +unsigned int count_data_pages(void)
    {
    struct zone *zone;
    unsigned long zone_pfn;
    @@ -267,6 +290,35 @@
    }

    /**
    + * On resume it is necessary to trace and eventually free the unsafe
    + * pages that have been allocated, because they are needed for I/O
    + * (on x86-64 we likely will "eat" these pages once again while
    + * creating the temporary page translation tables)
    + */
    +
    +struct eaten_page {
    + struct eaten_page *next;
    + char padding[PAGE_SIZE - sizeof(void *)];
    +};
    +
    +static struct eaten_page *eaten_pages = NULL;
    +
    +void release_eaten_pages(void)
    +{
    + struct eaten_page *p, *q;
    +
    + p = eaten_pages;
    + while (p) {
    + q = p->next;
    + /* We don't want swsusp_free() to free this page again */
    + ClearPageNosave(virt_to_page(p));
    + free_page((unsigned long)p);
    + p = q;
    + }
    + eaten_pages = NULL;
    +}
    +
    +/**
    * @safe_needed - on resume, for storing the PBE list and the image,
    * we can only use memory pages that do not conflict with the pages
    * which had been used before suspend.
    @@ -284,9 +336,12 @@
    if (safe_needed)
    do {
    res = (void *)get_zeroed_page(gfp_mask);
    - if (res && PageNosaveFree(virt_to_page(res)))
    + if (res && PageNosaveFree(virt_to_page(res))) {
    /* This is for swsusp_free() */
    SetPageNosave(virt_to_page(res));
    + ((struct eaten_page *)res)->next = eaten_pages;
    + eaten_pages = res;
    + }
    } while (res && PageNosaveFree(virt_to_page(res)));
    else
    res = (void *)get_zeroed_page(gfp_mask);
    Index: linux-2.6.14-mm2/kernel/power/swsusp.c
    ===================================================================
    --- linux-2.6.14-mm2.orig/kernel/power/swsusp.c 2005-11-12 12:07:47.000000000 +0100
    +++ linux-2.6.14-mm2/kernel/power/swsusp.c 2005-11-12 12:10:54.000000000 +0100
    @@ -74,11 +74,13 @@
    #include "power.h"

    #ifdef CONFIG_HIGHMEM
    +unsigned int count_highmem_pages(void);
    int save_highmem(void);
    int restore_highmem(void);
    #else
    static int save_highmem(void) { return 0; }
    static int restore_highmem(void) { return 0; }
    +static unsigned int count_highmem_pages(void) { return 0; }
    #endif

    extern char resume_file[];
    @@ -616,6 +618,45 @@
    return error;
    }

    +/**
    + * swsusp_shrink_memory - Try to free as much memory as needed
    + *
    + * ... but do not OOM-kill anyone
    + *
    + * Notice: all userland should be stopped before it is called, or
    + * livelock is possible.
    + */
    +
    +int swsusp_shrink_memory(void)
    +{
    + unsigned long cnt = SHRINK_BITE;
    + long tmp;
    + unsigned long pages = 0;
    + unsigned int i = 0;
    + char *p = "-\\|/";
    +
    + printk("Shrinking memory... ");
    + do {
    +#ifdef OPPORTUNISTIC_SHRINKING
    + cnt = count_data_pages() + count_highmem_pages();
    + cnt += (cnt + PBES_PER_PAGE - 1) / PBES_PER_PAGE +
    + PAGES_FOR_IO;
    + tmp = cnt - nr_free_pages();
    + if (tmp > 0) {
    + cnt = shrink_all_memory(SHRINK_BITE);
    + pages += cnt;
    + }
    +#else
    + tmp = shrink_all_memory(SHRINK_BITE);
    + pages += tmp;
    +#endif
    + printk("\b%c", p[i++%4]);
    + } while (tmp > 0 && cnt > 0);
    + printk("\bdone (%lu pages freed)\n", pages);
    +
    + return tmp > 0 ? -ENOMEM : 0;
    +}
    +
    int swsusp_suspend(void)
    {
    int error;
    @@ -1035,8 +1076,10 @@
    /* Allocate memory for the image and read the data from swap */
    if (!error)
    error = alloc_data_pages(pblist, GFP_ATOMIC, 1);
    - if (!error)
    + if (!error) {
    + release_eaten_pages();
    error = load_image_data(pblist, &handle, nr_pages);
    + }
    if (!error)
    *pblist_ptr = pblist;
    }
    Index: linux-2.6.14-mm2/include/linux/suspend.h
    ===================================================================
    --- linux-2.6.14-mm2.orig/include/linux/suspend.h 2005-11-12 12:07:47.000000000 +0100
    +++ linux-2.6.14-mm2/include/linux/suspend.h 2005-11-12 12:10:54.000000000 +0100
    @@ -73,6 +73,6 @@
    * XXX: We try to keep some more pages free so that I/O operations succeed
    * without paging. Might this be more?
    */
    -#define PAGES_FOR_IO 512
    +#define PAGES_FOR_IO 1024

    #endif /* _LINUX_SWSUSP_H */
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2005-11-12 21:31    [W:0.038 / U:147.304 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site