]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - kernel/power/snapshot.c
[MTD] [NAND] drivers/mtd/nand/nandsim.c: fix printk warnings
[linux-2.6-omap-h63xx.git] / kernel / power / snapshot.c
index 95250d7c8d91db36593fc3707b10c99a8370dc62..5d2ab836e9986f650bc86cecc8d45a8f757d15ff 100644 (file)
@@ -205,8 +205,7 @@ static void chain_free(struct chain_allocator *ca, int clear_page_nosave)
  *     objects.  The main list's elements are of type struct zone_bitmap
  *     and each of them corresonds to one zone.  For each zone bitmap
  *     object there is a list of objects of type struct bm_block that
- *     represent each blocks of bit chunks in which information is
- *     stored.
+ *     represent each blocks of bitmap in which information is stored.
  *
  *     struct memory_bitmap contains a pointer to the main list of zone
  *     bitmap objects, a struct bm_position used for browsing the bitmap,
@@ -224,26 +223,27 @@ static void chain_free(struct chain_allocator *ca, int clear_page_nosave)
  *     pfns that correspond to the start and end of the represented zone.
  *
  *     struct bm_block contains a pointer to the memory page in which
- *     information is stored (in the form of a block of bit chunks
- *     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.
+ *     information is stored (in the form of a block of bitmap)
+ *     It also contains the pfns that correspond to the start and end of
+ *     the represented memory area.
  */
 
 #define BM_END_OF_MAP  (~0UL)
 
-#define BM_CHUNKS_PER_BLOCK    (PAGE_SIZE / sizeof(long))
-#define BM_BITS_PER_CHUNK      (sizeof(long) << 3)
 #define BM_BITS_PER_BLOCK      (PAGE_SIZE << 3)
 
 struct bm_block {
        struct bm_block *next;          /* next element of the list */
        unsigned long start_pfn;        /* pfn represented by the first bit */
        unsigned long end_pfn;  /* pfn represented by the last bit plus 1 */
-       unsigned int size;      /* number of bit chunks */
-       unsigned long *data;    /* chunks of bits representing pages */
+       unsigned long *data;    /* bitmap representing pages */
 };
 
+static inline unsigned long bm_block_bits(struct bm_block *bb)
+{
+       return bb->end_pfn - bb->start_pfn;
+}
+
 struct zone_bitmap {
        struct zone_bitmap *next;       /* next element of the list */
        unsigned long start_pfn;        /* minimal pfn in this zone */
@@ -257,7 +257,6 @@ struct zone_bitmap {
 struct bm_position {
        struct zone_bitmap *zone_bm;
        struct bm_block *block;
-       int chunk;
        int bit;
 };
 
@@ -272,12 +271,6 @@ struct memory_bitmap {
 
 /* Functions that operate on memory bitmaps */
 
-static inline void memory_bm_reset_chunk(struct memory_bitmap *bm)
-{
-       bm->cur.chunk = 0;
-       bm->cur.bit = -1;
-}
-
 static void memory_bm_position_reset(struct memory_bitmap *bm)
 {
        struct zone_bitmap *zone_bm;
@@ -285,7 +278,7 @@ static void memory_bm_position_reset(struct memory_bitmap *bm)
        zone_bm = bm->zone_bm_list;
        bm->cur.zone_bm = zone_bm;
        bm->cur.block = zone_bm->bm_blocks;
-       memory_bm_reset_chunk(bm);
+       bm->cur.bit = 0;
 }
 
 static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free);
@@ -394,12 +387,10 @@ memory_bm_create(struct memory_bitmap *bm, gfp_t gfp_mask, int safe_needed)
                        bb->start_pfn = pfn;
                        if (nr >= BM_BITS_PER_BLOCK) {
                                pfn += BM_BITS_PER_BLOCK;
-                               bb->size = BM_CHUNKS_PER_BLOCK;
                                nr -= BM_BITS_PER_BLOCK;
                        } else {
                                /* This is executed only once in the loop */
                                pfn += nr;
-                               bb->size = DIV_ROUND_UP(nr, BM_BITS_PER_CHUNK);
                        }
                        bb->end_pfn = pfn;
                        bb = bb->next;
@@ -447,7 +438,7 @@ static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free)
  *     of @bm->cur_zone_bm are updated.
  */
 
-static void memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
+static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
                                void **addr, unsigned int *bit_nr)
 {
        struct zone_bitmap *zone_bm;
@@ -461,7 +452,8 @@ static void memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
                while (pfn < zone_bm->start_pfn || pfn >= zone_bm->end_pfn) {
                        zone_bm = zone_bm->next;
 
-                       BUG_ON(!zone_bm);
+                       if (!zone_bm)
+                               return -EFAULT;
                }
                bm->cur.zone_bm = zone_bm;
        }
@@ -477,65 +469,54 @@ static void memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
        }
        zone_bm->cur_block = bb;
        pfn -= bb->start_pfn;
-       *bit_nr = pfn % BM_BITS_PER_CHUNK;
-       *addr = bb->data + pfn / BM_BITS_PER_CHUNK;
+       *bit_nr = pfn;
+       *addr = bb->data;
+       return 0;
 }
 
 static void memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn)
 {
        void *addr;
        unsigned int bit;
+       int error;
 
-       memory_bm_find_bit(bm, pfn, &addr, &bit);
+       error = memory_bm_find_bit(bm, pfn, &addr, &bit);
+       BUG_ON(error);
        set_bit(bit, addr);
 }
 
-static void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn)
+static int mem_bm_set_bit_check(struct memory_bitmap *bm, unsigned long pfn)
 {
        void *addr;
        unsigned int bit;
+       int error;
 
-       memory_bm_find_bit(bm, pfn, &addr, &bit);
-       clear_bit(bit, addr);
+       error = memory_bm_find_bit(bm, pfn, &addr, &bit);
+       if (!error)
+               set_bit(bit, addr);
+       return error;
 }
 
-static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
+static void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn)
 {
        void *addr;
        unsigned int bit;
+       int error;
 
-       memory_bm_find_bit(bm, pfn, &addr, &bit);
-       return test_bit(bit, addr);
-}
-
-/* Two auxiliary functions for memory_bm_next_pfn */
-
-/* Find the first set bit in the given chunk, if there is one */
-
-static inline int next_bit_in_chunk(int bit, unsigned long *chunk_p)
-{
-       bit++;
-       while (bit < BM_BITS_PER_CHUNK) {
-               if (test_bit(bit, chunk_p))
-                       return bit;
-
-               bit++;
-       }
-       return -1;
+       error = memory_bm_find_bit(bm, pfn, &addr, &bit);
+       BUG_ON(error);
+       clear_bit(bit, addr);
 }
 
-/* Find a chunk containing some bits set in given block of bits */
-
-static inline int next_chunk_in_block(int n, struct bm_block *bb)
+static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
 {
-       n++;
-       while (n < bb->size) {
-               if (bb->data[n])
-                       return n;
+       void *addr;
+       unsigned int bit;
+       int error;
 
-               n++;
-       }
-       return -1;
+       error = memory_bm_find_bit(bm, pfn, &addr, &bit);
+       BUG_ON(error);
+       return test_bit(bit, addr);
 }
 
 /**
@@ -551,40 +532,33 @@ static unsigned long memory_bm_next_pfn(struct memory_bitmap *bm)
 {
        struct zone_bitmap *zone_bm;
        struct bm_block *bb;
-       int chunk;
        int bit;
 
        do {
                bb = bm->cur.block;
                do {
-                       chunk = bm->cur.chunk;
                        bit = bm->cur.bit;
-                       do {
-                               bit = next_bit_in_chunk(bit, bb->data + chunk);
-                               if (bit >= 0)
-                                       goto Return_pfn;
-
-                               chunk = next_chunk_in_block(chunk, bb);
-                               bit = -1;
-                       } while (chunk >= 0);
+                       bit = find_next_bit(bb->data, bm_block_bits(bb), bit);
+                       if (bit < bm_block_bits(bb))
+                               goto Return_pfn;
+
                        bb = bb->next;
                        bm->cur.block = bb;
-                       memory_bm_reset_chunk(bm);
+                       bm->cur.bit = 0;
                } while (bb);
                zone_bm = bm->cur.zone_bm->next;
                if (zone_bm) {
                        bm->cur.zone_bm = zone_bm;
                        bm->cur.block = zone_bm->bm_blocks;
-                       memory_bm_reset_chunk(bm);
+                       bm->cur.bit = 0;
                }
        } while (zone_bm);
        memory_bm_position_reset(bm);
        return BM_END_OF_MAP;
 
  Return_pfn:
-       bm->cur.chunk = chunk;
-       bm->cur.bit = bit;
-       return bb->start_pfn + chunk * BM_BITS_PER_CHUNK + bit;
+       bm->cur.bit = bit + 1;
+       return bb->start_pfn + bit;
 }
 
 /**
@@ -709,8 +683,15 @@ static void mark_nosave_pages(struct memory_bitmap *bm)
                                region->end_pfn << PAGE_SHIFT);
 
                for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++)
-                       if (pfn_valid(pfn))
-                               memory_bm_set_bit(bm, pfn);
+                       if (pfn_valid(pfn)) {
+                               /*
+                                * It is safe to ignore the result of
+                                * mem_bm_set_bit_check() here, since we won't
+                                * touch the PFNs for which the error is
+                                * returned anyway.
+                                */
+                               mem_bm_set_bit_check(bm, pfn);
+                       }
        }
 }
 
@@ -875,8 +856,8 @@ static inline void *saveable_highmem_page(unsigned long pfn) { return NULL; }
 #endif /* CONFIG_HIGHMEM */
 
 /**
- *     saveable - Determine whether a non-highmem page should be included in
- *     the suspend image.
+ *     saveable_page - Determine whether a non-highmem page should be included
+ *     in the suspend image.
  *
  *     We should save the page if it isn't Nosave, and is not in the range
  *     of pages statically defined as 'unsaveable', and it isn't a part of
@@ -897,7 +878,8 @@ static struct page *saveable_page(unsigned long pfn)
        if (swsusp_page_is_forbidden(page) || swsusp_page_is_free(page))
                return NULL;
 
-       if (PageReserved(page) && pfn_is_nosave(pfn))
+       if (PageReserved(page)
+           && (!kernel_page_present(page) || pfn_is_nosave(pfn)))
                return NULL;
 
        return page;
@@ -938,6 +920,25 @@ static inline void do_copy_page(long *dst, long *src)
                *dst++ = *src++;
 }
 
+
+/**
+ *     safe_copy_page - check if the page we are going to copy is marked as
+ *             present in the kernel page tables (this always is the case if
+ *             CONFIG_DEBUG_PAGEALLOC is not set and in that case
+ *             kernel_page_present() always returns 'true').
+ */
+static void safe_copy_page(void *dst, struct page *s_page)
+{
+       if (kernel_page_present(s_page)) {
+               do_copy_page(dst, page_address(s_page));
+       } else {
+               kernel_map_pages(s_page, 1, 1);
+               do_copy_page(dst, page_address(s_page));
+               kernel_map_pages(s_page, 1, 0);
+       }
+}
+
+
 #ifdef CONFIG_HIGHMEM
 static inline struct page *
 page_is_saveable(struct zone *zone, unsigned long pfn)
@@ -946,8 +947,7 @@ page_is_saveable(struct zone *zone, unsigned long pfn)
                        saveable_highmem_page(pfn) : saveable_page(pfn);
 }
 
-static inline void
-copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
+static void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
 {
        struct page *s_page, *d_page;
        void *src, *dst;
@@ -961,29 +961,26 @@ copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
                kunmap_atomic(src, KM_USER0);
                kunmap_atomic(dst, KM_USER1);
        } else {
-               src = page_address(s_page);
                if (PageHighMem(d_page)) {
                        /* Page pointed to by src may contain some kernel
                         * data modified by kmap_atomic()
                         */
-                       do_copy_page(buffer, src);
+                       safe_copy_page(buffer, s_page);
                        dst = kmap_atomic(pfn_to_page(dst_pfn), KM_USER0);
                        memcpy(dst, buffer, PAGE_SIZE);
                        kunmap_atomic(dst, KM_USER0);
                } else {
-                       dst = page_address(d_page);
-                       do_copy_page(dst, src);
+                       safe_copy_page(page_address(d_page), s_page);
                }
        }
 }
 #else
 #define page_is_saveable(zone, pfn)    saveable_page(pfn)
 
-static inline void
-copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
+static inline void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
 {
-       do_copy_page(page_address(pfn_to_page(dst_pfn)),
-                       page_address(pfn_to_page(src_pfn)));
+       safe_copy_page(page_address(pfn_to_page(dst_pfn)),
+                               pfn_to_page(src_pfn));
 }
 #endif /* CONFIG_HIGHMEM */