public inbox for linux-pm@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH 0/6] swsusp: rework swap handling
@ 2005-10-29 19:58 Rafael J. Wysocki
  2005-10-29 20:01 ` [RFC][PATCH 1/6] swsusp: rework swsusp_suspend Rafael J. Wysocki
                   ` (6 more replies)
  0 siblings, 7 replies; 17+ messages in thread
From: Rafael J. Wysocki @ 2005-10-29 19:58 UTC (permalink / raw)
  To: Pavel Machek; +Cc: LKML, linux-pm

Hi,

The following series of patches divides swsusp into two functionally
independent subsystems:

- the snapshot-handling part responsible for creating and populating
the snapshot-related data structure which is the list of page backup
entries aka pagedir,

- the swap-handling part responsible for writing the snapshot data to
and reading it from swap.

On suspend the snapshot-handling part creates the system snapshot
and makes the data stored in the snapshot available to the swap-handling
part via an interface function allowing it to transfer the data as
a series of consecutive data pages, in a specific order.  The swap-handling
part writes the data pages to a swap partition is such a way that they
can be read in exactly the same order in which they have been saved.

On resume the snapshot-handling part is invoked by the swap-handling
part to create the pagedir.  Then, the swap-handling part is allowed to
send it, with the help of an interface function, data pages that are used
to populate the snapshot data structure.  It is assumed that the data
pages will be sent in the same order in which they have been received
by the swap-handling part on suspend.  Finally, the system state (from
before suspend) is restored by the snaphot-handling part from the
data structure handled by it.

>From the point of view of the swap-handling part, the contents of the
data pages provided by the snapshot-handling do not matter at all.
It handles each data page in the same way without analyzing its
contents and the snapshot-handling part is responsible for recognizing
the metadata and using them as appropriate.  Consequently, in principle
the swap-handling part can be replaced with a user-space process and
the interface functions used in transferring data between the two parts
of swsusp can be replaced with a relatively simple kernel-user interface
in the future.

The approach used in this series of patches has some additional benefits:
1) the size of the pagedir is reduced by 1/4 which causes some more memory to
be available on resume,
2) the amount of metadata written to swap is reduced by 3/4,
3) the artificial limitation on the pagedir size, imposed by the size of the
swsusp_info structure, is lifted,
4) the size of swsusp_info structure is reduced so it can be merged with the
swsusp_header structure in the future,
5) the swap-handling part does not use any global variables related to the
snapshot data structure,
6) the __nosavedata variables are almost eliminated (on x86-64 the last of them
is the in_suspend variable).

I have divided the changes into some more or less logical steps for clarity.
Although the code has been designed as proof-of-concept, it is functional
and has been tested on x86-64, except for the cryptographic functionality
and error paths.

For your convenience the patches are available from:
http://www.sisk.pl/kernel/patches/2.6.14-rc5-mm1/

I will very much appreciate any comments, remarks and suggestions.

Greetings,
Rafael

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [RFC][PATCH 1/6] swsusp: rework swsusp_suspend
  2005-10-29 19:58 [RFC][PATCH 0/6] swsusp: rework swap handling Rafael J. Wysocki
@ 2005-10-29 20:01 ` Rafael J. Wysocki
  2005-10-29 22:25   ` Pavel Machek
  2005-10-29 20:06 ` [RFC][PATCH 2/6] swsusp: move snapshot-handling functions to snapshot.c Rafael J. Wysocki
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Rafael J. Wysocki @ 2005-10-29 20:01 UTC (permalink / raw)
  To: Pavel Machek; +Cc: LKML, linux-pm

This is a preliminary step.  It makes only the functions in swsusp.c call
functions in snapshot.c and not both ways.  Basically, it moves the code
without changing its functionality.

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

 kernel/power/power.h    |    2 -
 kernel/power/snapshot.c |   14 +---------
 kernel/power/swsusp.c   |   62 +++++++++++++++++++++++++++---------------------
 3 files changed, 39 insertions(+), 39 deletions(-)

Index: linux-2.6.14-rc5-mm1/kernel/power/snapshot.c
===================================================================
--- linux-2.6.14-rc5-mm1.orig/kernel/power/snapshot.c	2005-10-28 23:46:36.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/snapshot.c	2005-10-28 23:49:15.000000000 +0200
@@ -89,7 +89,7 @@
 }
 
 
-static int save_highmem(void)
+int save_highmem(void)
 {
 	struct zone *zone;
 	int res = 0;
@@ -121,7 +121,7 @@
 	return 0;
 }
 #else
-static int save_highmem(void) { return 0; }
+int save_highmem(void) { return 0; }
 int restore_highmem(void) { return 0; }
 #endif /* CONFIG_HIGHMEM */
 
@@ -383,11 +383,6 @@
 	unsigned nr_pages;
 
 	pr_debug("swsusp: critical section: \n");
-	if (save_highmem()) {
-		printk(KERN_CRIT "swsusp: Not enough free pages for highmem\n");
-		restore_highmem();
-		return -ENOMEM;
-	}
 
 	drain_local_pages();
 	nr_pages = count_data_pages();
@@ -407,11 +402,6 @@
 		return -ENOMEM;
 	}
 
-	if (!enough_swap(nr_pages)) {
-		printk(KERN_ERR "swsusp: Not enough free swap\n");
-		return -ENOSPC;
-	}
-
 	pagedir_nosave = swsusp_alloc(nr_pages);
 	if (!pagedir_nosave)
 		return -ENOMEM;
Index: linux-2.6.14-rc5-mm1/kernel/power/swsusp.c
===================================================================
--- linux-2.6.14-rc5-mm1.orig/kernel/power/swsusp.c	2005-10-28 23:46:36.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/swsusp.c	2005-10-28 23:49:15.000000000 +0200
@@ -507,6 +507,26 @@
 }
 
 /**
+ *	enough_swap - Make sure we have enough swap to save the image.
+ *
+ *	Returns TRUE or FALSE after checking the total amount of swap
+ *	space avaiable.
+ *
+ *	FIXME: si_swapinfo(&i) returns all swap devices information.
+ *	We should only consider resume_device.
+ */
+
+static int enough_swap(unsigned long nr_pages)
+{
+	struct sysinfo i;
+
+	si_swapinfo(&i);
+	pr_debug("swsusp: available swap: %lu pages\n", i.freeswap);
+	return i.freeswap > (nr_pages + PAGES_FOR_IO +
+		(nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE);
+}
+
+/**
  *	write_suspend_image - Write entire image and metadata.
  *
  */
@@ -514,6 +534,11 @@
 {
 	int error;
 
+	if (!enough_swap(nr_copy_pages)) {
+		printk(KERN_ERR "swsusp: Not enough free swap\n");
+		return -ENOSPC;
+	}
+
 	init_header();
 	if ((error = data_write()))
 		goto FreeData;
@@ -533,27 +558,6 @@
 	goto Done;
 }
 
-/**
- *	enough_swap - Make sure we have enough swap to save the image.
- *
- *	Returns TRUE or FALSE after checking the total amount of swap
- *	space avaiable.
- *
- *	FIXME: si_swapinfo(&i) returns all swap devices information.
- *	We should only consider resume_device.
- */
-
-int enough_swap(unsigned nr_pages)
-{
-	struct sysinfo i;
-
-	si_swapinfo(&i);
-	pr_debug("swsusp: available swap: %lu pages\n", i.freeswap);
-	return i.freeswap > (nr_pages + PAGES_FOR_IO +
-		(nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE);
-}
-
-
 /* It is important _NOT_ to umount filesystems at this point. We want
  * them synced (in case something goes wrong) but we DO not want to mark
  * filesystem clean: it is not. (And it does not matter, if we resume
@@ -576,6 +580,7 @@
 int swsusp_suspend(void)
 {
 	int error;
+
 	if ((error = arch_prepare_suspend()))
 		return error;
 	local_irq_disable();
@@ -587,15 +592,17 @@
 	 */
 	if ((error = device_power_down(PMSG_FREEZE))) {
 		printk(KERN_ERR "Some devices failed to power down, aborting suspend\n");
-		local_irq_enable();
-		return error;
+		goto Enable_irqs;
 	}
 
 	if ((error = swsusp_swap_check())) {
 		printk(KERN_ERR "swsusp: cannot find swap device, try swapon -a.\n");
-		device_power_up();
-		local_irq_enable();
-		return error;
+		goto Power_up;
+	}
+
+	if ((error = save_highmem())) {
+		printk(KERN_ERR "swsusp: Not enough free pages for highmem\n");
+		goto Restore_highmem;
 	}
 
 	save_processor_state();
@@ -603,8 +610,11 @@
 		printk(KERN_ERR "Error %d suspending\n", error);
 	/* Restore control flow magically appears here */
 	restore_processor_state();
+Restore_highmem:
 	restore_highmem();
+Power_up:
 	device_power_up();
+Enable_irqs:
 	local_irq_enable();
 	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-28 23:46:36.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/power.h	2005-10-28 23:49:15.000000000 +0200
@@ -65,8 +65,8 @@
 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 void swsusp_free(void);
-extern int enough_swap(unsigned nr_pages);

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [RFC][PATCH 2/6] swsusp: move snapshot-handling functions to snapshot.c
  2005-10-29 19:58 [RFC][PATCH 0/6] swsusp: rework swap handling Rafael J. Wysocki
  2005-10-29 20:01 ` [RFC][PATCH 1/6] swsusp: rework swsusp_suspend Rafael J. Wysocki
@ 2005-10-29 20:06 ` Rafael J. Wysocki
  2005-10-29 22:27   ` Pavel Machek
  2005-10-29 20:32 ` [RFC][PATCH 3/6] swsusp: introduce the swap map structure and interface functions Rafael J. Wysocki
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Rafael J. Wysocki @ 2005-10-29 20:06 UTC (permalink / raw)
  To: Pavel Machek; +Cc: LKML, linux-pm

This is another preliminary step.  It moves the snapshot-handling functions
remaining in swsusp.c to snapshot.c (moving the code without changing
the functionality) and makes the next patch be more clear (in my opinion).

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

 kernel/power/power.h    |    2 
 kernel/power/snapshot.c |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 kernel/power/swsusp.c   |  126 ------------------------------------------------
 3 files changed, 125 insertions(+), 124 deletions(-)

Index: linux-2.6.14-rc5-mm1/kernel/power/power.h
===================================================================
--- linux-2.6.14-rc5-mm1.orig/kernel/power/power.h	2005-10-28 23:49:15.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/power.h	2005-10-28 23:50:11.000000000 +0200
@@ -69,4 +69,6 @@
 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);
Index: linux-2.6.14-rc5-mm1/kernel/power/snapshot.c
===================================================================
--- linux-2.6.14-rc5-mm1.orig/kernel/power/snapshot.c	2005-10-28 23:49:15.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/snapshot.c	2005-10-28 23:50:11.000000000 +0200
@@ -423,3 +423,124 @@
 	printk("swsusp: critical section/: done (%d pages copied)\n", nr_pages);
 	return 0;
 }
+
+/**
+ *	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(unsigned 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
+ */
+
+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.
+ */
+
+struct pbe * swsusp_pagedir_relocate(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))
+                		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));
+	}
+
+	printk("swsusp: Relocated %d pages\n", rel);
+
+	return pblist;
+}
Index: linux-2.6.14-rc5-mm1/kernel/power/swsusp.c
===================================================================
--- linux-2.6.14-rc5-mm1.orig/kernel/power/swsusp.c	2005-10-28 23:49:15.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/swsusp.c	2005-10-28 23:50:11.000000000 +0200
@@ -645,130 +645,6 @@
 	return error;
 }
 
-/**
- *	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(unsigned 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.
- */
-
-static struct pbe * swsusp_pagedir_relocate(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;
-
-	pr_debug("swsusp: Relocating pagedir (%lu pages to check)\n",
-			swsusp_info.pagedir_pages);
-
-	/* 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 +
-					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));
-	}
-
-	printk("swsusp: Relocated %d pages\n", rel);
-
-	return pblist;
-}
-
 /*
  *	Using bio to read from swap.
  *	This code requires a bit more work than just using buffer heads
@@ -1015,6 +891,8 @@
 
 	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;
 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [RFC][PATCH 3/6] swsusp: introduce the swap map structure and interface functions
  2005-10-29 19:58 [RFC][PATCH 0/6] swsusp: rework swap handling Rafael J. Wysocki
  2005-10-29 20:01 ` [RFC][PATCH 1/6] swsusp: rework swsusp_suspend Rafael J. Wysocki
  2005-10-29 20:06 ` [RFC][PATCH 2/6] swsusp: move snapshot-handling functions to snapshot.c Rafael J. Wysocki
@ 2005-10-29 20:32 ` Rafael J. Wysocki
  2005-10-29 22:57   ` Pavel Machek
  2005-10-29 23:20   ` Pavel Machek
  2005-10-29 20:36 ` [RFC][PATCH 4/6] swsusp: move swap check out of swsusp_suspend Rafael J. Wysocki
                   ` (3 subsequent siblings)
  6 siblings, 2 replies; 17+ messages in thread
From: Rafael J. Wysocki @ 2005-10-29 20:32 UTC (permalink / raw)
  To: Pavel Machek; +Cc: LKML, linux-pm

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@sisk.pl>

 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@domdv.de>:
  * Added encrypted suspend option
  *
+ * 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
@@ -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@suse.cz>
+ * Copyright (C) 2005 Rafael J. Wysocki <rjw@sisk.pl>
  *
  * 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) \

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [RFC][PATCH 4/6] swsusp: move swap check out of swsusp_suspend
  2005-10-29 19:58 [RFC][PATCH 0/6] swsusp: rework swap handling Rafael J. Wysocki
                   ` (2 preceding siblings ...)
  2005-10-29 20:32 ` [RFC][PATCH 3/6] swsusp: introduce the swap map structure and interface functions Rafael J. Wysocki
@ 2005-10-29 20:36 ` Rafael J. Wysocki
  2005-10-29 23:21   ` Pavel Machek
  2005-10-29 20:41 ` [RFC][PATCH 5/6] swsusp: move swap-handling functions to separate file Rafael J. Wysocki
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Rafael J. Wysocki @ 2005-10-29 20:36 UTC (permalink / raw)
  To: Pavel Machek; +Cc: LKML, linux-pm

This is a non-essential step making the next patch possible.  No functionality
changes.

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

 kernel/power/swsusp.c |   13 +++++--------
 1 files changed, 5 insertions(+), 8 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:13:47.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/swsusp.c	2005-10-29 13:13:55.000000000 +0200
@@ -644,12 +644,15 @@
 {
 	int error;
 
+	if ((error = swsusp_swap_check())) {
+		printk(KERN_ERR "swsusp: cannot find swap device, try swapon -a.\n");
+		return error;
+	}
 	lock_swapdevices();
 	error = write_suspend_image();
 	/* This will unlock ignored swap devices since writing is finished */
 	lock_swapdevices();
 	return error;
-
 }
 
 
@@ -672,11 +675,6 @@
 		goto Enable_irqs;
 	}
 
-	if ((error = swsusp_swap_check())) {
-		printk(KERN_ERR "swsusp: cannot find swap device, try swapon -a.\n");
-		goto Power_up;
-	}
-
 	if ((error = save_highmem())) {
 		printk(KERN_ERR "swsusp: Not enough free pages for highmem\n");
 		goto Restore_highmem;
@@ -689,7 +687,6 @@
 	restore_processor_state();
 Restore_highmem:
 	restore_highmem();
-Power_up:
 	device_power_up();
 Enable_irqs:
 	local_irq_enable();
@@ -916,7 +913,7 @@
 		 * Reset swap signature now.
 		 */
 		error = bio_write_page(0, &swsusp_header);
-	} else { 
+	} else {
 		return -EINVAL;
 	}
 	if (!error)

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [RFC][PATCH 5/6] swsusp: move swap-handling functions to separate file
  2005-10-29 19:58 [RFC][PATCH 0/6] swsusp: rework swap handling Rafael J. Wysocki
                   ` (3 preceding siblings ...)
  2005-10-29 20:36 ` [RFC][PATCH 4/6] swsusp: move swap check out of swsusp_suspend Rafael J. Wysocki
@ 2005-10-29 20:41 ` Rafael J. Wysocki
  2005-10-30 13:33   ` Pavel Machek
  2005-10-29 20:46 ` [RFC][PATCH 6/6] swsusp: improve freeing of memory Rafael J. Wysocki
  2005-10-29 23:05 ` [RFC][PATCH 0/6] swsusp: rework swap handling Pavel Machek
  6 siblings, 1 reply; 17+ messages in thread
From: Rafael J. Wysocki @ 2005-10-29 20:41 UTC (permalink / raw)
  To: Pavel Machek; +Cc: LKML, linux-pm

This patch is to show that the swap-handling part of swsusp is really independent
and it can be moved entirely to a separate file.  It introduces the file swap.c
containing all of the swap-handling code.

After the change swsusp.c contains the functions that in my opinion do not
belong to either the snapshot-handling part or the swap-handling part
(swsusp_suspend(), swsusp_resume() and the functions related to highmem).

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

 kernel/power/Makefile   |    2 
 kernel/power/power.h    |    2 
 kernel/power/snapshot.c |   93 ----
 kernel/power/swap.c     |  915 +++++++++++++++++++++++++++++++++++++++++++++++
 kernel/power/swsusp.c   |  923 +++---------------------------------------------
 5 files changed, 988 insertions(+), 947 deletions(-)

Index: linux-2.6.14-rc5-mm1/kernel/power/Makefile
===================================================================
--- linux-2.6.14-rc5-mm1.orig/kernel/power/Makefile	2005-10-29 13:23:31.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/Makefile	2005-10-29 13:26:26.000000000 +0200
@@ -4,7 +4,7 @@
 endif
 
 obj-y				:= main.o process.o console.o pm.o
-obj-$(CONFIG_SOFTWARE_SUSPEND)	+= swsusp.o disk.o snapshot.o
+obj-$(CONFIG_SOFTWARE_SUSPEND)	+= swsusp.o disk.o snapshot.o swap.o
 
 obj-$(CONFIG_SUSPEND_SMP)	+= smp.o
 
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:58.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/power.h	2005-10-29 13:26:26.000000000 +0200
@@ -58,8 +58,6 @@
 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 void swsusp_free(void);
 
 extern unsigned snapshot_pages_to_save(void);
Index: linux-2.6.14-rc5-mm1/kernel/power/swap.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.14-rc5-mm1/kernel/power/swap.c	2005-10-29 13:26:26.000000000 +0200
@@ -0,0 +1,915 @@
+/*
+ * linux/kernel/power/snapshot.c
+ *
+ * This file provides the swap writing/reading functionality.
+ *
+ * Copyright (C) 1998-2005 Pavel Machek <pavel@suse.cz>
+ * Copyright (C) 2005 Rafael J. Wysocki <rjw@sisk.pl>
+ *
+ * This file is released under the GPLv2, and is based on swsusp.c.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/suspend.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+#include <linux/utsname.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/swap.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/buffer_head.h>
+#include <linux/swapops.h>
+#include <linux/bootmem.h>
+#include <linux/syscalls.h>
+#include <linux/highmem.h>
+#include <linux/bio.h>
+
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
+#include <asm/io.h>
+
+#include <linux/random.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+
+#include "power.h"
+
+#define CIPHER "aes"
+#define MAXKEY 32
+#define MAXIV  32
+
+extern char resume_file[];
+
+
+#define SWSUSP_SIG	"S1SUSPEND"
+
+static struct swsusp_header {
+	char reserved[PAGE_SIZE - 20 - MAXKEY - MAXIV - sizeof(swp_entry_t)];
+	u8 key_iv[MAXKEY+MAXIV];
+	swp_entry_t swsusp_info;
+	char	orig_sig[10];
+	char	sig[10];
+} __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header;
+
+static struct swsusp_info swsusp_info;
+
+/*
+ * Saving part...
+ */
+
+/* We memorize in swapfile_used what swap devices are used for suspension */
+#define SWAPFILE_UNUSED    0
+#define SWAPFILE_SUSPEND   1	/* This is the suspending device */
+#define SWAPFILE_IGNORED   2	/* Those are other swap devices ignored for suspension */
+
+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 u8 key_iv[MAXKEY+MAXIV];
+
+#ifdef CONFIG_SWSUSP_ENCRYPT
+
+static int crypto_init(int mode, void **mem)
+{
+	int error = 0;
+	int len;
+	char *modemsg;
+	struct crypto_tfm *tfm;
+
+	modemsg = mode ? "suspend not possible" : "resume not possible";
+
+	tfm = crypto_alloc_tfm(CIPHER, CRYPTO_TFM_MODE_CBC);
+	if(!tfm) {
+		printk(KERN_ERR "swsusp: no tfm, %s\n", modemsg);
+		error = -EINVAL;
+		goto out;
+	}
+
+	if(MAXKEY < crypto_tfm_alg_min_keysize(tfm)) {
+		printk(KERN_ERR "swsusp: key buffer too small, %s\n", modemsg);
+		error = -ENOKEY;
+		goto fail;
+	}
+
+	if (mode)
+		get_random_bytes(key_iv, MAXKEY+MAXIV);
+
+	len = crypto_tfm_alg_max_keysize(tfm);
+	if (len > MAXKEY)
+		len = MAXKEY;
+
+	if (crypto_cipher_setkey(tfm, key_iv, len)) {
+		printk(KERN_ERR "swsusp: key setup failure, %s\n", modemsg);
+		error = -EKEYREJECTED;
+		goto fail;
+	}
+
+	len = crypto_tfm_alg_ivsize(tfm);
+
+	if (MAXIV < len) {
+		printk(KERN_ERR "swsusp: iv buffer too small, %s\n", modemsg);
+		error = -EOVERFLOW;
+		goto fail;
+	}
+
+	crypto_cipher_set_iv(tfm, key_iv+MAXKEY, len);
+
+	*mem=(void *)tfm;
+
+	goto out;
+
+fail:	crypto_free_tfm(tfm);
+out:	return error;
+}
+
+static __inline__ void crypto_exit(void *mem)
+{
+	crypto_free_tfm((struct crypto_tfm *)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((void *)addr);
+	src.offset = 0;
+	src.length = PAGE_SIZE;
+	dst.page   = virt_to_page((void *)&swsusp_header);
+	dst.offset = 0;
+	dst.length = PAGE_SIZE;
+
+	error = crypto_cipher_encrypt((struct crypto_tfm *)mem, &dst, &src,
+					PAGE_SIZE);
+
+	if (!error)
+		error = write_page((unsigned long)&swsusp_header, entry);
+	return error;
+}
+
+static __inline__ int crypto_read(unsigned long offset, void *buf, void *mem)
+{
+	int error = 0;
+	struct scatterlist src, dst;
+
+	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(buf);
+
+		error = crypto_cipher_decrypt((struct crypto_tfm *)mem, &dst,
+						&src, PAGE_SIZE);
+	}
+	return error;
+}
+#else
+static __inline__ int crypto_init(int mode, void *mem)
+{
+	return 0;
+}
+
+static __inline__ void crypto_exit(void *mem)
+{
+}
+
+static __inline__ int crypto_write(unsigned long addr, swp_entry_t *entry, void *mem)
+{
+	return write_page(addr, entry);
+}
+
+static __inline__ int crypto_read(unsigned long offset, void *buf, void *mem)
+{
+	return bio_read_page(offset, buf);
+}
+#endif
+
+static int mark_swapfiles(swp_entry_t prev)
+{
+	int error;
+
+	rw_swap_page_sync(READ,
+			  swp_entry(root_swap, 0),
+			  virt_to_page((unsigned long)&swsusp_header));
+	if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) ||
+	    !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) {
+		memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10);
+		memcpy(swsusp_header.sig,SWSUSP_SIG, 10);
+		memcpy(swsusp_header.key_iv, key_iv, MAXKEY+MAXIV);
+		swsusp_header.swsusp_info = prev;
+		error = rw_swap_page_sync(WRITE,
+					  swp_entry(root_swap, 0),
+					  virt_to_page((unsigned long)
+						       &swsusp_header));
+	} else {
+		pr_debug("swsusp: Partition is not swap space.\n");
+		error = -ENODEV;
+	}
+	return error;
+}
+
+/*
+ * Check whether the swap device is the specified resume
+ * device, irrespective of whether they are specified by
+ * identical names.
+ *
+ * (Thus, device inode aliasing is allowed.  You can say /dev/hda4
+ * instead of /dev/ide/host0/bus0/target0/lun0/part4 [if using devfs]
+ * and they'll be considered the same device.  This is *necessary* for
+ * devfs, since the resume code can only recognize the form /dev/hda4,
+ * but the suspend code would see the long name.)
+ */
+static int is_resume_device(const struct swap_info_struct *swap_info)
+{
+	struct file *file = swap_info->swap_file;
+	struct inode *inode = file->f_dentry->d_inode;
+
+	return S_ISBLK(inode->i_mode) &&
+		swsusp_resume_device == MKDEV(imajor(inode), iminor(inode));
+}
+
+static int swsusp_swap_check(void) /* This is called before saving image */
+{
+	int i, len;
+
+	len=strlen(resume_file);
+	root_swap = 0xFFFF;
+
+	spin_lock(&swap_lock);
+	for (i=0; i<MAX_SWAPFILES; i++) {
+		if (!(swap_info[i].flags & SWP_WRITEOK)) {
+			swapfile_used[i]=SWAPFILE_UNUSED;
+		} else {
+			if (!len) {
+	    			printk(KERN_WARNING "resume= option should be used to set suspend device" );
+				if (root_swap == 0xFFFF) {
+					swapfile_used[i] = SWAPFILE_SUSPEND;
+					root_swap = i;
+				} else
+					swapfile_used[i] = SWAPFILE_IGNORED;
+			} else {
+	  			/* we ignore all swap devices that are not the resume_file */
+				if (is_resume_device(&swap_info[i])) {
+					swapfile_used[i] = SWAPFILE_SUSPEND;
+					root_swap = i;
+				} else {
+				  	swapfile_used[i] = SWAPFILE_IGNORED;
+				}
+			}
+		}
+	}
+	spin_unlock(&swap_lock);
+	return (root_swap != 0xffff) ? 0 : -ENODEV;
+}
+
+/**
+ * This is called after saving image so modification
+ * will be lost after resume... and that's what we want.
+ * we make the device unusable. A new call to
+ * lock_swapdevices can unlock the devices.
+ */
+static void lock_swapdevices(void)
+{
+	int i;
+
+	spin_lock(&swap_lock);
+	for (i = 0; i< MAX_SWAPFILES; i++)
+		if (swapfile_used[i] == SWAPFILE_IGNORED) {
+			swap_info[i].flags ^= SWP_WRITEOK;
+		}
+	spin_unlock(&swap_lock);
+}
+
+/**
+ *	write_page - Write one page to a fresh swap location.
+ *	@addr:	Address we're writing.
+ *	@loc:	Place to store the entry we used.
+ *
+ *	Allocate a new swap entry and 'sync' it. Note we discard -EIO
+ *	errors. That is an artifact left over from swsusp. It did not
+ *	check the return of rw_swap_page_sync() at all, since most pages
+ *	written back to swap would return -EIO.
+ *	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)
+{
+	swp_entry_t entry;
+	int error = 0;
+
+	entry = get_swap_page();
+	if (swp_offset(entry) &&
+	    swapfile_used[swp_type(entry)] == SWAPFILE_SUSPEND) {
+		error = rw_swap_page_sync(WRITE, entry,
+					  virt_to_page(addr));
+		if (error == -EIO)
+			error = 0;
+		if (!error)
+			*loc = entry;
+	} else
+		error = -ENOSPC;
+	return error;
+}
+
+static void dump_info(void)
+{
+	pr_debug(" swsusp: Version: %u\n",swsusp_info.version_code);
+	pr_debug(" swsusp: Num Pages: %ld\n",swsusp_info.num_physpages);
+	pr_debug(" swsusp: UTS Sys: %s\n",swsusp_info.uts.sysname);
+	pr_debug(" swsusp: UTS Node: %s\n",swsusp_info.uts.nodename);
+	pr_debug(" swsusp: UTS Release: %s\n",swsusp_info.uts.release);
+	pr_debug(" swsusp: UTS Version: %s\n",swsusp_info.uts.version);
+	pr_debug(" swsusp: UTS Machine: %s\n",swsusp_info.uts.machine);
+	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: Total: %ld Pages\n", swsusp_info.pages);
+}
+
+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.cpus = num_online_cpus();
+	swsusp_info.image_pages = img_pages;
+	swsusp_info.pages = nr_pages;
+}
+
+static int close_swap(void)
+{
+	swp_entry_t entry;
+	int error;
+
+	dump_info();
+	error = write_page((unsigned long)&swsusp_info, &entry);
+	if (!error) {
+		printk( "S" );
+		error = mark_swapfiles(entry);
+		printk( "|\n" );
+	}
+	return error;
+}
+
+/**
+ *	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 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.
+ */
+
+#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)
+{
+	swp_map_t *swp;
+
+	while (swp_map) {
+		swp = swp_map->next;
+		free_page((unsigned long)swp_map);
+		swp_map = swp;
+	}
+}
+
+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;
+}
+
+/**
+ *	save_swp_map - save the swap map used for tracing the data pages
+ *	stored in swap
+ */
+
+static int save_swp_map(swp_map_t *swp_map, swp_entry_t *start)
+{
+	swp_entry_t entry = (swp_entry_t){0};
+	int error;
+
+	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;
+}
+
+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;
+	}
+}
+
+/**
+ *	enough_swap - Make sure we have enough swap to save the image.
+ *
+ *	Returns TRUE or FALSE after checking the total amount of swap
+ *	space avaiable.
+ *
+ *	FIXME: si_swapinfo(&i) returns all swap devices information.
+ *	We should only consider resume_device.
+ */
+
+static int enough_swap(unsigned long nr_pages)
+{
+	struct sysinfo i;
+
+	si_swapinfo(&i);
+	pr_debug("swsusp: available swap: %lu pages\n", i.freeswap);
+	return i.freeswap > (nr_pages + PAGES_FOR_IO +
+		(nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE);
+}
+
+/**
+ *	write_suspend_image - Write entire image and metadata.
+ *
+ */
+static int write_suspend_image(void)
+{
+	unsigned nr_pages;
+	int error;
+	swp_map_t *swp_map;
+	void *buffer;
+
+	nr_pages = snapshot_pages_to_save();
+	if (!enough_swap(nr_pages)) {
+		printk(KERN_ERR "swsusp: Not enough free swap\n");
+		return -ENOSPC;
+	}
+
+	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 Free_map_entries;
+
+Free_mem:
+	free_swp_map(swp_map);
+	free_page((unsigned long)buffer);
+	memset(key_iv, 0, MAXKEY+MAXIV);
+	return error;
+
+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
+ * them synced (in case something goes wrong) but we DO not want to mark
+ * 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 error;
+
+	if ((error = swsusp_swap_check())) {
+		printk(KERN_ERR "swsusp: cannot find swap device, try swapon -a.\n");
+		return error;
+	}
+	lock_swapdevices();
+	error = write_suspend_image();
+	/* This will unlock ignored swap devices since writing is finished */
+	lock_swapdevices();
+	return error;
+}
+
+/**
+ *	Using bio to read from swap.
+ *	This code requires a bit more work than just using buffer heads
+ *	but, it is the recommended way for 2.5/2.6.
+ *	The following are to signal the beginning and end of I/O. Bios
+ *	finish asynchronously, while we want them to happen synchronously.
+ *	A simple atomic_t, and a wait loop take care of this problem.
+ */
+
+static atomic_t io_done = ATOMIC_INIT(0);
+
+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");
+	atomic_set(&io_done, 0);
+	return 0;
+}
+
+static struct block_device * resume_bdev;
+
+/**
+ *	submit - submit BIO request.
+ *	@rw:	READ or WRITE.
+ *	@off	physical offset of page.
+ *	@page:	page we're reading or writing.
+ *
+ *	Straight from the textbook - allocate and initialize the bio.
+ *	If we're writing, make sure the page is marked as dirty.
+ *	Then submit it and wait.
+ */
+
+static int submit(int rw, pgoff_t page_off, void * page)
+{
+	int error = 0;
+	struct bio * bio;
+
+	bio = bio_alloc(GFP_ATOMIC, 1);
+	if (!bio)
+		return -ENOMEM;
+	bio->bi_sector = page_off * (PAGE_SIZE >> 9);
+	bio_get(bio);
+	bio->bi_bdev = resume_bdev;
+	bio->bi_end_io = end_io;
+
+	if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) {
+		printk("swsusp: ERROR: adding page to bio at %ld\n",page_off);
+		error = -EFAULT;
+		goto Done;
+	}
+
+	if (rw == WRITE)
+		bio_set_pages_dirty(bio);
+
+	atomic_set(&io_done, 1);
+	submit_bio(rw | (1 << BIO_RW_SYNC), bio);
+	while (atomic_read(&io_done))
+		yield();
+
+ Done:
+	bio_put(bio);
+	return error;
+}
+
+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)
+{
+	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..
+ */
+
+static const char * sanity_check(void)
+{
+	dump_info();
+	if (swsusp_info.version_code != LINUX_VERSION_CODE)
+		return "kernel version";
+	if (swsusp_info.num_physpages != num_physpages)
+		return "memory size";
+	if (strcmp(swsusp_info.uts.sysname,system_utsname.sysname))
+		return "system type";
+	if (strcmp(swsusp_info.uts.release,system_utsname.release))
+		return "kernel release";
+	if (strcmp(swsusp_info.uts.version,system_utsname.version))
+		return "version";
+	if (strcmp(swsusp_info.uts.machine,system_utsname.machine))
+		return "machine";
+#if 0
+	/* We can't use number of online CPUs when we use hotplug to remove them ;-))) */
+	if (swsusp_info.cpus != num_possible_cpus())
+		return "number of cpus";
+#endif
+	return NULL;
+}
+
+
+static int check_header(void)
+{
+	const char * reason = NULL;
+	int error;
+
+	if ((error = bio_read_page(swp_offset(swsusp_header.swsusp_info), &swsusp_info)))
+		return error;
+
+ 	/* Is this same machine? */
+	if ((reason = sanity_check())) {
+		printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason);
+		return -EPERM;
+	}
+	return error;
+}
+
+static int check_sig(void)
+{
+	int error;
+
+	memset(&swsusp_header, 0, sizeof(swsusp_header));
+	if ((error = bio_read_page(0, &swsusp_header)))
+		return error;
+	if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
+		memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
+		memcpy(key_iv, swsusp_header.key_iv, MAXKEY+MAXIV);
+		memset(swsusp_header.key_iv, 0, MAXKEY+MAXIV);
+
+		/*
+		 * Reset swap signature now.
+		 */
+		error = bio_write_page(0, &swsusp_header);
+	} else {
+		return -EINVAL;
+	}
+	if (!error)
+		pr_debug("swsusp: Signature found, resuming\n");
+	return error;
+}
+
+static int check_suspend_image(void)
+{
+	int error = 0;
+
+	if ((error = check_sig()))
+		return error;
+
+	if ((error = check_header()))
+		return error;
+
+	return 0;
+}
+
+static int read_suspend_image(void)
+{
+	int error;
+
+	error = snapshot_recv_init(swsusp_info.pages, swsusp_info.image_pages);
+	if (!error)
+		error = load_image(swsusp_info.pages, swsusp_info.start);
+	if (!error)
+		error = snapshot_finish();
+	return error;
+}
+
+/**
+ *      swsusp_check - Check for saved image in swap
+ */
+
+int swsusp_check(void)
+{
+	int error;
+
+	resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
+	if (!IS_ERR(resume_bdev)) {
+		set_blocksize(resume_bdev, PAGE_SIZE);
+		error = check_suspend_image();
+		if (error)
+		    blkdev_put(resume_bdev);
+	} else
+		error = PTR_ERR(resume_bdev);
+
+	if (!error)
+		pr_debug("swsusp: resume file found\n");
+	else
+		pr_debug("swsusp: Error %d check for resume file\n", error);
+	return error;
+}
+
+/**
+ *	swsusp_read - Read saved image from swap.
+ */
+
+int swsusp_read(void)
+{
+	int error;
+
+	if (IS_ERR(resume_bdev)) {
+		pr_debug("swsusp: block device not initialised\n");
+		return PTR_ERR(resume_bdev);
+	}
+
+	error = read_suspend_image();
+	blkdev_put(resume_bdev);
+	memset(key_iv, 0, MAXKEY+MAXIV);
+
+	if (!error)
+		pr_debug("swsusp: Reading resume file was successful\n");
+	else
+		pr_debug("swsusp: Error %d resuming\n", error);
+	return error;
+}
+
+/**
+ *	swsusp_close - close swap device.
+ */
+
+void swsusp_close(void)
+{
+	if (IS_ERR(resume_bdev)) {
+		pr_debug("swsusp: block device not initialised\n");
+		return;
+	}
+
+	blkdev_put(resume_bdev);
+}
+
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:58.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/snapshot.c	2005-10-29 13:26:26.000000000 +0200
@@ -39,99 +39,6 @@
 static unsigned nr_copy_pages;
 static unsigned nr_pb_pages;
 
-#ifdef CONFIG_HIGHMEM
-struct highmem_page {
-	char *data;
-	struct page *page;
-	struct highmem_page *next;
-};
-
-static struct highmem_page *highmem_copy;
-
-static int save_highmem_zone(struct zone *zone)
-{
-	unsigned long zone_pfn;
-	mark_free_pages(zone);
-	for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) {
-		struct page *page;
-		struct highmem_page *save;
-		void *kaddr;
-		unsigned long pfn = zone_pfn + zone->zone_start_pfn;
-
-		if (!(pfn%1000))
-			printk(".");
-		if (!pfn_valid(pfn))
-			continue;
-		page = pfn_to_page(pfn);
-		/*
-		 * This condition results from rvmalloc() sans vmalloc_32()
-		 * and architectural memory reservations. This should be
-		 * corrected eventually when the cases giving rise to this
-		 * are better understood.
-		 */
-		if (PageReserved(page)) {
-			printk("highmem reserved page?!\n");
-			continue;
-		}
-		BUG_ON(PageNosave(page));
-		if (PageNosaveFree(page))
-			continue;
-		save = kmalloc(sizeof(struct highmem_page), GFP_ATOMIC);
-		if (!save)
-			return -ENOMEM;
-		save->next = highmem_copy;
-		save->page = page;
-		save->data = (void *) get_zeroed_page(GFP_ATOMIC);
-		if (!save->data) {
-			kfree(save);
-			return -ENOMEM;
-		}
-		kaddr = kmap_atomic(page, KM_USER0);
-		memcpy(save->data, kaddr, PAGE_SIZE);
-		kunmap_atomic(kaddr, KM_USER0);
-		highmem_copy = save;
-	}
-	return 0;
-}
-
-
-int save_highmem(void)
-{
-	struct zone *zone;
-	int res = 0;
-
-	pr_debug("swsusp: Saving Highmem\n");
-	for_each_zone (zone) {
-		if (is_highmem(zone))
-			res = save_highmem_zone(zone);
-		if (res)
-			return res;
-	}
-	return 0;
-}
-
-int restore_highmem(void)
-{
-	printk("swsusp: Restoring Highmem\n");
-	while (highmem_copy) {
-		struct highmem_page *save = highmem_copy;
-		void *kaddr;
-		highmem_copy = save->next;
-
-		kaddr = kmap_atomic(save->page, KM_USER0);
-		memcpy(kaddr, save->data, PAGE_SIZE);
-		kunmap_atomic(kaddr, KM_USER0);
-		free_page((long) save->data);
-		kfree(save);
-	}
-	return 0;
-}
-#else
-int save_highmem(void) { return 0; }
-int restore_highmem(void) { return 0; }
-#endif /* CONFIG_HIGHMEM */
-
-
 static int pfn_is_nosave(unsigned long pfn)
 {
 	unsigned long nosave_begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT;
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:25:41.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/swsusp.c	2005-10-29 13:26:26.000000000 +0200
@@ -76,586 +76,96 @@
 
 #include "power.h"
 
-#define CIPHER "aes"
-#define MAXKEY 32
-#define MAXIV  32
-
-extern char resume_file[];
-
-
-#define SWSUSP_SIG	"S1SUSPEND"
-
-static struct swsusp_header {
-	char reserved[PAGE_SIZE - 20 - MAXKEY - MAXIV - sizeof(swp_entry_t)];
-	u8 key_iv[MAXKEY+MAXIV];
-	swp_entry_t swsusp_info;
-	char	orig_sig[10];
-	char	sig[10];
-} __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header;
-
-static struct swsusp_info swsusp_info;
-
-/*
- * Saving part...
- */
-
-/* We memorize in swapfile_used what swap devices are used for suspension */
-#define SWAPFILE_UNUSED    0
-#define SWAPFILE_SUSPEND   1	/* This is the suspending device */
-#define SWAPFILE_IGNORED   2	/* Those are other swap devices ignored for suspension */
-
-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 u8 key_iv[MAXKEY+MAXIV];
-
-#ifdef CONFIG_SWSUSP_ENCRYPT
-
-static int crypto_init(int mode, void **mem)
-{
-	int error = 0;
-	int len;
-	char *modemsg;
-	struct crypto_tfm *tfm;
-
-	modemsg = mode ? "suspend not possible" : "resume not possible";
-
-	tfm = crypto_alloc_tfm(CIPHER, CRYPTO_TFM_MODE_CBC);
-	if(!tfm) {
-		printk(KERN_ERR "swsusp: no tfm, %s\n", modemsg);
-		error = -EINVAL;
-		goto out;
-	}
-
-	if(MAXKEY < crypto_tfm_alg_min_keysize(tfm)) {
-		printk(KERN_ERR "swsusp: key buffer too small, %s\n", modemsg);
-		error = -ENOKEY;
-		goto fail;
-	}
-
-	if (mode)
-		get_random_bytes(key_iv, MAXKEY+MAXIV);
-
-	len = crypto_tfm_alg_max_keysize(tfm);
-	if (len > MAXKEY)
-		len = MAXKEY;
-
-	if (crypto_cipher_setkey(tfm, key_iv, len)) {
-		printk(KERN_ERR "swsusp: key setup failure, %s\n", modemsg);
-		error = -EKEYREJECTED;
-		goto fail;
-	}
-
-	len = crypto_tfm_alg_ivsize(tfm);
-
-	if (MAXIV < len) {
-		printk(KERN_ERR "swsusp: iv buffer too small, %s\n", modemsg);
-		error = -EOVERFLOW;
-		goto fail;
-	}
-
-	crypto_cipher_set_iv(tfm, key_iv+MAXKEY, len);
-
-	*mem=(void *)tfm;
-
-	goto out;
-
-fail:	crypto_free_tfm(tfm);
-out:	return error;
-}
-
-static __inline__ void crypto_exit(void *mem)
-{
-	crypto_free_tfm((struct crypto_tfm *)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((void *)addr);
-	src.offset = 0;
-	src.length = PAGE_SIZE;
-	dst.page   = virt_to_page((void *)&swsusp_header);
-	dst.offset = 0;
-	dst.length = PAGE_SIZE;
-
-	error = crypto_cipher_encrypt((struct crypto_tfm *)mem, &dst, &src,
-					PAGE_SIZE);
-
-	if (!error)
-		error = write_page((unsigned long)&swsusp_header, entry);
-	return error;
-}
-
-static __inline__ int crypto_read(unsigned long offset, void *buf, void *mem)
-{
-	int error = 0;
-	struct scatterlist src, dst;
-
-	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(buf);
-
-		error = crypto_cipher_decrypt((struct crypto_tfm *)mem, &dst,
-						&src, PAGE_SIZE);
-	}
-	return error;
-}
-#else
-static __inline__ int crypto_init(int mode, void *mem)
-{
-	return 0;
-}
-
-static __inline__ void crypto_exit(void *mem)
-{
-}
-
-static __inline__ int crypto_write(unsigned long addr, swp_entry_t *entry, void *mem)
-{
-	return write_page(addr, entry);
-}
-
-static __inline__ int crypto_read(unsigned long offset, void *buf, void *mem)
-{
-	return bio_read_page(offset, buf);
-}
-#endif
-
-static int mark_swapfiles(swp_entry_t prev)
-{
-	int error;
-
-	rw_swap_page_sync(READ,
-			  swp_entry(root_swap, 0),
-			  virt_to_page((unsigned long)&swsusp_header));
-	if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) ||
-	    !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) {
-		memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10);
-		memcpy(swsusp_header.sig,SWSUSP_SIG, 10);
-		memcpy(swsusp_header.key_iv, key_iv, MAXKEY+MAXIV);
-		swsusp_header.swsusp_info = prev;
-		error = rw_swap_page_sync(WRITE,
-					  swp_entry(root_swap, 0),
-					  virt_to_page((unsigned long)
-						       &swsusp_header));
-	} else {
-		pr_debug("swsusp: Partition is not swap space.\n");
-		error = -ENODEV;
-	}
-	return error;
-}
-
-/*
- * Check whether the swap device is the specified resume
- * device, irrespective of whether they are specified by
- * identical names.
- *
- * (Thus, device inode aliasing is allowed.  You can say /dev/hda4
- * instead of /dev/ide/host0/bus0/target0/lun0/part4 [if using devfs]
- * and they'll be considered the same device.  This is *necessary* for
- * devfs, since the resume code can only recognize the form /dev/hda4,
- * but the suspend code would see the long name.)
- */
-static int is_resume_device(const struct swap_info_struct *swap_info)
-{
-	struct file *file = swap_info->swap_file;
-	struct inode *inode = file->f_dentry->d_inode;
-
-	return S_ISBLK(inode->i_mode) &&
-		swsusp_resume_device == MKDEV(imajor(inode), iminor(inode));
-}
-
-static int swsusp_swap_check(void) /* This is called before saving image */
-{
-	int i, len;
-
-	len=strlen(resume_file);
-	root_swap = 0xFFFF;
-
-	spin_lock(&swap_lock);
-	for (i=0; i<MAX_SWAPFILES; i++) {
-		if (!(swap_info[i].flags & SWP_WRITEOK)) {
-			swapfile_used[i]=SWAPFILE_UNUSED;
-		} else {
-			if (!len) {
-	    			printk(KERN_WARNING "resume= option should be used to set suspend device" );
-				if (root_swap == 0xFFFF) {
-					swapfile_used[i] = SWAPFILE_SUSPEND;
-					root_swap = i;
-				} else
-					swapfile_used[i] = SWAPFILE_IGNORED;
-			} else {
-	  			/* we ignore all swap devices that are not the resume_file */
-				if (is_resume_device(&swap_info[i])) {
-					swapfile_used[i] = SWAPFILE_SUSPEND;
-					root_swap = i;
-				} else {
-				  	swapfile_used[i] = SWAPFILE_IGNORED;
-				}
-			}
-		}
-	}
-	spin_unlock(&swap_lock);
-	return (root_swap != 0xffff) ? 0 : -ENODEV;
-}
-
-/**
- * This is called after saving image so modification
- * will be lost after resume... and that's what we want.
- * we make the device unusable. A new call to
- * lock_swapdevices can unlock the devices.
- */
-static void lock_swapdevices(void)
-{
-	int i;
-
-	spin_lock(&swap_lock);
-	for (i = 0; i< MAX_SWAPFILES; i++)
-		if (swapfile_used[i] == SWAPFILE_IGNORED) {
-			swap_info[i].flags ^= SWP_WRITEOK;
-		}
-	spin_unlock(&swap_lock);
-}
-
-/**
- *	write_page - Write one page to a fresh swap location.
- *	@addr:	Address we're writing.
- *	@loc:	Place to store the entry we used.
- *
- *	Allocate a new swap entry and 'sync' it. Note we discard -EIO
- *	errors. That is an artifact left over from swsusp. It did not
- *	check the return of rw_swap_page_sync() at all, since most pages
- *	written back to swap would return -EIO.
- *	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)
-{
-	swp_entry_t entry;
-	int error = 0;
-
-	entry = get_swap_page();
-	if (swp_offset(entry) &&
-	    swapfile_used[swp_type(entry)] == SWAPFILE_SUSPEND) {
-		error = rw_swap_page_sync(WRITE, entry,
-					  virt_to_page(addr));
-		if (error == -EIO)
-			error = 0;
-		if (!error)
-			*loc = entry;
-	} else
-		error = -ENOSPC;
-	return error;
-}
-
-static void dump_info(void)
-{
-	pr_debug(" swsusp: Version: %u\n",swsusp_info.version_code);
-	pr_debug(" swsusp: Num Pages: %ld\n",swsusp_info.num_physpages);
-	pr_debug(" swsusp: UTS Sys: %s\n",swsusp_info.uts.sysname);
-	pr_debug(" swsusp: UTS Node: %s\n",swsusp_info.uts.nodename);
-	pr_debug(" swsusp: UTS Release: %s\n",swsusp_info.uts.release);
-	pr_debug(" swsusp: UTS Version: %s\n",swsusp_info.uts.version);
-	pr_debug(" swsusp: UTS Machine: %s\n",swsusp_info.uts.machine);
-	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: Total: %ld Pages\n", swsusp_info.pages);
-}
-
-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.cpus = num_online_cpus();
-	swsusp_info.image_pages = img_pages;
-	swsusp_info.pages = nr_pages;
-}
-
-static int close_swap(void)
-{
-	swp_entry_t entry;
-	int error;
-
-	dump_info();
-	error = write_page((unsigned long)&swsusp_info, &entry);
-	if (!error) {
-		printk( "S" );
-		error = mark_swapfiles(entry);
-		printk( "|\n" );
-	}
-	return error;
-}
-
-/**
- *	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 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.
- */
-
-#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;
+#ifdef CONFIG_HIGHMEM
+struct highmem_page {
+	char *data;
+	struct page *page;
+	struct highmem_page *next;
 };
 
-typedef struct swp_map_page swp_map_t;
-
-static inline void free_swp_map(swp_map_t *swp_map)
-{
-	swp_map_t *swp;
-
-	while (swp_map) {
-		swp = swp_map->next;
-		free_page((unsigned long)swp_map);
-		swp_map = swp;
-	}
-}
+static struct highmem_page *highmem_copy;
 
-static swp_map_t *alloc_swp_map(unsigned nr_pages)
+static int save_highmem_zone(struct zone *zone)
 {
-	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;
+	unsigned long zone_pfn;
+	mark_free_pages(zone);
+	for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) {
+		struct page *page;
+		struct highmem_page *save;
+		void *kaddr;
+		unsigned long pfn = zone_pfn + zone->zone_start_pfn;
+
+		if (!(pfn%1000))
+			printk(".");
+		if (!pfn_valid(pfn))
+			continue;
+		page = pfn_to_page(pfn);
+		/*
+		 * This condition results from rvmalloc() sans vmalloc_32()
+		 * and architectural memory reservations. This should be
+		 * corrected eventually when the cases giving rise to this
+		 * are better understood.
+		 */
+		if (PageReserved(page)) {
+			printk("highmem reserved page?!\n");
+			continue;
 		}
-	}
-	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;
-}
-
-/**
- *	save_swp_map - save the swap map used for tracing the data pages
- *	stored in swap
- */
-
-static int save_swp_map(swp_map_t *swp_map, swp_entry_t *start)
-{
-	swp_entry_t entry = (swp_entry_t){0};
-	int error;
-
-	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;
-}
-
-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);
+		BUG_ON(PageNosave(page));
+		if (PageNosaveFree(page))
+			continue;
+		save = kmalloc(sizeof(struct highmem_page), GFP_ATOMIC);
+		if (!save)
+			return -ENOMEM;
+		save->next = highmem_copy;
+		save->page = page;
+		save->data = (void *) get_zeroed_page(GFP_ATOMIC);
+		if (!save->data) {
+			kfree(save);
+			return -ENOMEM;
 		}
-		swp = swp->next;
+		kaddr = kmap_atomic(page, KM_USER0);
+		memcpy(save->data, kaddr, PAGE_SIZE);
+		kunmap_atomic(kaddr, KM_USER0);
+		highmem_copy = save;
 	}
-	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;
-	}
-}
-
-/**
- *	enough_swap - Make sure we have enough swap to save the image.
- *
- *	Returns TRUE or FALSE after checking the total amount of swap
- *	space avaiable.
- *
- *	FIXME: si_swapinfo(&i) returns all swap devices information.
- *	We should only consider resume_device.
- */
-
-static int enough_swap(unsigned long nr_pages)
-{
-	struct sysinfo i;
-
-	si_swapinfo(&i);
-	pr_debug("swsusp: available swap: %lu pages\n", i.freeswap);
-	return i.freeswap > (nr_pages + PAGES_FOR_IO +
-		(nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE);
-}
-
-/**
- *	write_suspend_image - Write entire image and metadata.
- *
- */
-static int write_suspend_image(void)
+static int save_highmem(void)
 {
-	unsigned nr_pages;
-	int error;
-	swp_map_t *swp_map;
-	void *buffer;
+	struct zone *zone;
+	int res = 0;
 
-	nr_pages = snapshot_pages_to_save();
-	if (!enough_swap(nr_pages)) {
-		printk(KERN_ERR "swsusp: Not enough free swap\n");
-		return -ENOSPC;
+	pr_debug("swsusp: Saving Highmem\n");
+	for_each_zone (zone) {
+		if (is_highmem(zone))
+			res = save_highmem_zone(zone);
+		if (res)
+			return res;
 	}
-
-	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 Free_map_entries;
-
-Free_mem:
-	free_swp_map(swp_map);
-	free_page((unsigned long)buffer);
-	memset(key_iv, 0, MAXKEY+MAXIV);
-	return error;
-
-Free_map_entries:
-	free_swp_map_entries(swp_map);
-Free_pages_entries:
-	free_pages_entries(swp_map);
-	goto Free_mem;
+	return 0;
 }
 
-/* It is important _NOT_ to umount filesystems at this point. We want
- * them synced (in case something goes wrong) but we DO not want to mark
- * filesystem clean: it is not. (And it does not matter, if we resume
- * correctly, we'll mark system clean, anyway.)
- */
-int swsusp_write(void)
+static int restore_highmem(void)
 {
-	int error;
-
-	if ((error = swsusp_swap_check())) {
-		printk(KERN_ERR "swsusp: cannot find swap device, try swapon -a.\n");
-		return error;
+	printk("swsusp: Restoring Highmem\n");
+	while (highmem_copy) {
+		struct highmem_page *save = highmem_copy;
+		void *kaddr;
+		highmem_copy = save->next;
+
+		kaddr = kmap_atomic(save->page, KM_USER0);
+		memcpy(kaddr, save->data, PAGE_SIZE);
+		kunmap_atomic(kaddr, KM_USER0);
+		free_page((long) save->data);
+		kfree(save);
 	}
-	lock_swapdevices();
-	error = write_suspend_image();
-	/* This will unlock ignored swap devices since writing is finished */
-	lock_swapdevices();
-	return error;
+	return 0;
 }
-
-
+#else
+static int save_highmem(void) { return 0; }
+static int restore_highmem(void) { return 0; }
+#endif /* CONFIG_HIGHMEM */
 
 int swsusp_suspend(void)
 {
@@ -718,292 +228,3 @@
 	local_irq_enable();
 	return error;
 }
-
-/*
- *	Using bio to read from swap.
- *	This code requires a bit more work than just using buffer heads
- *	but, it is the recommended way for 2.5/2.6.
- *	The following are to signal the beginning and end of I/O. Bios
- *	finish asynchronously, while we want them to happen synchronously.
- *	A simple atomic_t, and a wait loop take care of this problem.
- */
-
-static atomic_t io_done = ATOMIC_INIT(0);
-
-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");
-	atomic_set(&io_done, 0);
-	return 0;
-}
-
-static struct block_device * resume_bdev;
-
-/**
- *	submit - submit BIO request.
- *	@rw:	READ or WRITE.
- *	@off	physical offset of page.
- *	@page:	page we're reading or writing.
- *
- *	Straight from the textbook - allocate and initialize the bio.
- *	If we're writing, make sure the page is marked as dirty.
- *	Then submit it and wait.
- */
-
-static int submit(int rw, pgoff_t page_off, void * page)
-{
-	int error = 0;
-	struct bio * bio;
-
-	bio = bio_alloc(GFP_ATOMIC, 1);
-	if (!bio)
-		return -ENOMEM;
-	bio->bi_sector = page_off * (PAGE_SIZE >> 9);
-	bio_get(bio);
-	bio->bi_bdev = resume_bdev;
-	bio->bi_end_io = end_io;
-
-	if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) {
-		printk("swsusp: ERROR: adding page to bio at %ld\n",page_off);
-		error = -EFAULT;
-		goto Done;
-	}
-
-	if (rw == WRITE)
-		bio_set_pages_dirty(bio);
-
-	atomic_set(&io_done, 1);
-	submit_bio(rw | (1 << BIO_RW_SYNC), bio);
-	while (atomic_read(&io_done))
-		yield();
-
- Done:
-	bio_put(bio);
-	return error;
-}
-
-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)
-{
-	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..
- */
-
-static const char * sanity_check(void)
-{
-	dump_info();
-	if (swsusp_info.version_code != LINUX_VERSION_CODE)
-		return "kernel version";
-	if (swsusp_info.num_physpages != num_physpages)
-		return "memory size";
-	if (strcmp(swsusp_info.uts.sysname,system_utsname.sysname))
-		return "system type";
-	if (strcmp(swsusp_info.uts.release,system_utsname.release))
-		return "kernel release";
-	if (strcmp(swsusp_info.uts.version,system_utsname.version))
-		return "version";
-	if (strcmp(swsusp_info.uts.machine,system_utsname.machine))
-		return "machine";
-#if 0
-	/* We can't use number of online CPUs when we use hotplug to remove them ;-))) */
-	if (swsusp_info.cpus != num_possible_cpus())
-		return "number of cpus";
-#endif
-	return NULL;
-}
-
-
-static int check_header(void)
-{
-	const char * reason = NULL;
-	int error;
-
-	if ((error = bio_read_page(swp_offset(swsusp_header.swsusp_info), &swsusp_info)))
-		return error;
-
- 	/* Is this same machine? */
-	if ((reason = sanity_check())) {
-		printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason);
-		return -EPERM;
-	}
-	return error;
-}
-
-static int check_sig(void)
-{
-	int error;
-
-	memset(&swsusp_header, 0, sizeof(swsusp_header));
-	if ((error = bio_read_page(0, &swsusp_header)))
-		return error;
-	if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
-		memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
-		memcpy(key_iv, swsusp_header.key_iv, MAXKEY+MAXIV);
-		memset(swsusp_header.key_iv, 0, MAXKEY+MAXIV);
-
-		/*
-		 * Reset swap signature now.
-		 */
-		error = bio_write_page(0, &swsusp_header);
-	} else {
-		return -EINVAL;
-	}
-	if (!error)
-		pr_debug("swsusp: Signature found, resuming\n");
-	return error;
-}
-
-static int check_suspend_image(void)
-{
-	int error = 0;
-
-	if ((error = check_sig()))
-		return error;
-
-	if ((error = check_header()))
-		return error;
-
-	return 0;
-}
-
-static int read_suspend_image(void)
-{
-	int error;
-
-	error = snapshot_recv_init(swsusp_info.pages, swsusp_info.image_pages);
-	if (!error)
-		error = load_image(swsusp_info.pages, swsusp_info.start);
-	if (!error)
-		error = snapshot_finish();
-	return error;
-}
-
-/**
- *      swsusp_check - Check for saved image in swap
- */
-
-int swsusp_check(void)
-{
-	int error;
-
-	resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
-	if (!IS_ERR(resume_bdev)) {
-		set_blocksize(resume_bdev, PAGE_SIZE);
-		error = check_suspend_image();
-		if (error)
-		    blkdev_put(resume_bdev);
-	} else
-		error = PTR_ERR(resume_bdev);
-
-	if (!error)
-		pr_debug("swsusp: resume file found\n");
-	else
-		pr_debug("swsusp: Error %d check for resume file\n", error);
-	return error;
-}
-
-/**
- *	swsusp_read - Read saved image from swap.
- */
-
-int swsusp_read(void)
-{
-	int error;
-
-	if (IS_ERR(resume_bdev)) {
-		pr_debug("swsusp: block device not initialised\n");
-		return PTR_ERR(resume_bdev);
-	}
-
-	error = read_suspend_image();
-	blkdev_put(resume_bdev);
-	memset(key_iv, 0, MAXKEY+MAXIV);
-
-	if (!error)
-		pr_debug("swsusp: Reading resume file was successful\n");
-	else
-		pr_debug("swsusp: Error %d resuming\n", error);
-	return error;
-}
-
-/**
- *	swsusp_close - close swap device.
- */
-
-void swsusp_close(void)
-{
-	if (IS_ERR(resume_bdev)) {
-		pr_debug("swsusp: block device not initialised\n");
-		return;
-	}
-
-	blkdev_put(resume_bdev);
-}

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [RFC][PATCH 6/6] swsusp: improve freeing of memory
  2005-10-29 19:58 [RFC][PATCH 0/6] swsusp: rework swap handling Rafael J. Wysocki
                   ` (4 preceding siblings ...)
  2005-10-29 20:41 ` [RFC][PATCH 5/6] swsusp: move swap-handling functions to separate file Rafael J. Wysocki
@ 2005-10-29 20:46 ` Rafael J. Wysocki
  2005-10-29 23:05 ` [RFC][PATCH 0/6] swsusp: rework swap handling Pavel Machek
  6 siblings, 0 replies; 17+ messages in thread
From: Rafael J. Wysocki @ 2005-10-29 20:46 UTC (permalink / raw)
  To: Pavel Machek; +Cc: LKML, linux-pm

This is the bonus patch not functionally related to the previous patches
that speeds up swsusp substantially (on my box swsusp is two times
faster with it) and makes the system be much more responsive after
resume.

However, it only makes sense if the limit imposed by the size of the
swsusp_info structure is lifted, which is done in one of the previous
patches.

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

 include/linux/suspend.h |    2 -
 kernel/power/disk.c     |   30 ++----------------------
 kernel/power/power.h    |    1 
 kernel/power/snapshot.c |    6 +---
 kernel/power/swsusp.c   |   60 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 67 insertions(+), 32 deletions(-)

Index: linux-2.6.14-rc5-mm1/kernel/power/disk.c
===================================================================
--- linux-2.6.14-rc5-mm1.orig/kernel/power/disk.c	2005-10-29 14:01:27.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/disk.c	2005-10-29 14:12:12.000000000 +0200
@@ -24,6 +24,7 @@
 
 extern suspend_disk_method_t pm_disk_mode;
 
+extern int swsusp_shrink_memory(void);
 extern int swsusp_suspend(void);
 extern int swsusp_write(void);
 extern int swsusp_check(void);
@@ -73,31 +74,6 @@
 static int in_suspend __nosavedata = 0;
 
 
-/**
- *	free_some_memory -  Try to free as much memory as possible
- *
- *	... but do not OOM-kill anyone
- *
- *	Notice: all userland should be stopped at this point, or
- *	livelock is possible.
- */
-
-static void free_some_memory(void)
-{
-	unsigned int i = 0;
-	unsigned int tmp;
-	unsigned long pages = 0;
-	char *p = "-\\|/";
-
-	printk("Freeing memory...  ");
-	while ((tmp = shrink_all_memory(10000))) {
-		pages += tmp;
-		printk("\b%c", p[i++ % 4]);
-	}
-	printk("\bdone (%li pages freed)\n", pages);
-}
-
-
 static inline void platform_finish(void)
 {
 	if (pm_disk_mode == PM_DISK_PLATFORM) {
@@ -127,8 +103,8 @@
 	}
 
 	/* Free memory before shutting down devices. */
-	free_some_memory();
-	return 0;
+	if (!(error = swsusp_shrink_memory()))
+		return 0;
 thaw:
 	thaw_processes();
 	enable_nonboot_cpus();
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 14:12:07.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/power.h	2005-10-29 14:12:12.000000000 +0200
@@ -58,6 +58,7 @@
 extern asmlinkage int swsusp_arch_suspend(void);
 extern asmlinkage int swsusp_arch_resume(void);
 
+extern unsigned count_data_pages(void);
 extern void swsusp_free(void);
 
 extern unsigned snapshot_pages_to_save(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 14:12:07.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/snapshot.c	2005-10-29 14:12:12.000000000 +0200
@@ -67,17 +67,15 @@
 	BUG_ON(PageReserved(page) && PageNosave(page));
 	if (PageNosave(page))
 		return 0;
-	if (PageReserved(page) && pfn_is_nosave(pfn)) {
-		pr_debug("[nosave pfn 0x%lx]", pfn);
+	if (PageReserved(page) && pfn_is_nosave(pfn))
 		return 0;
-	}
 	if (PageNosaveFree(page))
 		return 0;
 
 	return 1;
 }
 
-static unsigned count_data_pages(void)
+unsigned count_data_pages(void)
 {
 	struct zone *zone;
 	unsigned long zone_pfn;
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 14:12:07.000000000 +0200
+++ linux-2.6.14-rc5-mm1/kernel/power/swsusp.c	2005-10-29 14:12:12.000000000 +0200
@@ -77,6 +77,30 @@
 #include "power.h"
 
 #ifdef CONFIG_HIGHMEM
+static unsigned count_highmem_pages(void)
+{
+	struct zone *zone;
+	unsigned n = 0;
+
+	for_each_zone (zone)
+		if (is_highmem(zone)) {
+			mark_free_pages(zone);
+			for (zone_pfn = 0; zone_pfn < zone->spanned_pages; zone_pfn++) {
+				struct page *page;
+				unsigned long pfn = zone_pfn + zone->zone_start_pfn;
+				if (!pfn_valid(pfn))
+					continue;
+				page = pfn_to_page(pfn);
+				if (PageReserved(page))
+					continue;
+				if (PageNosaveFree(page))
+					continue;
+				n++;
+			}
+		}
+	return n;
+}
+
 struct highmem_page {
 	char *data;
 	struct page *page;
@@ -163,10 +187,46 @@
 	return 0;
 }
 #else
+static unsigned count_highmem_pages(void) { return 0; }
 static int save_highmem(void) { return 0; }
 static int restore_highmem(void) { return 0; }
 #endif /* CONFIG_HIGHMEM */
 
+/**
+ *	swsusp_shrink_memory -  Try to free as much memory as needed
+ *
+ *	... but do not OOM-kill anyone
+ *
+ *	Notice: all userland should be stopped before it is called, or
+ *	livelock is possible.
+ */
+
+int swsusp_shrink_memory(void)
+{
+	unsigned long cnt;
+	long tmp;
+	unsigned long pages = 0;
+	unsigned int i = 0;
+	char *p = "-\\|/";
+
+	printk("Shrinking memory...  ");
+	do {
+		cnt = count_data_pages() + count_highmem_pages();
+		cnt += (cnt + PBES_PER_PAGE - 1) / PBES_PER_PAGE +
+			PAGES_FOR_IO;
+		tmp = cnt - nr_free_pages();
+		if (tmp > 0) {
+			tmp = 10000;
+			cnt = shrink_all_memory(tmp);
+			pages += cnt;
+		}
+		printk("\b%c", p[i++%4]);
+	} while (tmp > 0 && cnt > 0);
+	printk("\bdone (%lu pages freed)\n", pages);
+
+	return tmp > 0 ? -ENOMEM : 0;
+}
+
 int swsusp_suspend(void)
 {
 	int error;
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 14:01:27.000000000 +0200
+++ linux-2.6.14-rc5-mm1/include/linux/suspend.h	2005-10-29 14:42:16.000000000 +0200
@@ -73,6 +73,6 @@
  * XXX: We try to keep some more pages free so that I/O operations succeed
  * without paging. Might this be more?
  */
-#define PAGES_FOR_IO	512
+#define PAGES_FOR_IO	1024
 
 #endif /* _LINUX_SWSUSP_H */

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC][PATCH 1/6] swsusp: rework swsusp_suspend
  2005-10-29 20:01 ` [RFC][PATCH 1/6] swsusp: rework swsusp_suspend Rafael J. Wysocki
@ 2005-10-29 22:25   ` Pavel Machek
  0 siblings, 0 replies; 17+ messages in thread
From: Pavel Machek @ 2005-10-29 22:25 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-pm, LKML

[-- Attachment #1: Type: text/plain, Size: 287 bytes --]


> This is a preliminary step.  It makes only the functions in swsusp.c call
> functions in snapshot.c and not both ways.  Basically, it moves the code
> without changing its functionality.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

ACK, nice.
								Pavel
-- 
Thanks, Sharp!

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC][PATCH 2/6] swsusp: move snapshot-handling functions to snapshot.c
  2005-10-29 20:06 ` [RFC][PATCH 2/6] swsusp: move snapshot-handling functions to snapshot.c Rafael J. Wysocki
@ 2005-10-29 22:27   ` Pavel Machek
  0 siblings, 0 replies; 17+ messages in thread
From: Pavel Machek @ 2005-10-29 22:27 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-pm, LKML

[-- Attachment #1: Type: text/plain, Size: 379 bytes --]

On So 29-10-05 22:06:22, Rafael J. Wysocki wrote:
> This is another preliminary step.  It moves the snapshot-handling functions
> remaining in swsusp.c to snapshot.c (moving the code without changing
> the functionality) and makes the next patch be more clear (in my opinion).
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

ACK and thanks.
								Pavel
-- 
Thanks, Sharp!

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC][PATCH 3/6] swsusp: introduce the swap map structure and interface functions
  2005-10-29 20:32 ` [RFC][PATCH 3/6] swsusp: introduce the swap map structure and interface functions Rafael J. Wysocki
@ 2005-10-29 22:57   ` Pavel Machek
  2005-10-29 23:20   ` Pavel Machek
  1 sibling, 0 replies; 17+ messages in thread
From: Pavel Machek @ 2005-10-29 22:57 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-pm, LKML

[-- Attachment #1: Type: text/plain, Size: 1758 bytes --]

Hi!

> 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).

One small comment. I miss "a" in "swap". Pretty please...

> + * Rafael J. Wysocki <rjw@sisk.pl>
> + * Added the swap map data structure and reworked the handling of swap
> + *

Feel free to add yourself to CREDITS, too. CREDITS are going to stay,
but this does not really belong here, and may have to be moved
somewhere else in future.

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

Nice...

> +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;

Please don't. Just use "struct swap_map_page" instead.

> +extern unsigned snapshot_pages_to_save(void);
> +extern unsigned snapshot_image_pages(void);

Make it "extern unsigned int". (That is in more than one place).

> +int snapshot_recv_init(unsigned nr_pages, unsigned img_pages)

Please, usefull words, that's receive_init and image_pages....

Otherwise it looks good. I'll check it once more...

							Pavel
-- 
Thanks, Sharp!

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC][PATCH 0/6] swsusp: rework swap handling
  2005-10-29 19:58 [RFC][PATCH 0/6] swsusp: rework swap handling Rafael J. Wysocki
                   ` (5 preceding siblings ...)
  2005-10-29 20:46 ` [RFC][PATCH 6/6] swsusp: improve freeing of memory Rafael J. Wysocki
@ 2005-10-29 23:05 ` Pavel Machek
  2005-10-30 12:17   ` Pavel Machek
  6 siblings, 1 reply; 17+ messages in thread
From: Pavel Machek @ 2005-10-29 23:05 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-pm, LKML

[-- Attachment #1: Type: text/plain, Size: 468 bytes --]

Hi!

> I have divided the changes into some more or less logical steps for clarity.
> Although the code has been designed as proof-of-concept, it is functional
> and has been tested on x86-64, except for the cryptographic functionality
> and error paths.

Don't worry about crypto paths too much. It is my fault, I should not
have taken them in the first place. Just ask ast for testing when you
are reasonably confident it will work.
								Pavel
-- 
Thanks, Sharp!

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC][PATCH 3/6] swsusp: introduce the swap map structure and interface functions
  2005-10-29 20:32 ` [RFC][PATCH 3/6] swsusp: introduce the swap map structure and interface functions Rafael J. Wysocki
  2005-10-29 22:57   ` Pavel Machek
@ 2005-10-29 23:20   ` Pavel Machek
  1 sibling, 0 replies; 17+ messages in thread
From: Pavel Machek @ 2005-10-29 23:20 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: LKML, linux-pm

Hi!

> 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
> +
> +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;
> +}

No, sorry, I don't like that. I like existing interface better. It
should be something like...

system_snapshot(), returns structure to save and nr_pages.

system_restore() takes struct pbe * (or something like that) and
nr_pages, and takes care. 

snapshot_send_init()...snapshot_finish() interface is too complex in
my eyes, and will be hard to get right.
							Pavel
-- 
Thanks, Sharp!

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC][PATCH 4/6] swsusp: move swap check out of swsusp_suspend
  2005-10-29 20:36 ` [RFC][PATCH 4/6] swsusp: move swap check out of swsusp_suspend Rafael J. Wysocki
@ 2005-10-29 23:21   ` Pavel Machek
  2005-10-30 12:40     ` Rafael J. Wysocki
  0 siblings, 1 reply; 17+ messages in thread
From: Pavel Machek @ 2005-10-29 23:21 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-pm, LKML

[-- Attachment #1: Type: text/plain, Size: 185 bytes --]

Hi!

> This is a non-essential step making the next patch possible.  No functionality
> changes.

If you can push this before 3/6, that would be nice.

								Pavel
-- 
Thanks, Sharp!

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC][PATCH 4/6] swsusp: move swap check out of swsusp_suspend
  2005-10-30 12:40     ` Rafael J. Wysocki
@ 2005-10-30 11:46       ` Pavel Machek
  0 siblings, 0 replies; 17+ messages in thread
From: Pavel Machek @ 2005-10-30 11:46 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: LKML, linux-pm

Hi!

> > > This is a non-essential step making the next patch possible.  No functionality
> > > changes.
> > 
> > If you can push this before 3/6, that would be nice.
> 
> Sure.  I think I'll send the two patches you have already acked and this one
> to Andrew as a separate series.  Then I'll get back to the 3/6 etc.

Splitting swsusp.c into swsusp.c and swap.c looked nice, too, btw. We
may finally get rid of "swsusp" name :-). But, in the long term, I'd
like to see swap reading/writing support removed from kernel. That
2.8.0 or something, through...
								Pavel
-- 
Thanks, Sharp!

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC][PATCH 0/6] swsusp: rework swap handling
  2005-10-29 23:05 ` [RFC][PATCH 0/6] swsusp: rework swap handling Pavel Machek
@ 2005-10-30 12:17   ` Pavel Machek
  0 siblings, 0 replies; 17+ messages in thread
From: Pavel Machek @ 2005-10-30 12:17 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: LKML, linux-pm, ast

Hi!

> > I have divided the changes into some more or less logical steps for clarity.
> > Although the code has been designed as proof-of-concept, it is functional
> > and has been tested on x86-64, except for the cryptographic functionality
> > and error paths.
> 
> Don't worry about crypto paths too much. It is my fault, I should not
> have taken them in the first place. Just ask ast for testing when you
> are reasonably confident it will work.

We exchanged few emails with ast, and he'll probably not kill me if I
remove crypto swsusp support. That's good news ;-).
								Pavel
-- 
Thanks, Sharp!

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC][PATCH 4/6] swsusp: move swap check out of swsusp_suspend
  2005-10-29 23:21   ` Pavel Machek
@ 2005-10-30 12:40     ` Rafael J. Wysocki
  2005-10-30 11:46       ` Pavel Machek
  0 siblings, 1 reply; 17+ messages in thread
From: Rafael J. Wysocki @ 2005-10-30 12:40 UTC (permalink / raw)
  To: Pavel Machek; +Cc: LKML, linux-pm

Hi,

On Sunday, 30 of October 2005 01:21, Pavel Machek wrote:
> Hi!
> 
> > This is a non-essential step making the next patch possible.  No functionality
> > changes.
> 
> If you can push this before 3/6, that would be nice.

Sure.  I think I'll send the two patches you have already acked and this one
to Andrew as a separate series.  Then I'll get back to the 3/6 etc.

Greetings,
Rafael

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC][PATCH 5/6] swsusp: move swap-handling functions to separate file
  2005-10-29 20:41 ` [RFC][PATCH 5/6] swsusp: move swap-handling functions to separate file Rafael J. Wysocki
@ 2005-10-30 13:33   ` Pavel Machek
  0 siblings, 0 replies; 17+ messages in thread
From: Pavel Machek @ 2005-10-30 13:33 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Pavel Machek, LKML, linux-pm

Hi!

> This patch is to show that the swap-handling part of swsusp is really independent
> and it can be moved entirely to a separate file.  It introduces the file swap.c
> containing all of the swap-handling code.
> 
> After the change swsusp.c contains the functions that in my opinion do not
> belong to either the snapshot-handling part or the swap-handling part
> (swsusp_suspend(), swsusp_resume() and the functions related to highmem).

Highmem handling should go to snapshot.c. Other parts do not need to know about
it.


linux-2.6.14-rc5-mm1/kernel/power/swap.c
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.14-rc5-mm1/kernel/power/swap.c	2005-10-29 13:26:26.000000000 +0200
> @@ -0,0 +1,915 @@
> +/*
> + * linux/kernel/power/snapshot.c

wrong name.

> +static void dump_info(void)
> +{
> +	pr_debug(" swsusp: Version: %u\n",swsusp_info.version_code);
> +	pr_debug(" swsusp: Num Pages: %ld\n",swsusp_info.num_physpages);
> +	pr_debug(" swsusp: UTS Sys: %s\n",swsusp_info.uts.sysname);
> +	pr_debug(" swsusp: UTS Node: %s\n",swsusp_info.uts.nodename);
> +	pr_debug(" swsusp: UTS Release: %s\n",swsusp_info.uts.release);
> +	pr_debug(" swsusp: UTS Version: %s\n",swsusp_info.uts.version);
> +	pr_debug(" swsusp: UTS Machine: %s\n",swsusp_info.uts.machine);
> +	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: Total: %ld Pages\n", swsusp_info.pages);
> +}

I'd rather get rid of this, or at least made it *way* more terse.

Ok, that probably belongs to separate patch.

				Pavel
-- 
64 bytes from 195.113.31.123: icmp_seq=28 ttl=51 time=448769.1 ms         

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2005-10-30 13:33 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-10-29 19:58 [RFC][PATCH 0/6] swsusp: rework swap handling Rafael J. Wysocki
2005-10-29 20:01 ` [RFC][PATCH 1/6] swsusp: rework swsusp_suspend Rafael J. Wysocki
2005-10-29 22:25   ` Pavel Machek
2005-10-29 20:06 ` [RFC][PATCH 2/6] swsusp: move snapshot-handling functions to snapshot.c Rafael J. Wysocki
2005-10-29 22:27   ` Pavel Machek
2005-10-29 20:32 ` [RFC][PATCH 3/6] swsusp: introduce the swap map structure and interface functions Rafael J. Wysocki
2005-10-29 22:57   ` Pavel Machek
2005-10-29 23:20   ` Pavel Machek
2005-10-29 20:36 ` [RFC][PATCH 4/6] swsusp: move swap check out of swsusp_suspend Rafael J. Wysocki
2005-10-29 23:21   ` Pavel Machek
2005-10-30 12:40     ` Rafael J. Wysocki
2005-10-30 11:46       ` Pavel Machek
2005-10-29 20:41 ` [RFC][PATCH 5/6] swsusp: move swap-handling functions to separate file Rafael J. Wysocki
2005-10-30 13:33   ` Pavel Machek
2005-10-29 20:46 ` [RFC][PATCH 6/6] swsusp: improve freeing of memory Rafael J. Wysocki
2005-10-29 23:05 ` [RFC][PATCH 0/6] swsusp: rework swap handling Pavel Machek
2005-10-30 12:17   ` Pavel Machek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox