[PATCH] swsusp: simplify pagedir relocation
[powerpc.git] / kernel / power / swsusp.c
index 12db1d2..8511c7f 100644 (file)
@@ -85,18 +85,11 @@ unsigned int nr_copy_pages __nosavedata = 0;
 /* Suspend pagedir is allocated before final copy, therefore it
    must be freed after resume
 
-   Warning: this is evil. There are actually two pagedirs at time of
-   resume. One is "pagedir_save", which is empty frame allocated at
-   time of suspend, that must be freed. Second is "pagedir_nosave",
-   allocated at time of resume, that travels through memory not to
-   collide with anything.
-
    Warning: this is even more evil than it seems. Pagedirs this file
    talks about are completely different from page directories used by
    MMU hardware.
  */
 suspend_pagedir_t *pagedir_nosave __nosavedata = NULL;
-suspend_pagedir_t *pagedir_save;
 
 #define SWSUSP_SIG     "S1SUSPEND"
 
@@ -122,8 +115,8 @@ static struct swsusp_info swsusp_info;
 static unsigned short swapfile_used[MAX_SWAPFILES];
 static unsigned short root_swap;
 
-static int write_page(unsigned long addr, swp_entry_t * loc);
-static int bio_read_page(pgoff_t page_off, void * page);
+static int write_page(unsigned long addr, swp_entry_t *loc);
+static int bio_read_page(pgoff_t page_off, void *page);
 
 static u8 key_iv[MAXKEY+MAXIV];
 
@@ -355,7 +348,7 @@ static void lock_swapdevices(void)
  *     This is a partial improvement, since we will at least return other
  *     errors, though we need to eventually fix the damn code.
  */
-static int write_page(unsigned long addr, swp_entry_t * loc)
+static int write_page(unsigned long addr, swp_entry_t *loc)
 {
        swp_entry_t entry;
        int error = 0;
@@ -383,9 +376,9 @@ static int write_page(unsigned long addr, swp_entry_t * loc)
 static void data_free(void)
 {
        swp_entry_t entry;
-       struct pbe * p;
+       struct pbe *p;
 
-       for_each_pbe(p, pagedir_nosave) {
+       for_each_pbe (p, pagedir_nosave) {
                entry = p->swap_address;
                if (entry.val)
                        swap_free(entry);
@@ -492,8 +485,8 @@ static void free_pagedir_entries(void)
 static int write_pagedir(void)
 {
        int error = 0;
-       unsigned n = 0;
-       struct pbe * pbe;
+       unsigned int n = 0;
+       struct pbe *pbe;
 
        printk( "Writing pagedir...");
        for_each_pb_page (pbe, pagedir_nosave) {
@@ -543,7 +536,7 @@ static int write_suspend_image(void)
  *     We should only consider resume_device.
  */
 
-int enough_swap(unsigned nr_pages)
+int enough_swap(unsigned int nr_pages)
 {
        struct sysinfo i;
 
@@ -636,127 +629,43 @@ int swsusp_resume(void)
 }
 
 /**
- *     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.
- *
- *     We don't know which pages are usable until we allocate them.
- *
- *     Allocated but unusable (ie eaten) memory pages are marked so that
- *     swsusp_free() can release them
- */
-
-unsigned long get_safe_page(gfp_t gfp_mask)
-{
-       unsigned long m;
-
-       do {
-               m = get_zeroed_page(gfp_mask);
-               if (m && PageNosaveFree(virt_to_page(m)))
-                       /* This is for swsusp_free() */
-                       SetPageNosave(virt_to_page(m));
-       } while (m && PageNosaveFree(virt_to_page(m)));
-       if (m) {
-               /* This is for swsusp_free() */
-               SetPageNosave(virt_to_page(m));
-               SetPageNosaveFree(virt_to_page(m));
-       }
-       return m;
-}
-
-/**
- *     check_pagedir - We ensure here that pages that the PBEs point to
- *     won't collide with pages where we're going to restore from the loaded
- *     pages later
- */
-
-static int check_pagedir(struct pbe *pblist)
-{
-       struct pbe *p;
-
-       /* This is necessary, so that we can free allocated pages
-        * in case of failure
-        */
-       for_each_pbe (p, pblist)
-               p->address = 0UL;
-
-       for_each_pbe (p, pblist) {
-               p->address = get_safe_page(GFP_ATOMIC);
-               if (!p->address)
-                       return -ENOMEM;
-       }
-       return 0;
-}
-
-/**
- *     swsusp_pagedir_relocate - It is possible, that some memory pages
- *     occupied by the list of PBEs collide with pages where we're going to
- *     restore from the loaded pages later.  We relocate them here.
+ *     mark_unsafe_pages - mark the pages that cannot be used for storing
+ *     the image during resume, because they conflict with the pages that
+ *     had been used before suspend
  */
 
-static struct pbe * swsusp_pagedir_relocate(struct pbe *pblist)
+static void mark_unsafe_pages(struct pbe *pblist)
 {
        struct zone *zone;
        unsigned long zone_pfn;
-       struct pbe *pbpage, *tail, *p;
-       void *m;
-       int rel = 0;
+       struct pbe *p;
 
        if (!pblist) /* a sanity check */
-               return NULL;
-
-       pr_debug("swsusp: Relocating pagedir (%lu pages to check)\n",
-                       swsusp_info.pagedir_pages);
+               return;
 
        /* Clear page flags */
-
        for_each_zone (zone) {
-               for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
-                       if (pfn_valid(zone_pfn + zone->zone_start_pfn))
-                               ClearPageNosaveFree(pfn_to_page(zone_pfn +
+               for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
+                       if (pfn_valid(zone_pfn + zone->zone_start_pfn))
+                               ClearPageNosaveFree(pfn_to_page(zone_pfn +
                                        zone->zone_start_pfn));
        }
 
        /* Mark orig addresses */
-
        for_each_pbe (p, pblist)
                SetPageNosaveFree(virt_to_page(p->orig_address));
 
-       tail = pblist + PB_PAGE_SKIP;
-
-       /* Relocate colliding pages */
-
-       for_each_pb_page (pbpage, pblist) {
-               if (PageNosaveFree(virt_to_page((unsigned long)pbpage))) {
-                       m = (void *)get_safe_page(GFP_ATOMIC | __GFP_COLD);
-                       if (!m)
-                               return NULL;
-                       memcpy(m, (void *)pbpage, PAGE_SIZE);
-                       if (pbpage == pblist)
-                               pblist = (struct pbe *)m;
-                       else
-                               tail->next = (struct pbe *)m;
-                       pbpage = (struct pbe *)m;
-
-                       /* We have to link the PBEs again */
-                       for (p = pbpage; p < pbpage + PB_PAGE_SKIP; p++)
-                               if (p->next) /* needed to save the end */
-                                       p->next = p + 1;
-
-                       rel++;
-               }
-               tail = pbpage + PB_PAGE_SKIP;
-       }
+}
 
-       /* This is for swsusp_free() */
-       for_each_pb_page (pbpage, pblist) {
-               SetPageNosave(virt_to_page(pbpage));
-               SetPageNosaveFree(virt_to_page(pbpage));
+static void copy_page_backup_list(struct pbe *dst, struct pbe *src)
+{
+       /* We assume both lists contain the same number of elements */
+       while (src) {
+               dst->orig_address = src->orig_address;
+               dst->swap_address = src->swap_address;
+               dst = dst->next;
+               src = src->next;
        }
-
-       printk("swsusp: Relocated %d pages\n", rel);
-
-       return pblist;
 }
 
 /*
@@ -770,7 +679,7 @@ static struct pbe * swsusp_pagedir_relocate(struct pbe *pblist)
 
 static atomic_t io_done = ATOMIC_INIT(0);
 
-static int end_io(struct bio * bio, unsigned int num, int err)
+static int end_io(struct bio *bio, unsigned int num, int err)
 {
        if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
                panic("I/O error reading memory image");
@@ -778,7 +687,7 @@ static int end_io(struct bio * bio, unsigned int num, int err)
        return 0;
 }
 
-static struct block_device * resume_bdev;
+static struct block_device *resume_bdev;
 
 /**
  *     submit - submit BIO request.
@@ -791,10 +700,10 @@ static struct block_device * resume_bdev;
  *     Then submit it and wait.
  */
 
-static int submit(int rw, pgoff_t page_off, void * page)
+static int submit(int rw, pgoff_t page_off, void *page)
 {
        int error = 0;
-       struct bio * bio;
+       struct bio *bio;
 
        bio = bio_alloc(GFP_ATOMIC, 1);
        if (!bio)
@@ -823,12 +732,12 @@ static int submit(int rw, pgoff_t page_off, void * page)
        return error;
 }
 
-static int bio_read_page(pgoff_t page_off, void * page)
+static int bio_read_page(pgoff_t page_off, void *page)
 {
        return submit(READ, page_off, page);
 }
 
-static int bio_write_page(pgoff_t page_off, void * page)
+static int bio_write_page(pgoff_t page_off, void *page)
 {
        return submit(WRITE, page_off, page);
 }
@@ -838,7 +747,7 @@ static int bio_write_page(pgoff_t page_off, void * page)
  * I really don't think that it's foolproof but more than nothing..
  */
 
-static const char * sanity_check(void)
+static const char *sanity_check(void)
 {
        dump_info();
        if (swsusp_info.version_code != LINUX_VERSION_CODE)
@@ -864,7 +773,7 @@ static const char * sanity_check(void)
 
 static int check_header(void)
 {
-       const char * reason = NULL;
+       const char *reason = NULL;
        int error;
 
        if ((error = bio_read_page(swp_offset(swsusp_header.swsusp_info), &swsusp_info)))
@@ -912,7 +821,7 @@ static int check_sig(void)
 
 static int data_read(struct pbe *pblist)
 {
-       struct pbe * p;
+       struct pbe *p;
        int error = 0;
        int i = 0;
        int mod = swsusp_info.image_pages / 100;
@@ -950,7 +859,7 @@ static int data_read(struct pbe *pblist)
 static int read_pagedir(struct pbe *pblist)
 {
        struct pbe *pbpage, *p;
-       unsigned i = 0;
+       unsigned int i = 0;
        int error;
 
        if (!pblist)
@@ -997,20 +906,25 @@ static int read_suspend_image(void)
        int error = 0;
        struct pbe *p;
 
-       if (!(p = alloc_pagedir(nr_copy_pages)))
+       if (!(p = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 0)))
                return -ENOMEM;
 
        if ((error = read_pagedir(p)))
                return error;
-
        create_pbe_list(p, nr_copy_pages);
-
-       if (!(pagedir_nosave = swsusp_pagedir_relocate(p)))
+       mark_unsafe_pages(p);
+       pagedir_nosave = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 1);
+       if (pagedir_nosave) {
+               create_pbe_list(pagedir_nosave, nr_copy_pages);
+               copy_page_backup_list(pagedir_nosave, p);
+       }
+       free_pagedir(p);
+       if (!pagedir_nosave)
                return -ENOMEM;
 
        /* Allocate memory for the image and read the data from swap */
 
-       error = check_pagedir(pagedir_nosave);
+       error = alloc_data_pages(pagedir_nosave, GFP_ATOMIC, 1);
 
        if (!error)
                error = data_read(pagedir_nosave);