Merge branch 'for-2.6.22' of git://git.kernel.dk/data/git/linux-2.6-block
[powerpc.git] / kernel / power / snapshot.c
index c024606..b703977 100644 (file)
 #include <linux/module.h>
 #include <linux/mm.h>
 #include <linux/suspend.h>
-#include <linux/smp_lock.h>
 #include <linux/delay.h>
 #include <linux/bitops.h>
 #include <linux/spinlock.h>
 #include <linux/kernel.h>
 #include <linux/pm.h>
 #include <linux/device.h>
+#include <linux/init.h>
 #include <linux/bootmem.h>
 #include <linux/syscalls.h>
 #include <linux/console.h>
 
 #include "power.h"
 
+static int swsusp_page_is_free(struct page *);
+static void swsusp_set_page_forbidden(struct page *);
+static void swsusp_unset_page_forbidden(struct page *);
+
 /* List of PBEs needed for restoring the pages that were allocated before
  * the suspend and included in the suspend image, but have also been
  * allocated by the "resume" kernel, so their contents cannot be written
@@ -67,15 +71,15 @@ static void *get_image_page(gfp_t gfp_mask, int safe_needed)
 
        res = (void *)get_zeroed_page(gfp_mask);
        if (safe_needed)
-               while (res && PageNosaveFree(virt_to_page(res))) {
+               while (res && swsusp_page_is_free(virt_to_page(res))) {
                        /* The page is unsafe, mark it for swsusp_free() */
-                       SetPageNosave(virt_to_page(res));
+                       swsusp_set_page_forbidden(virt_to_page(res));
                        allocated_unsafe_pages++;
                        res = (void *)get_zeroed_page(gfp_mask);
                }
        if (res) {
-               SetPageNosave(virt_to_page(res));
-               SetPageNosaveFree(virt_to_page(res));
+               swsusp_set_page_forbidden(virt_to_page(res));
+               swsusp_set_page_free(virt_to_page(res));
        }
        return res;
 }
@@ -91,8 +95,8 @@ static struct page *alloc_image_page(gfp_t gfp_mask)
 
        page = alloc_page(gfp_mask);
        if (page) {
-               SetPageNosave(page);
-               SetPageNosaveFree(page);
+               swsusp_set_page_forbidden(page);
+               swsusp_set_page_free(page);
        }
        return page;
 }
@@ -110,9 +114,9 @@ static inline void free_image_page(void *addr, int clear_nosave_free)
 
        page = virt_to_page(addr);
 
-       ClearPageNosave(page);
+       swsusp_unset_page_forbidden(page);
        if (clear_nosave_free)
-               ClearPageNosaveFree(page);
+               swsusp_unset_page_free(page);
 
        __free_page(page);
 }
@@ -224,11 +228,6 @@ static void chain_free(struct chain_allocator *ca, int clear_page_nosave)
  *     of type unsigned long each).  It also contains the pfns that
  *     correspond to the start and end of the represented memory area and
  *     the number of bit chunks in the block.
- *
- *     NOTE: Memory bitmaps are used for two types of operations only:
- *     "set a bit" and "find the next bit set".  Moreover, the searching
- *     is always carried out after all of the "set a bit" operations
- *     on given bitmap.
  */
 
 #define BM_END_OF_MAP  (~0UL)
@@ -443,15 +442,13 @@ static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free)
 }
 
 /**
- *     memory_bm_set_bit - set the bit in the bitmap @bm that corresponds
+ *     memory_bm_find_bit - find the bit in the bitmap @bm that corresponds
  *     to given pfn.  The cur_zone_bm member of @bm and the cur_block member
  *     of @bm->cur_zone_bm are updated.
- *
- *     If the bit cannot be set, the function returns -EINVAL .
  */
 
-static int
-memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn)
+static void memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
+                               void **addr, unsigned int *bit_nr)
 {
        struct zone_bitmap *zone_bm;
        struct bm_block *bb;
@@ -463,8 +460,8 @@ memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn)
                /* We don't assume that the zones are sorted by pfns */
                while (pfn < zone_bm->start_pfn || pfn >= zone_bm->end_pfn) {
                        zone_bm = zone_bm->next;
-                       if (unlikely(!zone_bm))
-                               return -EINVAL;
+
+                       BUG_ON(!zone_bm);
                }
                bm->cur.zone_bm = zone_bm;
        }
@@ -475,13 +472,40 @@ memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn)
 
        while (pfn >= bb->end_pfn) {
                bb = bb->next;
-               if (unlikely(!bb))
-                       return -EINVAL;
+
+               BUG_ON(!bb);
        }
        zone_bm->cur_block = bb;
        pfn -= bb->start_pfn;
-       set_bit(pfn % BM_BITS_PER_CHUNK, bb->data + pfn / BM_BITS_PER_CHUNK);
-       return 0;
+       *bit_nr = pfn % BM_BITS_PER_CHUNK;
+       *addr = bb->data + pfn / BM_BITS_PER_CHUNK;
+}
+
+static void memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn)
+{
+       void *addr;
+       unsigned int bit;
+
+       memory_bm_find_bit(bm, pfn, &addr, &bit);
+       set_bit(bit, addr);
+}
+
+static void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn)
+{
+       void *addr;
+       unsigned int bit;
+
+       memory_bm_find_bit(bm, pfn, &addr, &bit);
+       clear_bit(bit, addr);
+}
+
+static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
+{
+       void *addr;
+       unsigned int bit;
+
+       memory_bm_find_bit(bm, pfn, &addr, &bit);
+       return test_bit(bit, addr);
 }
 
 /* Two auxiliary functions for memory_bm_next_pfn */
@@ -563,6 +587,199 @@ static unsigned long memory_bm_next_pfn(struct memory_bitmap *bm)
        return bb->start_pfn + chunk * BM_BITS_PER_CHUNK + bit;
 }
 
+/**
+ *     This structure represents a range of page frames the contents of which
+ *     should not be saved during the suspend.
+ */
+
+struct nosave_region {
+       struct list_head list;
+       unsigned long start_pfn;
+       unsigned long end_pfn;
+};
+
+static LIST_HEAD(nosave_regions);
+
+/**
+ *     register_nosave_region - register a range of page frames the contents
+ *     of which should not be saved during the suspend (to be used in the early
+ *     initialization code)
+ */
+
+void __init
+register_nosave_region(unsigned long start_pfn, unsigned long end_pfn)
+{
+       struct nosave_region *region;
+
+       if (start_pfn >= end_pfn)
+               return;
+
+       if (!list_empty(&nosave_regions)) {
+               /* Try to extend the previous region (they should be sorted) */
+               region = list_entry(nosave_regions.prev,
+                                       struct nosave_region, list);
+               if (region->end_pfn == start_pfn) {
+                       region->end_pfn = end_pfn;
+                       goto Report;
+               }
+       }
+       /* This allocation cannot fail */
+       region = alloc_bootmem_low(sizeof(struct nosave_region));
+       region->start_pfn = start_pfn;
+       region->end_pfn = end_pfn;
+       list_add_tail(&region->list, &nosave_regions);
+ Report:
+       printk("swsusp: Registered nosave memory region: %016lx - %016lx\n",
+               start_pfn << PAGE_SHIFT, end_pfn << PAGE_SHIFT);
+}
+
+/*
+ * Set bits in this map correspond to the page frames the contents of which
+ * should not be saved during the suspend.
+ */
+static struct memory_bitmap *forbidden_pages_map;
+
+/* Set bits in this map correspond to free page frames. */
+static struct memory_bitmap *free_pages_map;
+
+/*
+ * Each page frame allocated for creating the image is marked by setting the
+ * corresponding bits in forbidden_pages_map and free_pages_map simultaneously
+ */
+
+void swsusp_set_page_free(struct page *page)
+{
+       if (free_pages_map)
+               memory_bm_set_bit(free_pages_map, page_to_pfn(page));
+}
+
+static int swsusp_page_is_free(struct page *page)
+{
+       return free_pages_map ?
+               memory_bm_test_bit(free_pages_map, page_to_pfn(page)) : 0;
+}
+
+void swsusp_unset_page_free(struct page *page)
+{
+       if (free_pages_map)
+               memory_bm_clear_bit(free_pages_map, page_to_pfn(page));
+}
+
+static void swsusp_set_page_forbidden(struct page *page)
+{
+       if (forbidden_pages_map)
+               memory_bm_set_bit(forbidden_pages_map, page_to_pfn(page));
+}
+
+int swsusp_page_is_forbidden(struct page *page)
+{
+       return forbidden_pages_map ?
+               memory_bm_test_bit(forbidden_pages_map, page_to_pfn(page)) : 0;
+}
+
+static void swsusp_unset_page_forbidden(struct page *page)
+{
+       if (forbidden_pages_map)
+               memory_bm_clear_bit(forbidden_pages_map, page_to_pfn(page));
+}
+
+/**
+ *     mark_nosave_pages - set bits corresponding to the page frames the
+ *     contents of which should not be saved in a given bitmap.
+ */
+
+static void mark_nosave_pages(struct memory_bitmap *bm)
+{
+       struct nosave_region *region;
+
+       if (list_empty(&nosave_regions))
+               return;
+
+       list_for_each_entry(region, &nosave_regions, list) {
+               unsigned long pfn;
+
+               printk("swsusp: Marking nosave pages: %016lx - %016lx\n",
+                               region->start_pfn << PAGE_SHIFT,
+                               region->end_pfn << PAGE_SHIFT);
+
+               for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++)
+                       memory_bm_set_bit(bm, pfn);
+       }
+}
+
+/**
+ *     create_basic_memory_bitmaps - create bitmaps needed for marking page
+ *     frames that should not be saved and free page frames.  The pointers
+ *     forbidden_pages_map and free_pages_map are only modified if everything
+ *     goes well, because we don't want the bits to be used before both bitmaps
+ *     are set up.
+ */
+
+int create_basic_memory_bitmaps(void)
+{
+       struct memory_bitmap *bm1, *bm2;
+       int error = 0;
+
+       BUG_ON(forbidden_pages_map || free_pages_map);
+
+       bm1 = kzalloc(sizeof(struct memory_bitmap), GFP_KERNEL);
+       if (!bm1)
+               return -ENOMEM;
+
+       error = memory_bm_create(bm1, GFP_KERNEL, PG_ANY);
+       if (error)
+               goto Free_first_object;
+
+       bm2 = kzalloc(sizeof(struct memory_bitmap), GFP_KERNEL);
+       if (!bm2)
+               goto Free_first_bitmap;
+
+       error = memory_bm_create(bm2, GFP_KERNEL, PG_ANY);
+       if (error)
+               goto Free_second_object;
+
+       forbidden_pages_map = bm1;
+       free_pages_map = bm2;
+       mark_nosave_pages(forbidden_pages_map);
+
+       printk("swsusp: Basic memory bitmaps created\n");
+
+       return 0;
+
+ Free_second_object:
+       kfree(bm2);
+ Free_first_bitmap:
+       memory_bm_free(bm1, PG_UNSAFE_CLEAR);
+ Free_first_object:
+       kfree(bm1);
+       return -ENOMEM;
+}
+
+/**
+ *     free_basic_memory_bitmaps - free memory bitmaps allocated by
+ *     create_basic_memory_bitmaps().  The auxiliary pointers are necessary
+ *     so that the bitmaps themselves are not referred to while they are being
+ *     freed.
+ */
+
+void free_basic_memory_bitmaps(void)
+{
+       struct memory_bitmap *bm1, *bm2;
+
+       BUG_ON(!(forbidden_pages_map && free_pages_map));
+
+       bm1 = forbidden_pages_map;
+       bm2 = free_pages_map;
+       forbidden_pages_map = NULL;
+       free_pages_map = NULL;
+       memory_bm_free(bm1, PG_UNSAFE_CLEAR);
+       kfree(bm1);
+       memory_bm_free(bm2, PG_UNSAFE_CLEAR);
+       kfree(bm2);
+
+       printk("swsusp: Basic memory bitmaps freed\n");
+}
+
 /**
  *     snapshot_additional_pages - estimate the number of additional pages
  *     be needed for setting up the suspend image data structures for given
@@ -591,7 +808,7 @@ static unsigned int count_free_highmem_pages(void)
 
        for_each_zone(zone)
                if (populated_zone(zone) && is_highmem(zone))
-                       cnt += zone->free_pages;
+                       cnt += zone_page_state(zone, NR_FREE_PAGES);
 
        return cnt;
 }
@@ -615,7 +832,8 @@ static struct page *saveable_highmem_page(unsigned long pfn)
 
        BUG_ON(!PageHighMem(page));
 
-       if (PageNosave(page) || PageReserved(page) || PageNosaveFree(page))
+       if (swsusp_page_is_forbidden(page) ||  swsusp_page_is_free(page) ||
+           PageReserved(page))
                return NULL;
 
        return page;
@@ -650,17 +868,6 @@ static inline void *saveable_highmem_page(unsigned long pfn) { return NULL; }
 static inline unsigned int count_highmem_pages(void) { return 0; }
 #endif /* CONFIG_HIGHMEM */
 
-/**
- *     pfn_is_nosave - check if given pfn is in the 'nosave' section
- */
-
-static inline int pfn_is_nosave(unsigned long pfn)
-{
-       unsigned long nosave_begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT;
-       unsigned long nosave_end_pfn = PAGE_ALIGN(__pa(&__nosave_end)) >> PAGE_SHIFT;
-       return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
-}
-
 /**
  *     saveable - Determine whether a non-highmem page should be included in
  *     the suspend image.
@@ -681,7 +888,7 @@ static struct page *saveable_page(unsigned long pfn)
 
        BUG_ON(PageHighMem(page));
 
-       if (PageNosave(page) || PageNosaveFree(page))
+       if (swsusp_page_is_forbidden(page) || swsusp_page_is_free(page))
                return NULL;
 
        if (PageReserved(page) && pfn_is_nosave(pfn))
@@ -821,9 +1028,10 @@ void swsusp_free(void)
                        if (pfn_valid(pfn)) {
                                struct page *page = pfn_to_page(pfn);
 
-                               if (PageNosave(page) && PageNosaveFree(page)) {
-                                       ClearPageNosave(page);
-                                       ClearPageNosaveFree(page);
+                               if (swsusp_page_is_forbidden(page) &&
+                                   swsusp_page_is_free(page)) {
+                                       swsusp_unset_page_forbidden(page);
+                                       swsusp_unset_page_free(page);
                                        __free_page(page);
                                }
                        }
@@ -869,7 +1077,7 @@ static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem)
        for_each_zone(zone) {
                meta += snapshot_additional_pages(zone);
                if (!is_highmem(zone))
-                       free += zone->free_pages;
+                       free += zone_page_state(zone, NR_FREE_PAGES);
        }
 
        nr_pages += count_pages_for_highmem(nr_highmem);
@@ -1146,7 +1354,7 @@ static int mark_unsafe_pages(struct memory_bitmap *bm)
                max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
                for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
                        if (pfn_valid(pfn))
-                               ClearPageNosaveFree(pfn_to_page(pfn));
+                               swsusp_unset_page_free(pfn_to_page(pfn));
        }
 
        /* Mark pages that correspond to the "original" pfns as "unsafe" */
@@ -1155,7 +1363,7 @@ static int mark_unsafe_pages(struct memory_bitmap *bm)
                pfn = memory_bm_next_pfn(bm);
                if (likely(pfn != BM_END_OF_MAP)) {
                        if (likely(pfn_valid(pfn)))
-                               SetPageNosaveFree(pfn_to_page(pfn));
+                               swsusp_set_page_free(pfn_to_page(pfn));
                        else
                                return -EFAULT;
                }
@@ -1321,14 +1529,14 @@ prepare_highmem_image(struct memory_bitmap *bm, unsigned int *nr_highmem_p)
                struct page *page;
 
                page = alloc_page(__GFP_HIGHMEM);
-               if (!PageNosaveFree(page)) {
+               if (!swsusp_page_is_free(page)) {
                        /* The page is "safe", set its bit the bitmap */
                        memory_bm_set_bit(bm, page_to_pfn(page));
                        safe_highmem_pages++;
                }
                /* Mark the page as allocated */
-               SetPageNosave(page);
-               SetPageNosaveFree(page);
+               swsusp_set_page_forbidden(page);
+               swsusp_set_page_free(page);
        }
        memory_bm_position_reset(bm);
        safe_highmem_bm = bm;
@@ -1360,7 +1568,7 @@ get_highmem_page_buffer(struct page *page, struct chain_allocator *ca)
        struct highmem_pbe *pbe;
        void *kaddr;
 
-       if (PageNosave(page) && PageNosaveFree(page)) {
+       if (swsusp_page_is_forbidden(page) && swsusp_page_is_free(page)) {
                /* We have allocated the "original" page frame and we can
                 * use it directly to store the loaded page.
                 */
@@ -1522,14 +1730,14 @@ prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
                        error = -ENOMEM;
                        goto Free;
                }
-               if (!PageNosaveFree(virt_to_page(lp))) {
+               if (!swsusp_page_is_free(virt_to_page(lp))) {
                        /* The page is "safe", add it to the list */
                        lp->next = safe_pages_list;
                        safe_pages_list = lp;
                }
                /* Mark the page as allocated */
-               SetPageNosave(virt_to_page(lp));
-               SetPageNosaveFree(virt_to_page(lp));
+               swsusp_set_page_forbidden(virt_to_page(lp));
+               swsusp_set_page_free(virt_to_page(lp));
                nr_pages--;
        }
        /* Free the reserved safe pages so that chain_alloc() can use them */
@@ -1558,7 +1766,7 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
        if (PageHighMem(page))
                return get_highmem_page_buffer(page, ca);
 
-       if (PageNosave(page) && PageNosaveFree(page))
+       if (swsusp_page_is_forbidden(page) && swsusp_page_is_free(page))
                /* We have allocated the "original" page frame and we can
                 * use it directly to store the loaded page.
                 */