[RFC][PATCH 3/6] swsusp: introduce the swap map structure and interface functions

From: Rafael J. Wysocki
Date: Sat Oct 29 2005 - 15:55:58 EST


This is the main part. It introduces a new data structure for the
swap-handling part of swsusp (the swap map structure, described in a comment)
and new functions for writing the image data to and reading them from swap.
It also introduces the interface functions allowing the snapshot-handling part
to communicate with the swap-handling part and modifies the struct pbe
structure (the swap_address member of it is no longer needed as the
swap-handling part uses its own independent data structures).

Signed-off-by: Rafael J. Wysocki <rjw@xxxxxxx>

include/linux/suspend.h | 6
kernel/power/power.h | 25 +-
kernel/power/snapshot.c | 174 +++++++++++++----
kernel/power/swsusp.c | 475 ++++++++++++++++++++++++++----------------------
4 files changed, 404 insertions(+), 276 deletions(-)

Index: linux-2.6.14-rc5-mm1/kernel/power/swsusp.c
===================================================================
--- linux-2.6.14-rc5-mm1.orig/kernel/power/swsusp.c 2005-10-29 13:24:56.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/swsusp.c 2005-10-29 14:01:42.000000000 +0200
@@ -33,6 +33,9 @@
* Andreas Steinmetz <ast@xxxxxxxx>:
* Added encrypted suspend option
*
+ * Rafael J. Wysocki <rjw@xxxxxxx>
+ * 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
@@ -79,24 +82,6 @@

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 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"

@@ -187,12 +172,12 @@
crypto_free_tfm((struct crypto_tfm *)mem);
}

-static __inline__ int crypto_write(struct pbe *p, void *mem)
+static __inline__ int crypto_write(unsigned long addr, swp_entry_t *entry, void *mem)
{
int error = 0;
struct scatterlist src, dst;

- src.page = virt_to_page(p->address);
+ src.page = virt_to_page((void *)addr);
src.offset = 0;
src.length = PAGE_SIZE;
dst.page = virt_to_page((void *)&swsusp_header);
@@ -203,23 +188,22 @@
PAGE_SIZE);

if (!error)
- error = write_page((unsigned long)&swsusp_header,
- &(p->swap_address));
+ error = write_page((unsigned long)&swsusp_header, entry);
return error;
}

-static __inline__ int crypto_read(struct pbe *p, void *mem)
+static __inline__ int crypto_read(unsigned long offset, void *buf, void *mem)
{
int error = 0;
struct scatterlist src, dst;

- error = bio_read_page(swp_offset(p->swap_address), (void *)p->address);
+ error = bio_read_page(offset, buf);
if (!error) {
src.offset = 0;
src.length = PAGE_SIZE;
dst.offset = 0;
dst.length = PAGE_SIZE;
- src.page = dst.page = virt_to_page((void *)p->address);
+ src.page = dst.page = virt_to_page(buf);

error = crypto_cipher_decrypt((struct crypto_tfm *)mem, &dst,
&src, PAGE_SIZE);
@@ -236,14 +220,14 @@
{
}

-static __inline__ int crypto_write(struct pbe *p, void *mem)
+static __inline__ int crypto_write(unsigned long addr, swp_entry_t *entry, void *mem)
{
- return write_page(p->address, &(p->swap_address));
+ return write_page(addr, entry);
}

-static __inline__ int crypto_read(struct pbe *p, void *mem)
+static __inline__ int crypto_read(unsigned long offset, void *buf, void *mem)
{
- return bio_read_page(swp_offset(p->swap_address), (void *)p->address);
+ return bio_read_page(offset, buf);
}
#endif

@@ -374,59 +358,6 @@
return error;
}

-/**
- * data_free - Free the swap entries used by the saved image.
- *
- * Walk the list of used swap entries and free each one.
- * This is only used for cleanup when suspend fails.
- */
-static void data_free(void)
-{
- swp_entry_t entry;
- struct pbe * p;
-
- for_each_pbe(p, pagedir_nosave) {
- entry = p->swap_address;
- if (entry.val)
- swap_free(entry);
- else
- break;
- }
-}
-
-/**
- * data_write - Write saved image to swap.
- *
- * Walk the list of pages in the image and sync each one to swap.
- */
-static int data_write(void)
-{
- int error = 0, i = 0;
- unsigned int mod = nr_copy_pages / 100;
- struct pbe *p;
- void *tfm;
-
- if ((error = crypto_init(1, &tfm)))
- return error;
-
- if (!mod)
- mod = 1;
-
- 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 = crypto_write(p, tfm))) {
- crypto_exit(tfm);
- return error;
- }
- i++;
- }
- printk("\b\b\b\bdone\n");
- crypto_exit(tfm);
- return error;
-}
-
static void dump_info(void)
{
pr_debug(" swsusp: Version: %u\n",swsusp_info.version_code);
@@ -439,19 +370,19 @@
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 nr_pages, unsigned img_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 = img_pages;
+ swsusp_info.pages = nr_pages;
}

static int close_swap(void)
@@ -470,40 +401,167 @@
}

/**
- * free_pagedir_entries - Free pages used by the page directory.
+ * Swap map handling functions
*
- * This is used during suspend for error recovery.
+ * The swap map is a data structure used for keeping track of each page
+ * written to the swap. It consists of many swp_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_swp (in swap) member.
+ *
+ * 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 swp_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 swp_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 swp_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 swp_map_page structures
+ * and the second for reading the data pages from swap.
*/

-static void free_pagedir_entries(void)
+#define MAP_PAGE_SIZE ((PAGE_SIZE - sizeof(swp_entry_t) - sizeof(void *)) \
+ / sizeof(swp_entry_t))
+
+struct swp_map_page {
+ swp_entry_t entries[MAP_PAGE_SIZE];
+ swp_entry_t next_swp;
+ struct swp_map_page *next;
+};
+
+typedef struct swp_map_page swp_map_t;
+
+static inline void free_swp_map(swp_map_t *swp_map)
{
- int i;
+ swp_map_t *swp;
+
+ while (swp_map) {
+ swp = swp_map->next;
+ free_page((unsigned long)swp_map);
+ swp_map = swp;
+ }
+}

- for (i = 0; i < swsusp_info.pagedir_pages; i++)
- swap_free(swsusp_info.pagedir[i]);
+static swp_map_t *alloc_swp_map(unsigned nr_pages)
+{
+ swp_map_t *swp_map, *swp;
+ unsigned n = 0;
+
+ if (!nr_pages)
+ return NULL;
+
+ pr_debug("alloc_swp_map(): nr_pages = %d\n", nr_pages);
+ swp_map = (swp_map_t *)get_zeroed_page(GFP_ATOMIC);
+ swp = swp_map;
+ for (n = MAP_PAGE_SIZE; n < nr_pages; n += MAP_PAGE_SIZE) {
+ swp->next = (swp_map_t *)get_zeroed_page(GFP_ATOMIC);
+ swp = swp->next;
+ if (!swp) {
+ free_swp_map(swp_map);
+ return NULL;
+ }
+ }
+ return swp_map;
}

+static inline swp_map_t *reverse_swp_map(swp_map_t *swp_map)
+{
+ swp_map_t *prev, *next;
+
+ prev = NULL;
+ while (swp_map) {
+ next = swp_map->next;
+ swp_map->next = prev;
+ prev = swp_map;
+ swp_map = next;
+ }
+ return prev;
+}

/**
- * write_pagedir - Write the array of pages holding the page directory.
- * @last: Last swap entry we write (needed for header).
+ * save_swp_map - save the swap map used for tracing the data pages
+ * stored in swap
*/

-static int write_pagedir(void)
+static int save_swp_map(swp_map_t *swp_map, swp_entry_t *start)
{
- int error = 0;
- unsigned n = 0;
- struct pbe * pbe;
+ swp_entry_t entry = (swp_entry_t){0};
+ int error;

- printk( "Writing pagedir...");
- for_each_pb_page (pbe, pagedir_nosave) {
- if ((error = write_page((unsigned long)pbe, &swsusp_info.pagedir[n++])))
+ while (swp_map) {
+ swp_map->next_swp = entry;
+ if ((error = write_page((unsigned long)swp_map, &entry)))
return error;
+ swp_map = swp_map->next;
}
+ *start = entry;
+ return 0;
+}

- swsusp_info.pagedir_pages = n;
- printk("done (%u pages)\n", n);
- return error;
+static inline void free_swp_map_entries(swp_map_t *swp_map)
+{
+ while (swp_map) {
+ if (swp_map->next_swp.val)
+ swap_free(swp_map->next_swp);
+ swp_map = swp_map->next;
+ }
+}
+
+/**
+ * save_image - save the image data provided by the snapshot-handling
+ * part to swap. The swap map is used for keeping track of the
+ * saved pages
+ */
+
+static int save_image(swp_map_t *swp, unsigned nr_pages, void *buf)
+{
+ unsigned n, k;
+ int error;
+ unsigned mod = nr_pages / 100;
+ void *tfm;
+
+ if ((error = crypto_init(1, &tfm)))
+ return error;
+ printk("Writing data to swap (%d pages) ... ", nr_pages);
+ n = 0;
+ while (swp) {
+ for (k = 0; k < MAP_PAGE_SIZE && n < nr_pages; k++, n++) {
+ error = snapshot_send_page(buf);
+ if (!error)
+ error = crypto_write((unsigned long)buf,
+ swp->entries + k, tfm);
+ if (error) {
+ crypto_exit(tfm);
+ return error;
+ }
+ if (!(n % mod))
+ printk("\b\b\b\b%3d%%", n / mod);
+ }
+ swp = swp->next;
+ }
+ printk("\b\b\b\bdone\n");
+ crypto_exit(tfm);
+ return 0;
+}
+
+static inline void free_pages_entries(swp_map_t *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;
+ }
}

/**
@@ -532,30 +590,49 @@
*/
static int write_suspend_image(void)
{
+ unsigned nr_pages;
int error;
+ swp_map_t *swp_map;
+ void *buffer;

- if (!enough_swap(nr_copy_pages)) {
+ nr_pages = snapshot_pages_to_save();
+ if (!enough_swap(nr_pages)) {
printk(KERN_ERR "swsusp: Not enough free swap\n");
return -ENOSPC;
}

- init_header();
- if ((error = data_write()))
- goto FreeData;
-
- if ((error = write_pagedir()))
- goto FreePagedir;
+ init_header(nr_pages, snapshot_image_pages());
+ buffer = (void *)get_zeroed_page(GFP_ATOMIC);
+ if (!buffer)
+ return -ENOMEM;

+ swp_map = alloc_swp_map(nr_pages);
+ if (!swp_map) {
+ free_page((unsigned long)buffer);
+ return -ENOMEM;
+ }
+ snapshot_send_init();
+ if ((error = save_image(swp_map, nr_pages, buffer)))
+ goto Free_pages_entries;
+ if ((error = snapshot_finish()))
+ goto Free_pages_entries;
+ swp_map = reverse_swp_map(swp_map);
+ if ((error = save_swp_map(swp_map, &swsusp_info.start)))
+ goto Free_map_entries;
if ((error = close_swap()))
- goto FreePagedir;
- Done:
+ goto Free_map_entries;
+
+Free_mem:
+ free_swp_map(swp_map);
+ free_page((unsigned long)buffer);
memset(key_iv, 0, MAXKEY+MAXIV);
return error;
- FreePagedir:
- free_pagedir_entries();
- FreeData:
- data_free();
- goto Done;
+
+Free_map_entries:
+ free_swp_map_entries(swp_map);
+Free_pages_entries:
+ free_pages_entries(swp_map);
+ goto Free_mem;
}

/* It is important _NOT_ to umount filesystems at this point. We want
@@ -719,6 +796,65 @@
return submit(WRITE, page_off, page);
}

+/**
+ * load_image - Load the image and metadata from swap, using the
+ * snapshot_recv_page() function provided by the snapshot-handling code
+ *
+ * We assume that the data has been saved using the swap map handling
+ * functions above
+ */
+static int load_image(unsigned nr_pages, swp_entry_t start)
+{
+ swp_map_t *swp;
+ void *buf;
+ unsigned n, k;
+ unsigned long offset = swp_offset(start);
+ int error;
+ unsigned mod = nr_pages / 100;
+ void *tfm;
+
+ if (!nr_pages || !offset)
+ return -EINVAL;
+
+ if ((error = crypto_init(0, &tfm)))
+ return error;
+
+ buf = (void *)get_zeroed_page(GFP_ATOMIC);
+ if (!buf) {
+ error = -ENOMEM;
+ goto Crypto_exit;
+ }
+ swp = (swp_map_t *)get_zeroed_page(GFP_ATOMIC);
+ if (!swp) {
+ error = -ENOMEM;
+ goto Free_buf;
+ }
+ printk("Loading data from swap (%d pages) ... ", nr_pages);
+ n = 0;
+ while (n < nr_pages) {
+ if ((error = crypto_read(offset, (void *)swp, tfm)))
+ goto Free;
+ for (k = 0; k < MAP_PAGE_SIZE && n < nr_pages; k++, n++) {
+ error = bio_read_page(swp_offset(swp->entries[k]), buf);
+ if (!error)
+ error = snapshot_recv_page(buf);
+ if (error)
+ goto Free;
+ if (!(n % mod))
+ printk("\b\b\b\b%3d%%", n / mod);
+ }
+ offset = swp_offset(swp->next_swp);
+ }
+ printk("\b\b\b\bdone\n");
+Free:
+ free_page((unsigned long)swp);
+Free_buf:
+ free_page((unsigned long)buf);
+Crypto_exit:
+ crypto_exit(tfm);
+ 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..
@@ -761,7 +897,6 @@
printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason);
return -EPERM;
}
- nr_copy_pages = swsusp_info.image_pages;
return error;
}

@@ -789,82 +924,6 @@
return error;
}

-/**
- * data_read - Read image pages from swap.
- *
- * You do not need to check for overlaps, check_pagedir()
- * already did that.
- */
-
-static int data_read(struct pbe *pblist)
-{
- struct pbe * p;
- int error = 0;
- int i = 0;
- int mod = swsusp_info.image_pages / 100;
- void *tfm;
-
- if ((error = crypto_init(0, &tfm)))
- return error;
-
- 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 = crypto_read(p, tfm))) {
- crypto_exit(tfm);
- return error;
- }
-
- i++;
- }
- printk("\b\b\b\bdone\n");
- crypto_exit(tfm);
- return error;
-}
-
-/**
- * read_pagedir - Read page backup list pages from swap
- */
-
-static int read_pagedir(struct pbe *pblist)
-{
- struct pbe *pbpage, *p;
- unsigned i = 0;
- int error;
-
- if (!pblist)
- return -EFAULT;
-
- printk("swsusp: Reading pagedir (%lu pages)\n",
- swsusp_info.pagedir_pages);
-
- for_each_pb_page (pbpage, pblist) {
- unsigned long offset = swp_offset(swsusp_info.pagedir[i++]);
-
- error = -EFAULT;
- if (offset) {
- p = (pbpage + PB_PAGE_SKIP)->next;
- error = bio_read_page(offset, (void *)pbpage);
- (pbpage + PB_PAGE_SKIP)->next = p;
- }
- if (error)
- break;
- }
-
- if (!error)
- BUG_ON(i != swsusp_info.pagedir_pages);
-
- return error;
-}
-
-
static int check_suspend_image(void)
{
int error = 0;
@@ -880,29 +939,13 @@

static int read_suspend_image(void)
{
- int error = 0;
- struct pbe *p;
-
- if (!(p = alloc_pagedir(nr_copy_pages)))
- return -ENOMEM;
-
- if ((error = read_pagedir(p)))
- return error;
-
- create_pbe_list(p, nr_copy_pages);
-
- pr_debug("swsusp: Relocating pagedir (%lu pages to check)\n",
- swsusp_info.pagedir_pages);
- if (!(pagedir_nosave = swsusp_pagedir_relocate(p)))
- return -ENOMEM;
-
- /* Allocate memory for the image and read the data from swap */
-
- error = check_pagedir(pagedir_nosave);
+ int error;

+ error = snapshot_recv_init(swsusp_info.pages, swsusp_info.image_pages);
if (!error)
- error = data_read(pagedir_nosave);
-
+ error = load_image(swsusp_info.pages, swsusp_info.start);
+ if (!error)
+ error = snapshot_finish();
return error;
}

Index: linux-2.6.14-rc5-mm1/kernel/power/power.h
===================================================================
--- linux-2.6.14-rc5-mm1.orig/kernel/power/power.h 2005-10-29 13:24:56.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/power.h 2005-10-29 14:01:41.000000000 +0200
@@ -9,19 +9,14 @@
#define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
#endif

-#define MAX_PBES ((PAGE_SIZE - sizeof(struct new_utsname) \
- - 4 - 3*sizeof(unsigned long) - sizeof(int) \
- - sizeof(void *)) / sizeof(swp_entry_t))
-
struct swsusp_info {
struct new_utsname uts;
u32 version_code;
unsigned long num_physpages;
int cpus;
unsigned long image_pages;
- unsigned long pagedir_pages;
- suspend_pagedir_t * suspend_pagedir;
- swp_entry_t pagedir[MAX_PBES];
+ unsigned long pages;
+ swp_entry_t start;
} __attribute__((aligned(PAGE_SIZE)));


@@ -58,17 +53,19 @@
/* References to section boundaries */
extern const void __nosave_begin, __nosave_end;

-extern unsigned int nr_copy_pages;
-extern suspend_pagedir_t *pagedir_nosave;
-extern suspend_pagedir_t *pagedir_save;
+extern struct pbe *pagedir_nosave;

extern asmlinkage int swsusp_arch_suspend(void);
extern asmlinkage int swsusp_arch_resume(void);

extern int save_highmem(void);
extern int restore_highmem(void);
-extern struct pbe * alloc_pagedir(unsigned nr_pages);
-extern void create_pbe_list(struct pbe *pblist, unsigned nr_pages);
-extern int check_pagedir(struct pbe *pblist);
-extern struct pbe * swsusp_pagedir_relocate(struct pbe *pblist);
extern void swsusp_free(void);
+
+extern unsigned snapshot_pages_to_save(void);
+extern unsigned snapshot_image_pages(void);
+extern void snapshot_send_init(void);
+extern int snapshot_send_page(void *buf);
+extern int snapshot_recv_init(unsigned nr_pages, unsigned img_pages);
+extern int snapshot_recv_page(void *buf);
+extern int snapshot_finish(void);
Index: linux-2.6.14-rc5-mm1/kernel/power/snapshot.c
===================================================================
--- linux-2.6.14-rc5-mm1.orig/kernel/power/snapshot.c 2005-10-29 13:24:56.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/snapshot.c 2005-10-29 14:11:17.000000000 +0200
@@ -4,6 +4,7 @@
* This file provide system snapshot/restore functionality.
*
* Copyright (C) 1998-2005 Pavel Machek <pavel@xxxxxxx>
+ * Copyright (C) 2005 Rafael J. Wysocki <rjw@xxxxxxx>
*
* This file is released under the GPLv2, and is based on swsusp.c.
*
@@ -33,6 +34,11 @@

#include "power.h"

+struct pbe *pagedir_nosave = NULL;
+
+static unsigned nr_copy_pages;
+static unsigned nr_pb_pages;
+
#ifdef CONFIG_HIGHMEM
struct highmem_page {
char *data;
@@ -250,7 +256,7 @@
* of memory pages allocated with alloc_pagedir()
*/

-void create_pbe_list(struct pbe *pblist, unsigned nr_pages)
+static void create_pbe_list(struct pbe *pblist, unsigned nr_pages)
{
struct pbe *pbpage, *p;
unsigned num = PBES_PER_PAGE;
@@ -293,7 +299,7 @@
* On each page we set up a list of struct_pbe elements.
*/

-struct pbe *alloc_pagedir(unsigned nr_pages)
+static struct pbe *alloc_pagedir(unsigned nr_pages)
{
unsigned num;
struct pbe *pblist, *pbe;
@@ -393,10 +399,6 @@
(nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE,
PAGES_FOR_IO, nr_free_pages());

- /* This is needed because of the fixed size of swsusp_info */
- if (MAX_PBES < (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE)
- return -ENOSPC;
-
if (!enough_free_mem(nr_pages)) {
printk(KERN_ERR "swsusp: Not enough free memory\n");
return -ENOMEM;
@@ -419,6 +421,7 @@
*/

nr_copy_pages = nr_pages;
+ nr_pb_pages = (nr_pages * sizeof(long) + PAGE_SIZE - 1) >> PAGE_SHIFT;

printk("swsusp: critical section/: done (%d pages copied)\n", nr_pages);
return 0;
@@ -454,48 +457,22 @@
}

/**
- * 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
- */
-
-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
+ * relocate_pbe_list - 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.
*/

-struct pbe * swsusp_pagedir_relocate(struct pbe *pblist)
+static struct pbe *relocate_pbe_list(struct pbe *pblist)
{
struct zone *zone;
unsigned long zone_pfn;
struct pbe *pbpage, *tail, *p;
void *m;
- int rel = 0;

if (!pblist) /* a sanity check */
return NULL;

/* 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))
@@ -504,13 +481,11 @@
}

/* Mark orig addresses */
-
for_each_pbe (p, pblist)
SetPageNosaveFree(virt_to_page(p->orig_address));

- tail = pblist + PB_PAGE_SKIP;
-
/* Relocate colliding pages */
+ tail = pblist + PB_PAGE_SKIP;

for_each_pb_page (pbpage, pblist) {
if (PageNosaveFree(virt_to_page((unsigned long)pbpage))) {
@@ -528,8 +503,6 @@
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;
}
@@ -540,7 +513,126 @@
SetPageNosaveFree(virt_to_page(pbpage));
}

- printk("swsusp: Relocated %d pages\n", rel);
-
return pblist;
}
+
+unsigned snapshot_pages_to_save(void)
+{
+ return nr_copy_pages + nr_pb_pages;
+}
+
+unsigned snapshot_image_pages(void)
+{
+ return nr_copy_pages;
+}
+
+static unsigned current_page = 0;
+static struct pbe *current_pbe;
+static struct pbe *current_pblist;
+
+void snapshot_send_init(void)
+{
+ current_page = 0;
+ current_pbe = pagedir_nosave;
+ current_pblist = NULL;
+}
+
+static void prepare_next_pb_page(unsigned long *buf)
+{
+ struct pbe *p;
+ unsigned n;
+
+ p = current_pbe;
+ for (n = 0; n < PAGE_SIZE/sizeof(long) && p; n++) {
+ buf[n] = p->orig_address;
+ p = p->next;
+ }
+ current_pbe = p;
+}
+
+int snapshot_send_page(void *buf)
+{
+ if (current_page >= nr_copy_pages + nr_pb_pages)
+ return -EINVAL;
+ if (current_page < nr_pb_pages) {
+ prepare_next_pb_page(buf);
+ if (!current_pbe)
+ current_pbe = pagedir_nosave;
+ } else {
+ memcpy(buf, (void *)current_pbe->address, PAGE_SIZE);
+ current_pbe = current_pbe->next;
+ }
+ current_page++;
+ return 0;
+}
+
+int snapshot_recv_init(unsigned nr_pages, unsigned img_pages)
+{
+ struct pbe *pblist;
+
+ if (nr_pages <= img_pages)
+ return -EINVAL;
+ /* We have to create a PBE list here */
+ pblist = alloc_pagedir(img_pages);
+ if (!pblist)
+ return -ENOMEM;
+ create_pbe_list(pblist, img_pages);
+ current_pblist = pblist;
+ current_pbe = pblist;
+ current_page = 0;
+ nr_copy_pages = img_pages;
+ nr_pb_pages = nr_pages - img_pages;
+ return 0;
+}
+
+static void load_next_pb_page(unsigned long *buf)
+{
+ struct pbe *p;
+ unsigned n;
+
+ p = current_pbe;
+ for (n = 0; n < PAGE_SIZE/sizeof(long) && p; n++) {
+ p->orig_address = buf[n];
+ p = p->next;
+ }
+ current_pbe = p;
+}
+
+int snapshot_recv_page(void *buf)
+{
+ if (!current_pblist ||
+ current_page >= nr_copy_pages + nr_pb_pages)
+ return -EINVAL;
+ if (current_page < nr_pb_pages) {
+ load_next_pb_page(buf);
+ if (!current_pbe) {
+ current_pblist = relocate_pbe_list(current_pblist);
+ if (!current_pblist) {
+ printk(KERN_ERR "\nswsusp: Not enough memory for relocating PBEs\n");
+ return -ENOMEM;
+ }
+ current_pbe = current_pblist;
+ }
+ } else {
+ current_pbe->address = get_safe_page(GFP_ATOMIC);
+ if (!current_pbe->address) {
+ printk(KERN_ERR "\nswsusp: Not enough memory for the image\n");
+ return -ENOMEM;
+ }
+ memcpy((void *)current_pbe->address, buf, PAGE_SIZE);
+ current_pbe = current_pbe->next;
+ }
+ current_page++;
+ return 0;
+}
+
+int snapshot_finish(void)
+{
+ if (current_pbe)
+ return -EINVAL;
+ current_page = 0;
+ if (current_pblist)
+ pagedir_nosave = current_pblist;
+ current_pblist = NULL;
+ return 0;
+}
Index: linux-2.6.14-rc5-mm1/include/linux/suspend.h
===================================================================
--- linux-2.6.14-rc5-mm1.orig/include/linux/suspend.h 2005-10-29 13:24:56.000000000 +0200
+++ linux-2.6.14-rc5-mm1/include/linux/suspend.h 2005-10-29 14:01:27.000000000 +0200
@@ -14,11 +14,7 @@
typedef struct pbe {
unsigned long address; /* address of the copy */
unsigned long orig_address; /* original address of page */
- swp_entry_t swap_address;
-
- struct pbe *next; /* also used as scratch space at
- * end of page (see link, diskpage)
- */
+ struct pbe *next;
} suspend_pagedir_t;

#define for_each_pbe(pbe, pblist) \

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/