Hibernation: Introduce SNAPSHOT_GET_IMAGE_SIZE ioctl
[powerpc.git] / kernel / power / snapshot.c
index 128da11..c5ce0f3 100644 (file)
@@ -14,7 +14,6 @@
 #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>
@@ -608,7 +607,8 @@ static LIST_HEAD(nosave_regions);
  */
 
 void __init
-register_nosave_region(unsigned long start_pfn, unsigned long end_pfn)
+__register_nosave_region(unsigned long start_pfn, unsigned long end_pfn,
+                        int use_kmalloc)
 {
        struct nosave_region *region;
 
@@ -624,8 +624,13 @@ register_nosave_region(unsigned long start_pfn, unsigned long end_pfn)
                        goto Report;
                }
        }
-       /* This allocation cannot fail */
-       region = alloc_bootmem_low(sizeof(struct nosave_region));
+       if (use_kmalloc) {
+               /* during init, this shouldn't fail */
+               region = kmalloc(sizeof(struct nosave_region), GFP_KERNEL);
+               BUG_ON(!region);
+       } else
+               /* 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);
@@ -704,7 +709,8 @@ static void mark_nosave_pages(struct memory_bitmap *bm)
                                region->end_pfn << PAGE_SHIFT);
 
                for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++)
-                       memory_bm_set_bit(bm, pfn);
+                       if (pfn_valid(pfn))
+                               memory_bm_set_bit(bm, pfn);
        }
 }
 
@@ -999,11 +1005,12 @@ copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
        }
        memory_bm_position_reset(orig_bm);
        memory_bm_position_reset(copy_bm);
-       do {
+       for(;;) {
                pfn = memory_bm_next_pfn(orig_bm);
-               if (likely(pfn != BM_END_OF_MAP))
-                       copy_data_page(memory_bm_next_pfn(copy_bm), pfn);
-       } while (pfn != BM_END_OF_MAP);
+               if (unlikely(pfn == BM_END_OF_MAP))
+                       break;
+               copy_data_page(memory_bm_next_pfn(copy_bm), pfn);
+       }
 }
 
 /* Total number of image pages */
@@ -1228,22 +1235,49 @@ asmlinkage int swsusp_save(void)
        nr_copy_pages = nr_pages;
        nr_meta_pages = DIV_ROUND_UP(nr_pages * sizeof(long), PAGE_SIZE);
 
-       printk("swsusp: critical section/: done (%d pages copied)\n", nr_pages);
+       printk("swsusp: critical section: done (%d pages copied)\n", nr_pages);
 
        return 0;
 }
 
-static void init_header(struct swsusp_info *info)
+#ifndef CONFIG_ARCH_HIBERNATION_HEADER
+static int init_header_complete(struct swsusp_info *info)
 {
-       memset(info, 0, sizeof(struct swsusp_info));
+       memcpy(&info->uts, init_utsname(), sizeof(struct new_utsname));
        info->version_code = LINUX_VERSION_CODE;
+       return 0;
+}
+
+static char *check_image_kernel(struct swsusp_info *info)
+{
+       if (info->version_code != LINUX_VERSION_CODE)
+               return "kernel version";
+       if (strcmp(info->uts.sysname,init_utsname()->sysname))
+               return "system type";
+       if (strcmp(info->uts.release,init_utsname()->release))
+               return "kernel release";
+       if (strcmp(info->uts.version,init_utsname()->version))
+               return "version";
+       if (strcmp(info->uts.machine,init_utsname()->machine))
+               return "machine";
+       return NULL;
+}
+#endif /* CONFIG_ARCH_HIBERNATION_HEADER */
+
+unsigned long snapshot_get_image_size(void)
+{
+       return nr_copy_pages + nr_meta_pages + 1;
+}
+
+static int init_header(struct swsusp_info *info)
+{
+       memset(info, 0, sizeof(struct swsusp_info));
        info->num_physpages = num_physpages;
-       memcpy(&info->uts, init_utsname(), sizeof(struct new_utsname));
-       info->cpus = num_online_cpus();
        info->image_pages = nr_copy_pages;
-       info->pages = nr_copy_pages + nr_meta_pages + 1;
+       info->pages = snapshot_get_image_size();
        info->size = info->pages;
        info->size <<= PAGE_SHIFT;
+       return init_header_complete(info);
 }
 
 /**
@@ -1297,7 +1331,11 @@ int snapshot_read_next(struct snapshot_handle *handle, size_t count)
                        return -ENOMEM;
        }
        if (!handle->offset) {
-               init_header((struct swsusp_info *)buffer);
+               int error;
+
+               error = init_header((struct swsusp_info *)buffer);
+               if (error)
+                       return error;
                handle->buffer = buffer;
                memory_bm_position_reset(&orig_bm);
                memory_bm_position_reset(&copy_bm);
@@ -1388,22 +1426,13 @@ duplicate_memory_bitmap(struct memory_bitmap *dst, struct memory_bitmap *src)
        }
 }
 
-static inline int check_header(struct swsusp_info *info)
+static int check_header(struct swsusp_info *info)
 {
-       char *reason = NULL;
+       char *reason;
 
-       if (info->version_code != LINUX_VERSION_CODE)
-               reason = "kernel version";
-       if (info->num_physpages != num_physpages)
+       reason = check_image_kernel(info);
+       if (!reason && info->num_physpages != num_physpages)
                reason = "memory size";
-       if (strcmp(info->uts.sysname,init_utsname()->sysname))
-               reason = "system type";
-       if (strcmp(info->uts.release,init_utsname()->release))
-               reason = "kernel release";
-       if (strcmp(info->uts.version,init_utsname()->version))
-               reason = "version";
-       if (strcmp(info->uts.machine,init_utsname()->machine))
-               reason = "machine";
        if (reason) {
                printk(KERN_ERR "swsusp: Resume mismatch: %s\n", reason);
                return -EPERM;