|
@@ -30,6 +30,9 @@
|
|
|
* Alex Badea <vampire@go.ro>:
|
|
|
* Fixed runaway init
|
|
|
*
|
|
|
+ * Rafael J. Wysocki <rjw@sisk.pl>
|
|
|
+ * Added the swap map data structure and reworked the handling of swap
|
|
|
+ *
|
|
|
* More state savers are welcome. Especially for the scsi layer...
|
|
|
*
|
|
|
* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
|
|
@@ -76,18 +79,6 @@ static int restore_highmem(void) { return 0; }
|
|
|
|
|
|
extern char resume_file[];
|
|
|
|
|
|
-/* Local variables that should not be affected by save */
|
|
|
-unsigned int nr_copy_pages __nosavedata = 0;
|
|
|
-
|
|
|
-/* Suspend pagedir is allocated before final copy, therefore it
|
|
|
- must be freed after resume
|
|
|
-
|
|
|
- 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;
|
|
|
-
|
|
|
#define SWSUSP_SIG "S1SUSPEND"
|
|
|
|
|
|
static struct swsusp_header {
|
|
@@ -238,48 +229,205 @@ static int write_page(unsigned long addr, swp_entry_t *loc)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * data_free - Free the swap entries used by the saved image.
|
|
|
+ * Swap map-handling functions
|
|
|
+ *
|
|
|
+ * The swap map is a data structure used for keeping track of each page
|
|
|
+ * written to the swap. It consists of many swap_map_page structures
|
|
|
+ * that contain each an array of MAP_PAGE_SIZE swap entries.
|
|
|
+ * These structures are linked together with the help of either the
|
|
|
+ * .next (in memory) or the .next_swap (in swap) member.
|
|
|
*
|
|
|
- * Walk the list of used swap entries and free each one.
|
|
|
- * This is only used for cleanup when suspend fails.
|
|
|
+ * The swap map is created during suspend. At that time we need to keep
|
|
|
+ * it in memory, because we have to free all of the allocated swap
|
|
|
+ * entries if an error occurs. The memory needed is preallocated
|
|
|
+ * so that we know in advance if there's enough of it.
|
|
|
+ *
|
|
|
+ * The first swap_map_page structure is filled with the swap entries that
|
|
|
+ * correspond to the first MAP_PAGE_SIZE data pages written to swap and
|
|
|
+ * so on. After the all of the data pages have been written, the order
|
|
|
+ * of the swap_map_page structures in the map is reversed so that they
|
|
|
+ * can be read from swap in the original order. This causes the data
|
|
|
+ * pages to be loaded in exactly the same order in which they have been
|
|
|
+ * saved.
|
|
|
+ *
|
|
|
+ * During resume we only need to use one swap_map_page structure
|
|
|
+ * at a time, which means that we only need to use two memory pages for
|
|
|
+ * reading the image - one for reading the swap_map_page structures
|
|
|
+ * and the second for reading the data pages from swap.
|
|
|
*/
|
|
|
-static void data_free(void)
|
|
|
+
|
|
|
+#define MAP_PAGE_SIZE ((PAGE_SIZE - sizeof(swp_entry_t) - sizeof(void *)) \
|
|
|
+ / sizeof(swp_entry_t))
|
|
|
+
|
|
|
+struct swap_map_page {
|
|
|
+ swp_entry_t entries[MAP_PAGE_SIZE];
|
|
|
+ swp_entry_t next_swap;
|
|
|
+ struct swap_map_page *next;
|
|
|
+};
|
|
|
+
|
|
|
+static inline void free_swap_map(struct swap_map_page *swap_map)
|
|
|
{
|
|
|
- swp_entry_t entry;
|
|
|
- struct pbe *p;
|
|
|
+ struct swap_map_page *swp;
|
|
|
|
|
|
- for_each_pbe (p, pagedir_nosave) {
|
|
|
- entry = p->swap_address;
|
|
|
- if (entry.val)
|
|
|
- swap_free(entry);
|
|
|
- else
|
|
|
- break;
|
|
|
+ while (swap_map) {
|
|
|
+ swp = swap_map->next;
|
|
|
+ free_page((unsigned long)swap_map);
|
|
|
+ swap_map = swp;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static struct swap_map_page *alloc_swap_map(unsigned int nr_pages)
|
|
|
+{
|
|
|
+ struct swap_map_page *swap_map, *swp;
|
|
|
+ unsigned n = 0;
|
|
|
+
|
|
|
+ if (!nr_pages)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ pr_debug("alloc_swap_map(): nr_pages = %d\n", nr_pages);
|
|
|
+ swap_map = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
|
|
|
+ swp = swap_map;
|
|
|
+ for (n = MAP_PAGE_SIZE; n < nr_pages; n += MAP_PAGE_SIZE) {
|
|
|
+ swp->next = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
|
|
|
+ swp = swp->next;
|
|
|
+ if (!swp) {
|
|
|
+ free_swap_map(swap_map);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
}
|
|
|
+ return swap_map;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * data_write - Write saved image to swap.
|
|
|
- *
|
|
|
- * Walk the list of pages in the image and sync each one to swap.
|
|
|
+ * reverse_swap_map - reverse the order of pages in the swap map
|
|
|
+ * @swap_map
|
|
|
*/
|
|
|
-static int data_write(void)
|
|
|
+
|
|
|
+static inline struct swap_map_page *reverse_swap_map(struct swap_map_page *swap_map)
|
|
|
{
|
|
|
- int error = 0, i = 0;
|
|
|
- unsigned int mod = nr_copy_pages / 100;
|
|
|
- struct pbe *p;
|
|
|
+ struct swap_map_page *prev, *next;
|
|
|
+
|
|
|
+ prev = NULL;
|
|
|
+ while (swap_map) {
|
|
|
+ next = swap_map->next;
|
|
|
+ swap_map->next = prev;
|
|
|
+ prev = swap_map;
|
|
|
+ swap_map = next;
|
|
|
+ }
|
|
|
+ return prev;
|
|
|
+}
|
|
|
|
|
|
- if (!mod)
|
|
|
- mod = 1;
|
|
|
+/**
|
|
|
+ * free_swap_map_entries - free the swap entries allocated to store
|
|
|
+ * the swap map @swap_map (this is only called in case of an error)
|
|
|
+ */
|
|
|
+static inline void free_swap_map_entries(struct swap_map_page *swap_map)
|
|
|
+{
|
|
|
+ while (swap_map) {
|
|
|
+ if (swap_map->next_swap.val)
|
|
|
+ swap_free(swap_map->next_swap);
|
|
|
+ swap_map = swap_map->next;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- printk( "Writing data to swap (%d pages)... ", nr_copy_pages );
|
|
|
- for_each_pbe (p, pagedir_nosave) {
|
|
|
- if (!(i%mod))
|
|
|
- printk( "\b\b\b\b%3d%%", i / mod );
|
|
|
- if ((error = write_page(p->address, &p->swap_address)))
|
|
|
+/**
|
|
|
+ * save_swap_map - save the swap map used for tracing the data pages
|
|
|
+ * stored in the swap
|
|
|
+ */
|
|
|
+
|
|
|
+static int save_swap_map(struct swap_map_page *swap_map, swp_entry_t *start)
|
|
|
+{
|
|
|
+ swp_entry_t entry = (swp_entry_t){0};
|
|
|
+ int error;
|
|
|
+
|
|
|
+ while (swap_map) {
|
|
|
+ swap_map->next_swap = entry;
|
|
|
+ if ((error = write_page((unsigned long)swap_map, &entry)))
|
|
|
return error;
|
|
|
- i++;
|
|
|
+ swap_map = swap_map->next;
|
|
|
}
|
|
|
- printk("\b\b\b\bdone\n");
|
|
|
+ *start = entry;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * free_image_entries - free the swap entries allocated to store
|
|
|
+ * the image data pages (this is only called in case of an error)
|
|
|
+ */
|
|
|
+
|
|
|
+static inline void free_image_entries(struct swap_map_page *swp)
|
|
|
+{
|
|
|
+ unsigned k;
|
|
|
+
|
|
|
+ while (swp) {
|
|
|
+ for (k = 0; k < MAP_PAGE_SIZE; k++)
|
|
|
+ if (swp->entries[k].val)
|
|
|
+ swap_free(swp->entries[k]);
|
|
|
+ swp = swp->next;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * The swap_map_handle structure is used for handling the swap map in
|
|
|
+ * a file-alike way
|
|
|
+ */
|
|
|
+
|
|
|
+struct swap_map_handle {
|
|
|
+ struct swap_map_page *cur;
|
|
|
+ unsigned int k;
|
|
|
+};
|
|
|
+
|
|
|
+static inline void init_swap_map_handle(struct swap_map_handle *handle,
|
|
|
+ struct swap_map_page *map)
|
|
|
+{
|
|
|
+ handle->cur = map;
|
|
|
+ handle->k = 0;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int swap_map_write_page(struct swap_map_handle *handle,
|
|
|
+ unsigned long addr)
|
|
|
+{
|
|
|
+ int error;
|
|
|
+
|
|
|
+ error = write_page(addr, handle->cur->entries + handle->k);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+ if (++handle->k >= MAP_PAGE_SIZE) {
|
|
|
+ handle->cur = handle->cur->next;
|
|
|
+ handle->k = 0;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * save_image_data - save the data pages pointed to by the PBEs
|
|
|
+ * from the list @pblist using the swap map handle @handle
|
|
|
+ * (assume there are @nr_pages data pages to save)
|
|
|
+ */
|
|
|
+
|
|
|
+static int save_image_data(struct pbe *pblist,
|
|
|
+ struct swap_map_handle *handle,
|
|
|
+ unsigned int nr_pages)
|
|
|
+{
|
|
|
+ unsigned int m;
|
|
|
+ struct pbe *p;
|
|
|
+ int error = 0;
|
|
|
+
|
|
|
+ printk("Saving image data pages (%u pages) ... ", nr_pages);
|
|
|
+ m = nr_pages / 100;
|
|
|
+ if (!m)
|
|
|
+ m = 1;
|
|
|
+ nr_pages = 0;
|
|
|
+ for_each_pbe (p, pblist) {
|
|
|
+ error = swap_map_write_page(handle, p->address);
|
|
|
+ if (error)
|
|
|
+ break;
|
|
|
+ if (!(nr_pages % m))
|
|
|
+ printk("\b\b\b\b%3d%%", nr_pages / m);
|
|
|
+ nr_pages++;
|
|
|
+ }
|
|
|
+ if (!error)
|
|
|
+ printk("\b\b\b\bdone\n");
|
|
|
return error;
|
|
|
}
|
|
|
|
|
@@ -295,19 +443,20 @@ static void dump_info(void)
|
|
|
pr_debug(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname);
|
|
|
pr_debug(" swsusp: CPUs: %d\n",swsusp_info.cpus);
|
|
|
pr_debug(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages);
|
|
|
- pr_debug(" swsusp: Pagedir: %ld Pages\n",swsusp_info.pagedir_pages);
|
|
|
+ pr_debug(" swsusp: Total: %ld Pages\n", swsusp_info.pages);
|
|
|
}
|
|
|
|
|
|
-static void init_header(void)
|
|
|
+static void init_header(unsigned int nr_pages)
|
|
|
{
|
|
|
memset(&swsusp_info, 0, sizeof(swsusp_info));
|
|
|
swsusp_info.version_code = LINUX_VERSION_CODE;
|
|
|
swsusp_info.num_physpages = num_physpages;
|
|
|
memcpy(&swsusp_info.uts, &system_utsname, sizeof(system_utsname));
|
|
|
|
|
|
- swsusp_info.suspend_pagedir = pagedir_nosave;
|
|
|
swsusp_info.cpus = num_online_cpus();
|
|
|
- swsusp_info.image_pages = nr_copy_pages;
|
|
|
+ swsusp_info.image_pages = nr_pages;
|
|
|
+ swsusp_info.pages = nr_pages +
|
|
|
+ ((nr_pages * sizeof(long) + PAGE_SIZE - 1) >> PAGE_SHIFT);
|
|
|
}
|
|
|
|
|
|
static int close_swap(void)
|
|
@@ -326,39 +475,53 @@ static int close_swap(void)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * free_pagedir_entries - Free pages used by the page directory.
|
|
|
- *
|
|
|
- * This is used during suspend for error recovery.
|
|
|
+ * pack_orig_addresses - the .orig_address fields of the PBEs from the
|
|
|
+ * list starting at @pbe are stored in the array @buf[] (1 page)
|
|
|
*/
|
|
|
|
|
|
-static void free_pagedir_entries(void)
|
|
|
+static inline struct pbe *pack_orig_addresses(unsigned long *buf,
|
|
|
+ struct pbe *pbe)
|
|
|
{
|
|
|
- int i;
|
|
|
+ int j;
|
|
|
|
|
|
- for (i = 0; i < swsusp_info.pagedir_pages; i++)
|
|
|
- swap_free(swsusp_info.pagedir[i]);
|
|
|
+ for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) {
|
|
|
+ buf[j] = pbe->orig_address;
|
|
|
+ pbe = pbe->next;
|
|
|
+ }
|
|
|
+ if (!pbe)
|
|
|
+ for (; j < PAGE_SIZE / sizeof(long); j++)
|
|
|
+ buf[j] = 0;
|
|
|
+ return pbe;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
- * write_pagedir - Write the array of pages holding the page directory.
|
|
|
- * @last: Last swap entry we write (needed for header).
|
|
|
+ * save_image_metadata - save the .orig_address fields of the PBEs
|
|
|
+ * from the list @pblist using the swap map handle @handle
|
|
|
*/
|
|
|
|
|
|
-static int write_pagedir(void)
|
|
|
+static int save_image_metadata(struct pbe *pblist,
|
|
|
+ struct swap_map_handle *handle)
|
|
|
{
|
|
|
- int error = 0;
|
|
|
+ unsigned long *buf;
|
|
|
unsigned int n = 0;
|
|
|
- struct pbe *pbe;
|
|
|
+ struct pbe *p;
|
|
|
+ int error = 0;
|
|
|
|
|
|
- printk( "Writing pagedir...");
|
|
|
- for_each_pb_page (pbe, pagedir_nosave) {
|
|
|
- if ((error = write_page((unsigned long)pbe, &swsusp_info.pagedir[n++])))
|
|
|
- return error;
|
|
|
+ printk("Saving image metadata ... ");
|
|
|
+ buf = (unsigned long *)get_zeroed_page(GFP_ATOMIC);
|
|
|
+ if (!buf)
|
|
|
+ return -ENOMEM;
|
|
|
+ p = pblist;
|
|
|
+ while (p) {
|
|
|
+ p = pack_orig_addresses(buf, p);
|
|
|
+ error = swap_map_write_page(handle, (unsigned long)buf);
|
|
|
+ if (error)
|
|
|
+ break;
|
|
|
+ n++;
|
|
|
}
|
|
|
-
|
|
|
- swsusp_info.pagedir_pages = n;
|
|
|
- printk("done (%u pages)\n", n);
|
|
|
+ free_page((unsigned long)buf);
|
|
|
+ if (!error)
|
|
|
+ printk("done (%u pages saved)\n", n);
|
|
|
return error;
|
|
|
}
|
|
|
|
|
@@ -384,33 +547,48 @@ static int enough_swap(unsigned int nr_pages)
|
|
|
|
|
|
/**
|
|
|
* write_suspend_image - Write entire image and metadata.
|
|
|
- *
|
|
|
*/
|
|
|
-static int write_suspend_image(void)
|
|
|
+static int write_suspend_image(struct pbe *pblist, unsigned int nr_pages)
|
|
|
{
|
|
|
+ struct swap_map_page *swap_map;
|
|
|
+ struct swap_map_handle handle;
|
|
|
int error;
|
|
|
|
|
|
- if (!enough_swap(nr_copy_pages)) {
|
|
|
+ if (!enough_swap(nr_pages)) {
|
|
|
printk(KERN_ERR "swsusp: Not enough free swap\n");
|
|
|
return -ENOSPC;
|
|
|
}
|
|
|
|
|
|
- init_header();
|
|
|
- if ((error = data_write()))
|
|
|
- goto FreeData;
|
|
|
+ init_header(nr_pages);
|
|
|
+ swap_map = alloc_swap_map(swsusp_info.pages);
|
|
|
+ if (!swap_map)
|
|
|
+ return -ENOMEM;
|
|
|
+ init_swap_map_handle(&handle, swap_map);
|
|
|
|
|
|
- if ((error = write_pagedir()))
|
|
|
- goto FreePagedir;
|
|
|
+ error = save_image_metadata(pblist, &handle);
|
|
|
+ if (!error)
|
|
|
+ error = save_image_data(pblist, &handle, nr_pages);
|
|
|
+ if (error)
|
|
|
+ goto Free_image_entries;
|
|
|
|
|
|
- if ((error = close_swap()))
|
|
|
- goto FreePagedir;
|
|
|
- Done:
|
|
|
+ swap_map = reverse_swap_map(swap_map);
|
|
|
+ error = save_swap_map(swap_map, &swsusp_info.start);
|
|
|
+ if (error)
|
|
|
+ goto Free_map_entries;
|
|
|
+
|
|
|
+ error = close_swap();
|
|
|
+ if (error)
|
|
|
+ goto Free_map_entries;
|
|
|
+
|
|
|
+Free_swap_map:
|
|
|
+ free_swap_map(swap_map);
|
|
|
return error;
|
|
|
- FreePagedir:
|
|
|
- free_pagedir_entries();
|
|
|
- FreeData:
|
|
|
- data_free();
|
|
|
- goto Done;
|
|
|
+
|
|
|
+Free_map_entries:
|
|
|
+ free_swap_map_entries(swap_map);
|
|
|
+Free_image_entries:
|
|
|
+ free_image_entries(swap_map);
|
|
|
+ goto Free_swap_map;
|
|
|
}
|
|
|
|
|
|
/* It is important _NOT_ to umount filesystems at this point. We want
|
|
@@ -418,7 +596,7 @@ static int write_suspend_image(void)
|
|
|
* filesystem clean: it is not. (And it does not matter, if we resume
|
|
|
* correctly, we'll mark system clean, anyway.)
|
|
|
*/
|
|
|
-int swsusp_write(void)
|
|
|
+int swsusp_write(struct pbe *pblist, unsigned int nr_pages)
|
|
|
{
|
|
|
int error;
|
|
|
|
|
@@ -427,14 +605,12 @@ int swsusp_write(void)
|
|
|
return error;
|
|
|
}
|
|
|
lock_swapdevices();
|
|
|
- error = write_suspend_image();
|
|
|
+ error = write_suspend_image(pblist, nr_pages);
|
|
|
/* This will unlock ignored swap devices since writing is finished */
|
|
|
lock_swapdevices();
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
int swsusp_suspend(void)
|
|
|
{
|
|
|
int error;
|
|
@@ -531,7 +707,6 @@ 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;
|
|
|
}
|
|
@@ -611,6 +786,61 @@ static int bio_write_page(pgoff_t page_off, void *page)
|
|
|
return submit(WRITE, page_off, page);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * The following functions allow us to read data using a swap map
|
|
|
+ * in a file-alike way
|
|
|
+ */
|
|
|
+
|
|
|
+static inline void release_swap_map_reader(struct swap_map_handle *handle)
|
|
|
+{
|
|
|
+ if (handle->cur)
|
|
|
+ free_page((unsigned long)handle->cur);
|
|
|
+ handle->cur = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int get_swap_map_reader(struct swap_map_handle *handle,
|
|
|
+ swp_entry_t start)
|
|
|
+{
|
|
|
+ int error;
|
|
|
+
|
|
|
+ if (!swp_offset(start))
|
|
|
+ return -EINVAL;
|
|
|
+ handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
|
|
|
+ if (!handle->cur)
|
|
|
+ return -ENOMEM;
|
|
|
+ error = bio_read_page(swp_offset(start), handle->cur);
|
|
|
+ if (error) {
|
|
|
+ release_swap_map_reader(handle);
|
|
|
+ return error;
|
|
|
+ }
|
|
|
+ handle->k = 0;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int swap_map_read_page(struct swap_map_handle *handle, void *buf)
|
|
|
+{
|
|
|
+ unsigned long offset;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ if (!handle->cur)
|
|
|
+ return -EINVAL;
|
|
|
+ offset = swp_offset(handle->cur->entries[handle->k]);
|
|
|
+ if (!offset)
|
|
|
+ return -EINVAL;
|
|
|
+ error = bio_read_page(offset, buf);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+ if (++handle->k >= MAP_PAGE_SIZE) {
|
|
|
+ handle->k = 0;
|
|
|
+ offset = swp_offset(handle->cur->next_swap);
|
|
|
+ if (!offset)
|
|
|
+ release_swap_map_reader(handle);
|
|
|
+ else
|
|
|
+ error = bio_read_page(offset, handle->cur);
|
|
|
+ }
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Sanity check if this image makes sense with this kernel/swap context
|
|
|
* I really don't think that it's foolproof but more than nothing..
|
|
@@ -639,7 +869,6 @@ static const char *sanity_check(void)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
static int check_header(void)
|
|
|
{
|
|
|
const char *reason = NULL;
|
|
@@ -653,7 +882,6 @@ static int check_header(void)
|
|
|
printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason);
|
|
|
return -EPERM;
|
|
|
}
|
|
|
- nr_copy_pages = swsusp_info.image_pages;
|
|
|
return error;
|
|
|
}
|
|
|
|
|
@@ -680,75 +908,88 @@ static int check_sig(void)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * data_read - Read image pages from swap.
|
|
|
- *
|
|
|
- * You do not need to check for overlaps, check_pagedir()
|
|
|
- * already did that.
|
|
|
+ * load_image_data - load the image data using the swap map handle
|
|
|
+ * @handle and store them using the page backup list @pblist
|
|
|
+ * (assume there are @nr_pages pages to load)
|
|
|
*/
|
|
|
|
|
|
-static int data_read(struct pbe *pblist)
|
|
|
+static int load_image_data(struct pbe *pblist,
|
|
|
+ struct swap_map_handle *handle,
|
|
|
+ unsigned int nr_pages)
|
|
|
{
|
|
|
+ int error;
|
|
|
+ unsigned int m;
|
|
|
struct pbe *p;
|
|
|
- int error = 0;
|
|
|
- int i = 0;
|
|
|
- int mod = swsusp_info.image_pages / 100;
|
|
|
-
|
|
|
- if (!mod)
|
|
|
- mod = 1;
|
|
|
-
|
|
|
- printk("swsusp: Reading image data (%lu pages): ",
|
|
|
- swsusp_info.image_pages);
|
|
|
-
|
|
|
- for_each_pbe (p, pblist) {
|
|
|
- if (!(i % mod))
|
|
|
- printk("\b\b\b\b%3d%%", i / mod);
|
|
|
|
|
|
- if ((error = bio_read_page(swp_offset(p->swap_address),
|
|
|
- (void *)p->address)))
|
|
|
- return error;
|
|
|
-
|
|
|
- i++;
|
|
|
+ if (!pblist)
|
|
|
+ return -EINVAL;
|
|
|
+ printk("Loading image data pages (%u pages) ... ", nr_pages);
|
|
|
+ m = nr_pages / 100;
|
|
|
+ if (!m)
|
|
|
+ m = 1;
|
|
|
+ nr_pages = 0;
|
|
|
+ p = pblist;
|
|
|
+ while (p) {
|
|
|
+ error = swap_map_read_page(handle, (void *)p->address);
|
|
|
+ if (error)
|
|
|
+ break;
|
|
|
+ p = p->next;
|
|
|
+ if (!(nr_pages % m))
|
|
|
+ printk("\b\b\b\b%3d%%", nr_pages / m);
|
|
|
+ nr_pages++;
|
|
|
}
|
|
|
- printk("\b\b\b\bdone\n");
|
|
|
+ if (!error)
|
|
|
+ printk("\b\b\b\bdone\n");
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * read_pagedir - Read page backup list pages from swap
|
|
|
+ * unpack_orig_addresses - copy the elements of @buf[] (1 page) to
|
|
|
+ * the PBEs in the list starting at @pbe
|
|
|
*/
|
|
|
|
|
|
-static int read_pagedir(struct pbe *pblist)
|
|
|
+static inline struct pbe *unpack_orig_addresses(unsigned long *buf,
|
|
|
+ struct pbe *pbe)
|
|
|
{
|
|
|
- struct pbe *pbpage, *p;
|
|
|
- unsigned int i = 0;
|
|
|
- int error;
|
|
|
+ int j;
|
|
|
|
|
|
- if (!pblist)
|
|
|
- return -EFAULT;
|
|
|
+ for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) {
|
|
|
+ pbe->orig_address = buf[j];
|
|
|
+ pbe = pbe->next;
|
|
|
+ }
|
|
|
+ return pbe;
|
|
|
+}
|
|
|
|
|
|
- printk("swsusp: Reading pagedir (%lu pages)\n",
|
|
|
- swsusp_info.pagedir_pages);
|
|
|
+/**
|
|
|
+ * load_image_metadata - load the image metadata using the swap map
|
|
|
+ * handle @handle and put them into the PBEs in the list @pblist
|
|
|
+ */
|
|
|
|
|
|
- for_each_pb_page (pbpage, pblist) {
|
|
|
- unsigned long offset = swp_offset(swsusp_info.pagedir[i++]);
|
|
|
+static int load_image_metadata(struct pbe *pblist, struct swap_map_handle *handle)
|
|
|
+{
|
|
|
+ struct pbe *p;
|
|
|
+ unsigned long *buf;
|
|
|
+ unsigned int n = 0;
|
|
|
+ int error = 0;
|
|
|
|
|
|
- error = -EFAULT;
|
|
|
- if (offset) {
|
|
|
- p = (pbpage + PB_PAGE_SKIP)->next;
|
|
|
- error = bio_read_page(offset, (void *)pbpage);
|
|
|
- (pbpage + PB_PAGE_SKIP)->next = p;
|
|
|
- }
|
|
|
+ printk("Loading image metadata ... ");
|
|
|
+ buf = (unsigned long *)get_zeroed_page(GFP_ATOMIC);
|
|
|
+ if (!buf)
|
|
|
+ return -ENOMEM;
|
|
|
+ p = pblist;
|
|
|
+ while (p) {
|
|
|
+ error = swap_map_read_page(handle, buf);
|
|
|
if (error)
|
|
|
break;
|
|
|
+ p = unpack_orig_addresses(buf, p);
|
|
|
+ n++;
|
|
|
}
|
|
|
-
|
|
|
+ free_page((unsigned long)buf);
|
|
|
if (!error)
|
|
|
- BUG_ON(i != swsusp_info.pagedir_pages);
|
|
|
-
|
|
|
+ printk("done (%u pages loaded)\n", n);
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
static int check_suspend_image(void)
|
|
|
{
|
|
|
int error = 0;
|
|
@@ -762,34 +1003,39 @@ static int check_suspend_image(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int read_suspend_image(void)
|
|
|
+static int read_suspend_image(struct pbe **pblist_ptr)
|
|
|
{
|
|
|
int error = 0;
|
|
|
- struct pbe *p;
|
|
|
+ struct pbe *p, *pblist;
|
|
|
+ struct swap_map_handle handle;
|
|
|
+ unsigned int nr_pages = swsusp_info.image_pages;
|
|
|
|
|
|
- if (!(p = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 0)))
|
|
|
+ p = alloc_pagedir(nr_pages, GFP_ATOMIC, 0);
|
|
|
+ if (!p)
|
|
|
return -ENOMEM;
|
|
|
-
|
|
|
- if ((error = read_pagedir(p)))
|
|
|
+ error = get_swap_map_reader(&handle, swsusp_info.start);
|
|
|
+ if (error)
|
|
|
+ /* The PBE list at p will be released by swsusp_free() */
|
|
|
return error;
|
|
|
- create_pbe_list(p, nr_copy_pages);
|
|
|
- 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);
|
|
|
+ error = load_image_metadata(p, &handle);
|
|
|
+ if (!error) {
|
|
|
+ mark_unsafe_pages(p);
|
|
|
+ pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, 1);
|
|
|
+ if (pblist)
|
|
|
+ copy_page_backup_list(pblist, p);
|
|
|
+ free_pagedir(p);
|
|
|
+ if (!pblist)
|
|
|
+ error = -ENOMEM;
|
|
|
+
|
|
|
+ /* Allocate memory for the image and read the data from swap */
|
|
|
+ if (!error)
|
|
|
+ error = alloc_data_pages(pblist, GFP_ATOMIC, 1);
|
|
|
+ if (!error)
|
|
|
+ error = load_image_data(pblist, &handle, nr_pages);
|
|
|
+ if (!error)
|
|
|
+ *pblist_ptr = pblist;
|
|
|
}
|
|
|
- free_pagedir(p);
|
|
|
- if (!pagedir_nosave)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- /* Allocate memory for the image and read the data from swap */
|
|
|
-
|
|
|
- error = alloc_data_pages(pagedir_nosave, GFP_ATOMIC, 1);
|
|
|
-
|
|
|
- if (!error)
|
|
|
- error = data_read(pagedir_nosave);
|
|
|
-
|
|
|
+ release_swap_map_reader(&handle);
|
|
|
return error;
|
|
|
}
|
|
|
|
|
@@ -821,7 +1067,7 @@ int swsusp_check(void)
|
|
|
* swsusp_read - Read saved image from swap.
|
|
|
*/
|
|
|
|
|
|
-int swsusp_read(void)
|
|
|
+int swsusp_read(struct pbe **pblist_ptr)
|
|
|
{
|
|
|
int error;
|
|
|
|
|
@@ -830,7 +1076,7 @@ int swsusp_read(void)
|
|
|
return PTR_ERR(resume_bdev);
|
|
|
}
|
|
|
|
|
|
- error = read_suspend_image();
|
|
|
+ error = read_suspend_image(pblist_ptr);
|
|
|
blkdev_put(resume_bdev);
|
|
|
|
|
|
if (!error)
|