diff -ruN beta19-original/drivers/ide/ide-disk.c beta19-19/drivers/ide/ide-disk.c
--- beta19-original/drivers/ide/ide-disk.c	2003-03-04 13:09:45.000000000 +1300
+++ beta19-19/drivers/ide/ide-disk.c	2003-03-22 20:08:51.000000000 +1200
@@ -57,6 +57,7 @@
 #include <linux/genhd.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/suspend-debug.h>
 
 #define _IDE_DISK
 
@@ -1851,13 +1852,32 @@
 
 void ide_disk_suspend(void)
 {
-	int i;
+	int i, drivenum = 0;
+	ide_drive_t *drive;
 
 	i=1;
 	while (ide_disks_busy(i++)) {
 		schedule();
 		i &= (unsigned int)((1<<21)-1);	/* CBD: we print a warning only after a great number of errors, no need to frighten the newbie ;-) */
 	}
+	/* Ensure caches are purged too (derived from module unload) - Nigel Cunningham */
+	PRINTK(SUSPEND_VERBOSE, "Flushing writeback caches.\n");
+	while ((drive = ide_scan_devices(ide_disk, idedisk_driver.name,
+			&idedisk_driver, drivenum)) != NULL) {
+		PRINTK(SUSPEND_VERBOSE, "Drive %s: (%d, %d)", 
+				drive->name,
+				(drive->id->command_set_1 & 0x20),
+				drive->id->cfs_enable_1 & 0x20);
+		if ((drive->id->command_set_1 & 0x20) && drive->id->cfs_enable_1 & 0x20) {
+			PRINTK(SUSPEND_VERBOSE, "Flushing write cache.");
+			if (do_idedisk_flushcache(drive))
+				printk(KERN_ERR "Failed!");
+		}
+		PRINTK(SUSPEND_VERBOSE, "\n");
+		drivenum++;
+	}
+	PRINTK(SUSPEND_VERBOSE, "Done.\n");
+	
 	for (i=0; i<MAX_HWIFS; i++)
 		if(ide_hwifs[i].present) { /* when using pcmcia ide-disk hwgroup is non NULL but the
 					      disk can be absent */		
diff -ruN beta19-original/include/asm-i386/suspend.h beta19-19/include/asm-i386/suspend.h
--- beta19-original/include/asm-i386/suspend.h	2003-03-04 13:09:45.000000000 +1300
+++ beta19-19/include/asm-i386/suspend.h	2003-03-29 10:45:50.000000000 +1200
@@ -217,6 +217,7 @@
 /* Local variables for do_magic */
 static int loop __nosavedata = 0;
 static int loop2 __nosavedata = 0;
+static int state1 __nosavedata = 0;
 
 /*
  * FIXME: This function should really be written in assembly. Actually
@@ -259,13 +260,16 @@
  */
 	do_magic_resume_1();
 
-	for (loop=0; loop < pageset1_size; loop++) {
+	state1 = swsusp_state[1];	/* Make whether pausing between steps persistent */
+	
+	for (loop=0; loop < pagedir_nosave.pageset_size; loop++) {
 		/* You may not call something (like copy_page) here: see above */
-		for (loop2=0; loop2 < PAGE_SIZE; loop2++) {
-			*(((char *)(PAGEDIR_ENTRY(pagedir_nosave,loop)->address1))+loop2) =
-				*(((char *)(PAGEDIR_ENTRY(pagedir_nosave,loop)->address3))+loop2);
+		for (loop2=0; loop2 < (PAGE_SIZE / sizeof(unsigned long)); loop2++) {
+			*(((unsigned long *)(page_address(PAGEDIR_ENTRY(&pagedir_nosave,loop)->origaddress)))+loop2) =
+				*(((unsigned long *)(page_address(PAGEDIR_ENTRY(&pagedir_nosave,loop)->address)))+loop2);
 			__flush_tlb();
 		}
+		
 	}
 
 	restore_processor_context();
@@ -273,6 +277,8 @@
 /* Ahah, we now run with our old stack, and with registers copied from
    suspend time */
 
+	swsusp_state[1] = state1;
+
 	do_magic_resume_2();
 }
 #endif 
diff -ruN beta19-original/include/linux/mm.h beta19-19/include/linux/mm.h
--- beta19-original/include/linux/mm.h	2003-03-19 11:56:55.000000000 +1200
+++ beta19-19/include/linux/mm.h	2003-03-29 10:45:50.000000000 +1200
@@ -301,7 +301,7 @@
 #define PG_nosave		17      /* swsusp - used to be 16!! */
 #define PG_free			18	/* swsusp - page is unused - dont rely on outside swsusp! */
 #define PG_knowshared		19
-#define PG_dontcopy		20
+#define PG_Pageset2		20
 #define PG_collides		18	/* Reuse during start of resume */
 
 /* Make it prettier to test the above... */
@@ -329,9 +329,9 @@
 #define PageKnowshared(page)	test_bit(PG_knowshared, &(page)->flags)
 #define SetPageKnowshared(page)	set_bit(PG_knowshared, &(page)->flags)
 #define ClearPageKnowshared(page)	clear_bit(PG_knowshared, &(page)->flags)
-#define PageDontcopy(page)	test_bit(PG_dontcopy, &(page)->flags)
-#define SetPageDontcopy(page)	set_bit(PG_dontcopy, &(page)->flags)
-#define ClearPageDontcopy(page)	clear_bit(PG_dontcopy, &(page)->flags)
+#define PagePageset2(page)	test_bit(PG_Pageset2, &(page)->flags)
+#define SetPagePageset2(page)	set_bit(PG_Pageset2, &(page)->flags)
+#define ClearPagePageset2(page)	clear_bit(PG_Pageset2, &(page)->flags)
 #define PageCollides(page)		test_bit(PG_collides, &(page)->flags)
 #define SetPageCollides(page)	set_bit(PG_collides, &(page)->flags)
 #define ClearPageCollides(page)	clear_bit(PG_collides, &(page)->flags)
diff -ruN beta19-original/include/linux/suspend-debug.h beta19-19/include/linux/suspend-debug.h
--- beta19-original/include/linux/suspend-debug.h	1970-01-01 12:00:00.000000000 +1200
+++ beta19-19/include/linux/suspend-debug.h	2003-03-31 15:35:27.000000000 +1200
@@ -0,0 +1,61 @@
+
+#ifndef _LINUX_SWSUSP_DEBUG_H
+#define _LINUX_SWSUSP_DEBUG_H
+
+#include <linux/suspend.h>
+
+#define name_suspend "Suspend Machine:  "
+#define name_resume  "Resume Machine:   "
+#define swsusp_version "beta 19-19"
+#define name_swsusp  "Swsusp " swsusp_version ":   "
+#define console_suspend "S U S P E N D   T O   D I S K"
+#define console_resume "R E S U M E   F R O M   D I S K"
+/*
+ * Debug
+ */
+#define	DEBUG_DEFAULT	1	/* activate debugging setting in /proc/sys/kernel/swsusp
+				   Commenting out will save very few cycles and prevent from
+				   debugging new hardware. */
+/* first status register */
+#define SUSPEND_PENDING (swsusp_state[0] & 0x1)
+#define SUSPEND_ABORTING (swsusp_state[0] & 0x2)
+/* second status register */
+#define SUSPEND_REBOOT  (swsusp_state[1] & 0x1)
+#define SUSPEND_FREEMEM (swsusp_state[1] & 0x2)
+#define SUSPEND_PAUSE (swsusp_state[1] & 0x4)
+#define SUSPEND_NOPAGESET2 (swsusp_state[1] & 0x8)
+#define SUSPEND_NOPAGESET2INACTIVE (swsusp_state[1] & 0x10)
+#define SUSPEND_NOPAGESET2ACTIVE (swsusp_state[1] & 0x20)
+#define SUSPEND_LOGALL (swsusp_state[1] & 0x40)
+/* third status register */
+#define SUSPEND_DEBUG   	0x1
+#define SUSPEND_PROGRESS   	0x2
+#define SUSPEND_VERBOSE   	0x4
+#define SUSPEND_SLOW		0x8
+#define SUSPEND_BEEP		0x10
+/* fourth status register */
+#define STAGE_FREEZER 		0x1
+#define STAGE_EAT_MEMORY 	0x2
+#define STAGE_MARK_PAGESET2 	0x4
+#define STAGE_WRITE_PAGESETS 	0x8
+#define STAGE_FREE_PAGES 	0x10
+
+extern void printnolog(int restartline, const char *fmt, ...);
+
+#ifdef DEBUG_DEFAULT
+# define PRINTTHRESH 3		/* Number of lines at the start/end of pagesets to show */
+extern int currentstage;	/* Current stage (STAGEPRINT macro) */
+# define STAGEPRINT ((currentstage == 0) || (swsusp_state[3] & currentstage))
+# define PRINTK(mask, f, a...)	((((mask) & swsusp_state[2]) && STAGEPRINT) ? printk(f, ## a):0)
+# define PRINTNOLOG(mask, restartline, f, a...)	((((mask) & swsusp_state[2]) && STAGEPRINT) ? \
+		printnolog(restartline, f, ## a) :0)
+# define MDELAY(a) 		if (swsusp_state[2] & SUSPEND_SLOW)  mdelay(a); else
+# define beepOK if (swsusp_state[2] & SUSPEND_BEEP)  { currentbeep += 200;  kd_mksound(currentbeep,HZ/8); mdelay(150);} else
+# define beepERR if (swsusp_state[2] & SUSPEND_BEEP) { kd_mksound(300,HZ/4); mdelay(300); } else
+#else
+# define PRINTK(f, a...)
+# define MDELAY(a)
+# define beepOK
+# define beepERR
+#endif
+#endif
diff -ruN beta19-original/include/linux/suspend.h beta19-19/include/linux/suspend.h
--- beta19-original/include/linux/suspend.h	2003-03-04 13:09:45.000000000 +1300
+++ beta19-19/include/linux/suspend.h	2003-03-29 10:45:50.000000000 +1200
@@ -7,6 +7,7 @@
 #include <linux/swap.h>
 #include <linux/notifier.h>
 #include <linux/config.h>
+#include <linux/kdev_t.h>
 
 extern unsigned char software_suspend_enabled;
 
@@ -14,19 +15,24 @@
 #define RESUME_SPECIFIED 2
 
 #ifdef CONFIG_SOFTWARE_SUSPEND
-extern int swsusp_state[5];
+extern int swsusp_state[6];
 
 /* page backup entry */
 typedef struct pbe {
-	unsigned long address1;		/* original address of pageset1 entry */
-	unsigned long address2;		/* original address of pageset2 entry */
-	unsigned long address3;		/* copy back address */
-	swp_entry_t swap_address1;
-	swp_entry_t swap_address2;
-	swp_entry_t dummy[3];		/* we need at least one sizeof(long) scratch space at 
+	struct page * origaddress;	/* original address of pageset entry */
+	struct page * address;		/* address of copy of pageset entry (pageset1 only) */
+	swp_entry_t swap_address;
+	swp_entry_t dummy;		/* we need at least one sizeof(long) scratch space at 
 					 * end of page (see link, diskpage) and
 					 * sizeof(struct pbe) must be multiple of 4*sizeof(long)
 					 */
+} suspend_pbe_t;
+
+typedef struct pagedir {
+	int pageset_size;
+	int pagedir_size;
+	int alloc_from;
+	struct pbe **data;
 } suspend_pagedir_t;
 
 #define SWAP_FILENAME_MAXLENGTH	32
@@ -42,23 +48,22 @@
 	unsigned long swapforimage;
 	unsigned int expected_size1;
 	unsigned int expected_size2;
-	suspend_pagedir_t **suspend_pagedir;
-	unsigned int pageset1_size;
-	unsigned int pageset2_size;
-	unsigned int pagedir_size;
+	struct pagedir pagedir;
 	int param0;
 	int param1;
 	int param2;
 	int param3;
 	int param4;
+	kdev_t kdevs[MAX_SWAPFILES];
 	struct swap_location {
 		char filename[SWAP_FILENAME_MAXLENGTH];
 	} swap_location[MAX_SWAPFILES];
 };
 
-#define SUSPEND_PD_PAGES(x)     (((x)*sizeof(struct pbe))/PAGE_SIZE+1)
-#define PAGEDIR_CAPACITY(x)     (((x)*PAGE_SIZE/sizeof(struct pbe)))
-#define PAGEDIR_ENTRY(pagedir, i) (pagedir[i/PAGEDIR_CAPACITY(1)] + (i%PAGEDIR_CAPACITY(1)))
+#define PBES_PER_PAGE	(PAGE_SIZE/sizeof(struct pbe))
+#define SUSPEND_PD_PAGES(x)	(((x)+PBES_PER_PAGE-1)/PBES_PER_PAGE)
+#define PAGEDIR_CAPACITY(x)     (((x)*PBES_PER_PAGE))
+#define PAGEDIR_ENTRY(pagedir, i) ((pagedir)->data[((i)/PBES_PER_PAGE)] + ((i)%PBES_PER_PAGE))
    
 extern struct tq_struct suspend_tq;
 
diff -ruN beta19-original/kernel/suspend.c beta19-19/kernel/suspend.c
--- beta19-original/kernel/suspend.c	2003-03-04 13:09:45.000000000 +1300
+++ beta19-19/kernel/suspend.c	2003-03-31 18:19:32.000000000 +1200
@@ -76,6 +76,7 @@
 #include <linux/console.h>
 #include <linux/fs.h>
 #include <linux/string.h>
+#include <linux/suspend-debug.h>
 
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
@@ -90,13 +91,13 @@
  */
 #define SWSUSP_CHECK_TIMEOUT	(HZ)
 
+#if defined(CONFIG_VT) && defined(CONFIG_VT_CONSOLE)
 #define SUSPEND_CONSOLE	(MAX_NR_CONSOLES-1)
-#if 0 || !defined(CONFIG_VT) || !defined(CONFIG_VT_CONSOLE)
-# undef SUSPEND_CONSOLE
 #endif
 
 #define TIMEOUT	(6 * HZ)			/* Timeout for stopping processes */
-#define ADDRESS(x) ((unsigned long) phys_to_virt(((x) << PAGE_SHIFT)))
+#define __ADDRESS(x)  ((unsigned long) phys_to_virt(x))
+#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT)
 
 extern int C_A_D;
 
@@ -116,8 +117,11 @@
 extern void vt_console_print(struct console *co, const char * b, unsigned count);
 extern void reset_terminal(int currcons, int do_clear);
 extern void hide_cursor(int currcons);
-extern void free_suspend_cache_page(void);
+extern void free_suspend_cache_page(struct page * page);
 extern inline pte_t *lookup_address(unsigned long address);
+extern int rw_swap_page_base(int rw, swp_entry_t entry, struct page *page);
+extern int try_to_free_pages_zone_swsusp(zone_t *classzone, unsigned int gfp_mask, int priority, int nr_pages);
+extern int try_to_free_pages_swsusp(unsigned int gfp_mask, int amount_needed);
 
 /* Locks */
 spinlock_t suspend_pagedir_lock __nosavedata = SPIN_LOCK_UNLOCKED;
@@ -125,13 +129,16 @@
 /* Variables to be preserved over suspend */
 static int new_loglevel = 7;
 static int orig_loglevel = 0;
+#ifdef CONFIG_VT
 static int orig_fgconsole;
-static int pageset1_size_check, pageset2_size_check;
+#endif
 
 static int resume_status = 0;
 static char resume_file[256] = "";			/* For resume= kernel option */
 /* Local variables that should not be affected by save */
-static int pageset1_size, pageset2_size; // Can't make unsigned int because mucks up MAX function
+static int pageset2_sizelow;
+#define pageset1_size (pagedir1.pageset_size)
+#define pageset2_size (pagedir2.pageset_size)
 static int expected_size1, expected_size2; 
 
 static int pm_suspend_state = 0;
@@ -146,8 +153,10 @@
  * forgetting the loaded pagedir at the same time. We then used the saved
  * pagedir to load pageset2 (if necessary) before freeing that pagedir.
  */
-static suspend_pagedir_t **pagedir_nosave __nosavedata = NULL;
-static suspend_pagedir_t **pagedir_save = NULL;
+
+static struct pagedir pagedir1 = { 0, 0, 0, NULL}, pagedir2 = {0, 0, 0, NULL};
+static int pagedir1_size, pagedir2_size;
+static struct pagedir __nosavedata pagedir_nosave;
 
 #ifdef SUSPEND_CONSOLE
 static int orig_kmsg;
@@ -173,8 +182,9 @@
 };
 
 typedef struct pageset_sizes_result {
-	int size1;
+	int size1; /* Can't be unsigned - breaks MAX function */
 	int size2;
+	int size2low;
 } pageset_sizes_t;
 
 /*
@@ -183,55 +193,9 @@
  */
 #define PAGES_FOR_IO 512
 
-#define name_suspend "Suspend Machine:  "
-#define name_resume  "Resume Machine:   "
-#define swsusp_version "beta 19"
-#define name_swsusp  "Swsusp " swsusp_version ":   "
-#define console_suspend "S U S P E N D   T O   D I S K"
-#define console_resume "R E S U M E   F R O M   D I S K"
-static int now_resuming = 0;
-/*
- * Debug
- */
-#define	DEBUG_DEFAULT	1	/* activate debugging setting in /proc/sys/kernel/swsusp
-				   Commenting out will save very few cycles and prevent from
-				   debugging new hardware. */
-/* first status register */
-#define SUSPEND_PENDING (swsusp_state[0] & 0x1)
-#define SUSPEND_ABORTING (swsusp_state[0] & 0x2)
-/* second status register */
-#define SUSPEND_REBOOT  (swsusp_state[1] & 0x1)
-#define SUSPEND_FREEMEM (swsusp_state[1] & 0x2)
-#define SUSPEND_PAUSE (swsusp_state[1] & 0x4)
-#define SUSPEND_NOPAGESET2 (swsusp_state[1] & 0x8)
-#define SUSPEND_NOPAGESET2INACTIVE (swsusp_state[1] & 0x10)
-#define SUSPEND_NOPAGESET2ACTIVE (swsusp_state[1] & 0x20)
-/* third status register */
-#define SUSPEND_DEBUG   	0x1
-#define SUSPEND_PROGRESS   	0x2
-#define SUSPEND_VERBOSE   	0x4
-#define SUSPEND_SLOW		0x8
-#define SUSPEND_BEEP		0x10
-/* fourth status register */
-#define STAGE_FREEZER 		0x1
-#define STAGE_EAT_MEMORY 	0x2
-#define STAGE_MARK_PAGESET2 	0x4
-#define STAGE_WRITE_PAGESETS 	0x8
-#define STAGE_FREE_PAGES 	0x10
-
-static void printnolog(const char *fmt, ...);
-
 #ifdef DEBUG_DEFAULT
-# define PRINTTHRESH 3		/* Number of lines at the start/end of pagesets to show */
-static int currentstage = 0;	/* Current stage (STAGEPRINT macro) */
-# define STAGEPRINT ((currentstage == 0) || (swsusp_state[3] & currentstage))
-# define PRINTK(mask, f, a...)	((((mask) & swsusp_state[2]) && STAGEPRINT) ? printk(f, ## a):0)
-# define PRINTNOLOG(mask, f, a...)	((((mask) & swsusp_state[2]) && STAGEPRINT) ? printnolog(f, ## a):0)
-# define MDELAY(a) 		if (swsusp_state[2] & SUSPEND_SLOW)  mdelay(a); else
-static int currentbeep = 260;
-# define beepOK if (swsusp_state[2] & SUSPEND_BEEP)  { currentbeep += 200;  kd_mksound(currentbeep,HZ/8); mdelay(150);} else
-# define beepERR if (swsusp_state[2] & SUSPEND_BEEP) { kd_mksound(300,HZ/4); mdelay(300); } else
-int swsusp_state[5] = {0,	/* when set to 1 swsusp_mainloop activates software_suspend
+int currentstage = 0;
+int swsusp_state[6] = {0,	/* when set to 1 swsusp_mainloop activates software_suspend
 				   bit 0: off = normal state, on = suspend required
 				   bit 1: aborting suspend
 				*/
@@ -241,7 +205,7 @@
 				   bit 2: pause between steps
 				   bit 3: no pageset2
 				*/
-		       SUSPEND_PROGRESS | SUSPEND_BEEP,
+		       SUSPEND_PROGRESS,
 				/* debugging parameter is an OR of flags:
 				   bit 0: debugging messages
 				   bit 1: display progression
@@ -251,121 +215,53 @@
 				   This value can also be set by swsusp_dbg= kernel boot option.
 				*/
 		       0,	/* which section to debug */
-		       0};	/* limit on image size in MB (test assumes 4K pages) */
+		       0,	/* limit on image size in MB (test assumes 4K pages) */
+		       0};	/* temporary - pageset1 entry to watch */
 #else
-# define PRINTK(f, a...)
-# define MDELAY(a)
-int swsusp_state[5] = {0,0,SUSPEND_PROGRESS, 0, 0}; /* see above for signification of values */
-# define beepOK
-# define beepERR
+int swsusp_state[6] = {0,0,SUSPEND_PROGRESS, 0, 0, 0}; /* see above for meaning of values */
 #endif
 
+static int now_resuming = 0;
+static int abort_requested = 0;
+static int currentbeep = 260;
+void printnolog(int restartline, const char *fmt, ...);
 // |= 0x2 is the flag that we are aborting the process.
 #define abort_suspend(f, a...) do { \
 	if (!SUSPEND_ABORTING) { \
-		printk("\n"); \
-		printk(f, ## a); \
-		display_debug_info(); \
-		beepERR; \
-                printk("Waiting for you to press and release SHIFT before continuing.\n"); \
-		while (!(shift_state & (1 << KG_SHIFT))) \
-			schedule(); \
-		while ((shift_state & (1 << KG_SHIFT))) \
-			schedule(); \
+		if (!abort_requested) { \
+			int was_locked = (spin_is_locked(&suspend_pagedir_lock)); \
+			if (was_locked) \
+				spin_unlock_irq(&suspend_pagedir_lock); \
+			printk("\n"); \
+			printk(f, ## a); \
+			beepERR; \
+        	        printk("Waiting for you to press and release SHIFT before continuing.\n"); \
+			while (!(shift_state & (1 << KG_SHIFT))) \
+				schedule(); \
+			while ((shift_state & (1 << KG_SHIFT))) \
+				schedule(); \
+			if (was_locked) \
+				spin_lock_irq(&suspend_pagedir_lock); \
+		} \
 		swsusp_state[0] |= 0x2; \
 	} \
 } while(0)
 
-static void generate_free_page_map(void) 
-{
-	int i, loop;
-	struct page * page;
-	pg_data_t *pgdat = pgdat_list;
-	unsigned type;
-	unsigned long flags;
-
-	for(i=0; i < max_mapnr; i++)
-		ClearPageFree(mem_map+i);
-	
-	for (type=0;type < MAX_NR_ZONES; type++) {
-		zone_t *zone = pgdat->node_zones + type;
-		int order = MAX_ORDER - 1;
-		free_area_t *area;
-		struct list_head *head, *curr;
-		spin_lock_irqsave(&zone->lock, flags);
-		do {
-			int first_entry = 1;
-			area = zone->free_area + order;
-			head = &area->free_list;
-			curr = head;
-
-			for(;;) {
-				if(!curr) {
-//					printk(KERN_ERR name_suspend "FIXME: this should not happen but it does!!!");
-					break;
-				}
-				if (first_entry) 
-					first_entry--;
-				else {
-					page = list_entry(curr, struct page, list);
-					for(loop=0; loop < (1 << order); loop++)
-						SetPageFree(page+loop);
-				}
-
-				curr = curr->next;
-				if (curr == head)
-					break; 				
-			}
-		} while(order--);
-		spin_unlock_irqrestore(&zone->lock, flags);
-	}
-	PRINTK(SUSPEND_VERBOSE,"At end, free pages is %d\n", nr_free_pages());
-}
-
-static int is_head_of_free_region(struct page * page)
-{
-	struct page * posn = page;
-
-	while (((posn-mem_map) < max_mapnr) && (PageFree(posn))) 
-		posn++;
-	return (posn - page);
-}
-
 /* Exported variables */
 struct page * last_suspend_cache_page;		/* Pointer to the last cache page */
 struct buffer_head * suspend_swap_bh;
 static unsigned long orig_mem_free = 0, base_mem_free = 0, swapforimage = 0;
 
-#define MAX(a,b)	((a)>(b)?(a):(b))	/* Maximum of two numbers */
-
-#ifdef SUSPEND_CONSOLE 
-#define WAIT_FOR_SHIFT { \
-	if (SUSPEND_PAUSE) { \
-		int i; \
-		unsigned int currcons = fg_console; \
-		printk("\r"); \
-		for (i=0; i < (video_num_columns - 2); i++) \
-			vt_console_print(suspend_console, " ", 1); \
-		printk("\r"); \
-		for (i=0; i < ((video_num_columns - 49) / 2); i++) \
-			vt_console_print(suspend_console, " ", 1); \
-		printk("Waiting for you to press and release SHIFT before continuing.\n"); \
-		while (!(shift_state & (1 << KG_SHIFT))) \
-			schedule(); \
-		while ((shift_state & (1 << KG_SHIFT))) \
-			schedule(); \
-	} \
-}
-#else
-#define WAIT_FOR_SHIFT  do { } while (0)
-#endif
+#define MAX(a,b)	((signed long)(a)>(signed long)(b)?(a):(b))	/* Maximum of two numbers */
+#define MIN(a,b)	((signed long)(a)<(signed long)(b)?(a):(b))	/* Minimum of two numbers */
 
 static kdev_t resume_device;
+static int resume_root_index = 0;
 #define BUFFER_BUSY_BITS	((1<<BH_Dirty) | (1<<BH_Lock))
 #define buffer_busy(bh)		(atomic_read(&(bh)->b_count) | ((bh)->b_state & BUFFER_BUSY_BITS))
 
-#define BREAD_BUFFER_HEAD(pos) \
-	bh = bread(resume_device, pos/PAGE_SIZE, PAGE_SIZE); \
+#define BREAD_BUFFER_HEAD(dev, pos) \
+	bh = bread(dev, pos/PAGE_SIZE, PAGE_SIZE); \
 	if (!bh || (!bh->b_data)) { \
 		return -EIO; \
 	}
@@ -373,74 +269,96 @@
 #define BFREE(bh) \
 	if (!buffer_busy(bh)) \
 		try_to_free_buffers(bh->b_page, __GFP_FAST); \
-	free_suspend_cache_page();
+	free_suspend_cache_page(last_suspend_cache_page);
 
 #define PRINTFREEMEM printk("Free memory: %d.\n", nr_free_pages())
 #define PANIC(f, a...) 	panic(f, ## a) 
 
-static int pagedir_size_check;
-static int pagedir_size;
-static int pagedir_capacity;
-static int pagedir_alloc_from;
 static struct sysinfo swapinfo;
 static unsigned long copybuff = 0;
 
+#define NUM_SYNC_WRITES 1
+struct io_info {
+	union p_diskpage buffer;
+	struct page * cache_page;
+	int orig_page_count;
+};
+
 static void drivers_unsuspend(void);
 static int drivers_suspend(void);
-static int read_secondary_pagedir(suspend_pagedir_t ** pagedir);
+static int read_secondary_pagedir(int);
 static int read_primary_suspend_image(char * specialfile, int noresume);
 static void markpagesforpageset2(void);
 static void display_debug_info(void);
 
+char * currentmessage;
+#if defined(CONFIG_LPP)
+extern void fbcon_progress( unsigned int progress, char *text);
+#define PROGRESS(value, text) fbcon_progress(value,text)
+#else
+#define PROGRESS(value,text)
+#endif
+
 /*
  * This function is intended to do the same job as printk, but without actually
  * logging what is printed. The point is to be able to get debugging info on
  * screen without filling the logs with "1/534. ^M 2/534^M. 3/534^M"
  */
-static void printnolog(const char *fmt, ...)
+void printnolog(int restartline, const char *fmt, ...)
 {
 #if defined(SUSPEND_CONSOLE)
 	static char printk_buf[1024];	/* Same as printk - should be safe */
 	va_list args;
-	int printed_len;
+	int printed_len = 0;
 
-	/*
-	 * We only print these messages if SUSPEND_PROGRESS is enabled, but
-	 * not if SUSPEND_PROGRESS is the only bit enabled (when we do the
-	 * nice display).
-	 */
+	
+	if (restartline) {
+		if (SUSPEND_LOGALL)
+			printk("\n");
+		else
+			printed_len = vsnprintf(printk_buf, sizeof(printk_buf), "\r", NULL);
+	}
 
-	/* Emit the output into the temporary buffer */
 	va_start(args, fmt);
-	printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
+	printed_len += vsnprintf(printk_buf + printed_len, sizeof(printk_buf) - printed_len, fmt, args);
 	va_end(args);
 
-	vt_console_print(suspend_console, printk_buf, printed_len);
+	if (SUSPEND_LOGALL) 
+		printk(printk_buf);
+	else
+		vt_console_print(suspend_console, printk_buf, printed_len);
+
 #endif
 }
 static void prepare_status(char * action)
 {
 #if defined(SUSPEND_CONSOLE)
-	char posn[2];
+#if !defined(CONFIG_LPP)
+	unsigned char posn[2];
+#endif
 	unsigned int currcons = fg_console;
 
 	if ((swsusp_state[2]& ~SUSPEND_BEEP) != SUSPEND_PROGRESS)
 		return;
 	
-	hide_cursor(fg_console);
-
 	barwidth = (video_num_columns - (video_num_columns / 2) - 1);
 	barposn = 0;
 
+#if defined(CONFIG_LPP)
+	currentmessage = action;
+	PROGRESS(newbarposn, currentmessage);
+#else
+	hide_cursor(fg_console);
+
 	/* Print version */
-	posn[0] = (char) (0);
-	posn[1] = (char) (video_num_lines);
+	posn[0] = (unsigned char) (0);
+	posn[1] = (unsigned char) (video_num_lines);
 	putconsxy(fg_console, posn);
 	vt_console_print(suspend_console, name_swsusp, strlen(name_swsusp));
 
 	/* Print header */
-	posn[0] = (char) ((video_num_columns - 29) / 2);
-	posn[1] = (char) ((video_num_lines / 3) -4);
+	posn[0] = (unsigned char) ((video_num_columns - 29) / 2);
+	posn[1] = (unsigned char) ((video_num_lines / 3) -4);
 	putconsxy(fg_console, posn);
 	if (now_resuming)
 		vt_console_print(suspend_console, console_resume, strlen(console_resume));
@@ -465,22 +383,22 @@
 	vt_console_print(suspend_console, "[", 1);
 
 	/* Draw right bracket of progress bar. */
-	posn[0] = (char) (video_num_columns - (video_num_columns / 4));
+	posn[0] = (unsigned char) (video_num_columns - (video_num_columns / 4));
 	putconsxy(fg_console, posn);
 	vt_console_print(suspend_console, "]", 1);
 
 	/* Position at start of progress */
-	posn[0] = (char) (video_num_columns / 4 + 1);
+	posn[0] = (unsigned char) (video_num_columns / 4 + 1);
 	putconsxy(fg_console, posn);
 
 	/* Clear bar */
 	for (barposn = 0; barposn < barwidth; barposn++)
 		vt_console_print(suspend_console, " ", 1);
 	putconsxy(fg_console, posn);
-
+	hide_cursor(fg_console);
+#endif
 	barposn = 0;
 	lastpercentage = 0;
-	hide_cursor(fg_console);
 	MDELAY(1000);
 #endif
 }
@@ -488,8 +406,10 @@
 static void update_status(int percentage)
 {
 #if defined(SUSPEND_CONSOLE)
-	char posn[2];
+#if !defined(CONFIG_LPP)
+	unsigned char posn[2];
 	unsigned int currcons = fg_console;
+#endif
 
 	if ((swsusp_state[2] & ~SUSPEND_BEEP)!= SUSPEND_PROGRESS)
 		return;
@@ -504,18 +424,82 @@
 	if (newbarposn == barposn)
 		return;       
 
-	posn[0] = (char) (video_num_columns / 4 + 1 + barposn);
-	posn[1] = (char) ((video_num_lines / 3) + 1);
+#if defined(CONFIG_LPP)
+	PROGRESS(newbarposn, currentmessage);
+#else
+	posn[0] = (unsigned char) (video_num_columns / 4 + 1 + barposn);
+	posn[1] = (unsigned char) ((video_num_lines / 3) + 1);
 	putconsxy(fg_console, posn);
 
 	lastpercentage = percentage;
 	
 	for (; barposn < newbarposn; barposn++)
 		vt_console_print(suspend_console, "-", 1);
-	barposn = newbarposn;
 	hide_cursor(fg_console);
 #endif
+	barposn = newbarposn;
+#endif
+}
+
+static void check_shift_keys(int pause)
+{
+#ifdef SUSPEND_CONSOLE 
+	int toggled;
+	int was_locked = (spin_is_locked(&suspend_pagedir_lock));
+	if (was_locked)
+		spin_unlock_irq(&suspend_pagedir_lock);
+
+	if (!SUSPEND_ABORTING) {
+		/* If pressing right hand CONTROL key, toggle ABORTING flag */
+		if ((shift_state & (1 << KG_ALT))) {
+			PRINTNOLOG(SUSPEND_VERBOSE, 1, "   --- ALT KEY BEING HELD : ABORTING PROCESS --- \n");
+			prepare_status("   --- ALT KEY BEING HELD : ABORTING PROCESS ---");
+			while ((shift_state & (1 << KG_ALT)))
+				schedule();
+			abort_requested = 1;
+			abort_suspend("Alt key pressed.\n");
+		}
+	}
+	/* If already pressing SHIFT, toggle SUSPEND_PAUSE */
+	toggled = 0;
+	if ((shift_state & (1 << KG_SHIFT))) {
+		swsusp_state[1] ^= 0x4;
+		PRINTNOLOG(SUSPEND_VERBOSE, 1, "Pausing %s.\n", 
+				SUSPEND_PAUSE ? "enabled" : "disabled");
+		while ((shift_state & (1 << KG_SHIFT)))
+			schedule();
+		toggled = 1;
+	}
+	if (SUSPEND_PAUSE && (!toggled) && (pause)) { /* Don't pause if just toggled */
+		int i;
+		unsigned int currcons = fg_console;
+		printk("\r");
+		for (i=0; i < (video_num_columns - 2); i++)
+			vt_console_print(suspend_console, " ", 1);
+		printk("\r");
+		for (i=0; i < ((video_num_columns - 49) / 2); i++)
+			vt_console_print(suspend_console, " ", 1);
+		printk("Waiting for you to press and release SHIFT before continuing.\n");
+		while (!(shift_state & (1 << KG_SHIFT)))
+			schedule();
+		while ((shift_state & (1 << KG_SHIFT)))
+			schedule();
+		/* If pressing ALT, toggle ABORTING flag */
+		if (!SUSPEND_ABORTING) {
+			if ((shift_state & (1 << KG_ALT))) {
+				PRINTNOLOG(SUSPEND_VERBOSE, 1, "   --- ALT KEY BEING HELD : ABORTING PROCESS --- \n");
+				prepare_status("   --- ALT KEY BEING HELD : ABORTING PROCESS ---");
+				while ((shift_state & (1 << KG_ALT)))
+					schedule();
+				abort_suspend("Alt key pressed.\n");
+			}
+		}
+	}
+	if (was_locked)
+		spin_lock_irq(&suspend_pagedir_lock);
+#endif
 }
+
 /*
  * Refrigerator and related stuff
  */
@@ -538,7 +522,7 @@
 	/* You need correct to work with real-time processes.
 	   OTOH, this way one process may see (via /proc/) some other
 	   process in stopped state (and thereby discovered we were
-	   suspended. We probably do not care. 
+	   suspended. We probably do not care). 
 	 */
 	processesfrozen++;
 	
@@ -561,34 +545,29 @@
 	processestofreeze = 0;
 	processesfrozen = 0;
 
-	PRINTK(SUSPEND_VERBOSE,name_suspend "Stopping tasks\n" );
+	PRINTNOLOG(SUSPEND_VERBOSE, 1, name_suspend "Stopping tasks\n" );
 	prepare_status("Freezing processes...");
 
 	suspend_task = current->pid;
-		
+
 	start_time = jiffies;
 	do {
 		todo = 0;
 		read_lock(&tasklist_lock);
 		for_each_task(p) {
 			unsigned long flags;
-			PRINTK(SUSPEND_VERBOSE, "%s: ", p->comm);
 			if (p->flags & PF_IOTHREAD) {
-				//PRINTK(SUSPEND_VERBOSE, "Suspend task.\n");
 				continue;
 			}
 			if (!(p->flags & PF_REFRIGERATE)) {
-				//PRINTK(SUSPEND_VERBOSE, "Refrigerate flag not set (%lx).\n", p->flags);
 				continue;
 			}
 			if (p->flags & PF_FROZEN) {
-				//PRINTK(SUSPEND_VERBOSE, "Already frozen.\n");
 				continue;
 			}
 			/* FIXME: smp problem here: we may not access other process' flags
 			   without locking */
 			p->flags |= PF_FREEZE;
-			PRINTK(SUSPEND_VERBOSE, "Signalling %s\n", p->comm);
 			spin_lock_irqsave(&p->sigmask_lock, flags);
 			signal_wake_up(p);
 			spin_unlock_irqrestore(&p->sigmask_lock, flags);
@@ -596,7 +575,7 @@
 		}
 		read_unlock(&tasklist_lock);
 		if (processestofreeze == 0) {
-			PRINTK(SUSPEND_VERBOSE,"%d processes to be frozen.\n", todo);
+			PRINTNOLOG(SUSPEND_VERBOSE, 1, "%d processes to be frozen.\n", todo);
 			processestofreeze = todo;
 		}
 		yield();
@@ -606,15 +585,17 @@
 		}
 		if (processestofreeze) // Don't update when enter fridge b/c requires process to acquire suspend console.
 			update_status((processestofreeze - processesfrozen) * 100 / processestofreeze);
-		PRINTK(SUSPEND_VERBOSE, "----\n");
+		//PRINTK(SUSPEND_VERBOSE, "----\n");
 	} while(processestofreeze > processesfrozen);
-	
+
+	check_shift_keys(1);
+
 	return 0;
 }
 
 static void thaw_processes(void)
 {
-	PRINTK(SUSPEND_VERBOSE, name_resume "Restarting tasks\n");
+	PRINTNOLOG(SUSPEND_VERBOSE, 1, name_resume "Restarting tasks\n");
 	suspend_task = 0;
 }
 
@@ -622,8 +603,30 @@
  * Saving part...
  */
 
+#define __calcpagedirsize(size) (SUSPEND_PD_PAGES(size))
+static int calcpagedirsize(int size)
+{
+	/*
+	 * If adding the pagedir and header will mean we need to store more pages 
+	 * than fit, we need to expand the size until it does fit (the pagedir itself 
+	 * might require more than one pagedir to store, hence the while).
+	 */
+	int result;
+
+	if (size == 0)
+		return 0;
+
+	result = __calcpagedirsize(size);
+	while (__calcpagedirsize(size + result + 1) > result) {
+		result++;
+	}
+	return result;
+}
+
 static __inline__ int fill_suspend_header(struct suspend_header *sh)
 {
+	int i;
+
 	memset((char *)sh, 0, sizeof(*sh));
 
 	sh->version_code = LINUX_VERSION_CODE;
@@ -636,15 +639,20 @@
 	strncpy(sh->version, system_utsname.version, 65);
 	sh->num_cpus = smp_num_cpus;
 	sh->page_size = PAGE_SIZE;
-	sh->suspend_pagedir = pagedir_save;
-	sh->pagedir_size = pagedir_size;
-	sh->pageset1_size = pageset1_size;
-	sh->pageset2_size = pageset2_size;
+	sh->pagedir = pagedir1;
 	sh->param0 = swsusp_state[0];
 	sh->param1 = swsusp_state[1];
 	sh->param2 = swsusp_state[2];
 	sh->param3 = swsusp_state[3];
 	sh->param4 = swsusp_state[4];
+
+	/* Save kdevs so we can handle multiple swap files on resume */
+	for (i = 0; i < MAX_SWAPFILES; i++)
+		if (swap_info[i].swap_device) {
+			PRINTNOLOG(SUSPEND_VERBOSE, 1, "Swap device %d is %x.\n", i, swap_info[i].swap_device);
+			sh->kdevs[i] = swap_info[i].swap_device;
+		}
+
 	/* TODO: needed? mounted fs' last mounted date comparison
 	 * [so they haven't been mounted since last suspend.
 	 * Maybe it isn't.] [we'd need to do this for _all_ fs-es]
@@ -687,79 +695,101 @@
 	return 0;
 }
 
-static int rw_swap_page_nofree(int rw, swp_entry_t entry, union p_diskpage cur)
+static int start_rw_swap_page_nofree(int rw, swp_entry_t entry, struct io_info cur, int sync_all)
 {
-	struct page *page;
+	struct page *page = virt_to_page(cur.buffer.address);
+
+	//PRINTK(SUSPEND_VERBOSE, "{%d}", nr_free_pages());
 
-	page=virt_to_page(cur.address);
+	atomic_inc(&page->count);
+	atomic_inc(&page->count);
 	lock_page(page);
-	rw_swap_page_nolock_norfree(rw, entry, cur.ptr);
-	sync_page(page);
-	if (suspend_swap_bh) {
+
+	BUG_ON(page->mapping);
+	page->mapping = &swapper_space;
+
+	if (rw_swap_page_base(rw, entry, page))
 		lock_page(page);
-		if (!buffer_busy(page->buffers))
-			try_to_free_buffers(page->buffers->b_page, __GFP_FAST);
-		free_suspend_cache_page();
-		unlock_page(page);
-	}
-	if(!Page_Uptodate(page)) {
-		abort_suspend("Page not up-to-date in rw_swap_page_nofree!\n");
-		return 1;
-	}
+
+	if (last_suspend_cache_page)
+		cur.cache_page = last_suspend_cache_page;
+	else
+		cur.cache_page = 0;
+	last_suspend_cache_page = NULL;
 	return 0;
 }
 
-#define RW_SWAP_PAGE_SYNC(a, b, c) rw_swap_page_sync(a, b, c)
-static int rw_swap_page_sync(int rw, swp_entry_t entry, union p_diskpage cur)
+static int finish_rw_swap_page_nofree(int rw, struct io_info cur)
 {
-	struct page *page;
-	//unsigned long dummy1;
-	//struct inode *dummy2;
+	struct page *page = virt_to_page(cur.buffer.address);
 
-	page=virt_to_page(cur.address);	
-	lock_page(page);
-	//get_swaphandle_info(entry, &dummy1, &suspend_device, &dummy2);
-	rw_swap_page_nolock(rw, entry, cur.ptr);
+	if (rw == WRITE) {
+		if (!block_flushpage(page, 0))
+			PAGE_BUG(page);
+	}
 	sync_page(page);
-	if (suspend_swap_bh) {
-		lock_page(page);
+
+	page->mapping = NULL;
+	UnlockPage(page);
+	if (page->buffers) {
+		if (suspend_swap_bh && (page->buffers != suspend_swap_bh))
+			PRINTK(SUSPEND_VERBOSE, "\nBuffer is not assigned one!\n");
 		if (!buffer_busy(page->buffers))
-			try_to_free_buffers(page->buffers->b_page, __GFP_FAST);
-		free_suspend_cache_page();
-		unlock_page(page);
-	}
-	if(!Page_Uptodate(page)) {
-		abort_suspend("Page not up-to-date in rw_swap_page_nofree!\n");
-		return 1;
+			try_to_free_buffers(page, __GFP_FAST);
+		else
+			PRINTK(SUSPEND_VERBOSE, "\nBuffer still marked as busy!\n");
 	}
+	suspend_swap_bh = NULL;
+
+	if (rw == READ)
+		free_suspend_cache_page(cur.cache_page);
+	atomic_dec(&page->count);
+	atomic_dec(&page->count);
+
+	//PRINTK(SUSPEND_VERBOSE, "^%d^", nr_free_pages());
 	return 0;
 }
 
+static int rw_swap_page_nofree(int rw, swp_entry_t entry, union p_diskpage cur)
+{
+	/* Do both - effectively a synchronous write or read */
+	struct io_info io_info;
+	int sret, fret;
+	
+	io_info.buffer = cur;
+	io_info.cache_page = 0;
+	sret = start_rw_swap_page_nofree(rw, entry, io_info, 1);
+	fret = finish_rw_swap_page_nofree(rw, io_info);
+	return sret ? sret : fret;
+}
+
 static void mark_swapfiles(swp_entry_t prev, int mode)
 {
 	swp_entry_t entry;
 	union p_diskpage cur;
 
 	cur.address = copybuff;
-	/* XXX: this is dirty hack to get first page of swap file */
+	
 	entry = SWP_ENTRY(root_swap, 0);
-	if (RW_SWAP_PAGE_SYNC(READ, entry, cur)) {
+	if (rw_swap_page_nofree(READ, entry, cur)) {
+		PRINTK(SUSPEND_VERBOSE, "Failed to read suspend header");
+		check_shift_keys(1);
 		return;
 	}
 
 	switch(mode) {
 	case MARK_SWAP_RESUME:
-		if (!memcmp("SUSP1R",cur.pointer->swh.magic.magic,6))
+		if (!memcmp("1R",cur.pointer->swh.magic.magic,2))
 			memcpy(cur.pointer->swh.magic.magic,"SWAP-SPACE",10);
-		else if (!memcmp("SUSP2R",cur.pointer->swh.magic.magic,6))
+		else if (!memcmp("2R",cur.pointer->swh.magic.magic,2))
 			memcpy(cur.pointer->swh.magic.magic,"SWAPSPACE2",10);
 		else printk(name_resume "Unable to find suspended-data signature (%.10s - misspelled?\n", 
 			    cur.pointer->swh.magic.magic);
 		break;
 	case MARK_SWAP_RECOVER:
-		if (!memcmp("SUSP1R",cur.pointer->swh.magic.magic,6))
+		if (!memcmp("1R",cur.pointer->swh.magic.magic,2))
 			memcpy(cur.pointer->swh.magic.magic,"SWAP-SPACE",10);
-		else if (!memcmp("SUSP2R",cur.pointer->swh.magic.magic,6))
+		else if (!memcmp("2R",cur.pointer->swh.magic.magic,2))
 			memcpy(cur.pointer->swh.magic.magic,"SWAPSPACE2",10);
 	  	else if ((!memcmp("SWAP-SPACE",cur.pointer->swh.magic.magic,10))
 			 &&(!memcmp("SWAPSPACE2",cur.pointer->swh.magic.magic,10))) {
@@ -769,18 +799,24 @@
 		break;
 	case MARK_SWAP_SUSPEND:
 	  	if ((!memcmp("SWAP-SPACE",cur.pointer->swh.magic.magic,10)))
-		  	memcpy(cur.pointer->swh.magic.magic,"SUSP1R....",10);
+		  	memcpy(cur.pointer->swh.magic.magic,"1R",2);
 		else if ((!memcmp("SWAPSPACE2",cur.pointer->swh.magic.magic,10)))
-			memcpy(cur.pointer->swh.magic.magic,"SUSP2R....",10);
+			memcpy(cur.pointer->swh.magic.magic,"2R",2);
 		else {
 			abort_suspend("\nSwapspace is not swapspace (%.10s)\n", cur.pointer->swh.magic.magic);
 			return;
 		}
+
+		/* Put bdev of suspend header in last byte of swap header (unsigned short) */
+		{
+			kdev_t * header_ptr = (kdev_t *) (&(cur.pointer->swh.magic.magic[2]));
+			*header_ptr = swap_info[SWP_TYPE(prev)].swap_device;
+		}
 		cur.pointer->link.next = prev; /* prev is the first/last swap page of the resume area */
-		/* link.next lies *no more* in last 4 bytes of magic */
+		/* link.next lies in last 4 bytes of magic */
 		break;
 	}
-	RW_SWAP_PAGE_SYNC(WRITE, entry, cur);
+	rw_swap_page_nofree(WRITE, entry, cur);
 }
 
 static void read_swapfiles(void) /* This is called before saving image */
@@ -808,7 +844,6 @@
 					swapfile_used[i] = SWAPFILE_SUSPEND;
 					root_swap = i;
 				} else {
-					PRINTK(SUSPEND_DEBUG, name_suspend "device %s (%x != %x) ignored\n", swap_info[i].swap_file->d_name.name, swap_info[i].swap_device, resume_device);
 				  	swapfile_used[i] = SWAPFILE_IGNORED;
 				}
 			}
@@ -817,114 +852,148 @@
 	swap_list_unlock();
 }
 
-static void lock_swapdevices(void) /* This is called after saving image so modification
-				      will be lost after resume... and that's what we want. */
-{
-	int i;
-
-	swap_list_lock();
-	for(i = 0; i< MAX_SWAPFILES; i++)
-		if (swapfile_used[i] == SWAPFILE_IGNORED) {
-			swap_info[i].flags ^= 0xFF; /* we make the device unusable. A new call to
-						       lock_swapdevices can unlock the devices. */
-		}
-	swap_list_unlock();
-	beepOK;			/* beep at start/end of writing */
-}
-
-static int write_pageset(struct pbe **pagedir, int whichtowrite)
+static int write_pageset(struct pagedir * pagedir, int whichtowrite)
 {
-	int i, size;
+	int i, j = 0, size, ret, syncnow, result = 0;
 	swp_entry_t entry;
-	union p_diskpage cur;
+	struct io_info cur[NUM_SYNC_WRITES];
+	char * virt;
 
 #define abort_and_exit(f, a...) do { \
 			abort_suspend(f, ## a); \
+			result = -1; \
 			drivers_suspend(); \
-			return -1; \
+			goto write_pageset_free_buffers; \
 	} while(0)
 
 	drivers_unsuspend();
+	PRINTNOLOG(SUSPEND_VERBOSE, 1, "[%d free pages.]", nr_free_pages());
+
+	size = pagedir->pageset_size;
+
 	if (whichtowrite == 1) {
-		size = pageset1_size;
 		prepare_status("Writing suspend image (1)...");
 	} else {
-		size = pageset2_size;
 		prepare_status("Writing suspend image (2)...");
 	}	
-	cur.address = copybuff;
+	
+	for (i=0; i< NUM_SYNC_WRITES; i++) {
+		cur[i].buffer.address = get_zeroed_page(GFP_ATOMIC);
+		if (!cur[i].buffer.address) {
+			result = -1;
+			goto write_pageset_free_buffers;
+		}
+	}
 
 	for (i=0; i<size; i++) {
 		if (!(i%100))
 			PRINTK(SUSPEND_DEBUG, ".");
-		PRINTNOLOG(SUSPEND_VERBOSE,name_suspend "\rWriting data to swap (%d/%d pages).", i + 1, size);
+		PRINTNOLOG(SUSPEND_VERBOSE, 1, name_suspend "Writing pageset: %d/%d ", i + 1, size);
 		update_status((int) (i * 100 / size));
 
-		entry = get_swap_page();
-		if (!entry.val)
-			abort_and_exit("Not enough swapspace when writing data.\n");
-		if (swapfile_used[SWP_TYPE(entry)] != SWAPFILE_SUSPEND)
-			abort_and_exit("Page %d: not enough swapspace on suspend device.\n", i);
+		entry = PAGEDIR_ENTRY(pagedir,i)->swap_address;
 	    
 		if (whichtowrite == 1) {
-			PAGEDIR_ENTRY(pagedir,i)->swap_address1 = entry;
 #ifdef DEBUG_DEFAULT
-			if ((i < PRINTTHRESH) || ((i + PRINTTHRESH) > size))
-				PRINTNOLOG(SUSPEND_VERBOSE, "!%d! [1]%lx -> [2]%lx -> [sw]%lx.\n", i, PAGEDIR_ENTRY(pagedir, i)->address1, PAGEDIR_ENTRY(pagedir, i)->address2, entry.val);
+			//if ((i < PRINTTHRESH) || ((i + PRINTTHRESH) > size))
+				PRINTNOLOG(SUSPEND_VERBOSE, 0, "!%d! [1]%lx -> [2]%lx -> %lx -> [sw]%lx.", 
+						i,
+						(unsigned long) page_address(PAGEDIR_ENTRY(pagedir, i)->origaddress), 
+						(unsigned long) page_address(PAGEDIR_ENTRY(pagedir, i)->address), 
+						cur[i%NUM_SYNC_WRITES].buffer.address,
+						entry.val);
 #endif
+			copy_page((char *) cur[i%NUM_SYNC_WRITES].buffer.address, (char *) page_address(PAGEDIR_ENTRY(pagedir, i)->address));
 		} else {
-			PAGEDIR_ENTRY(pagedir, i)->swap_address2 = entry;
 #ifdef DEBUG_DEFAULT
-			if ((i < PRINTTHRESH) || ((i + PRINTTHRESH) > size))
-				PRINTNOLOG(SUSPEND_VERBOSE, "!%d! [2]%lx -> [sw]%lx\n", i, PAGEDIR_ENTRY(pagedir, i)->address2, entry.val);
-#endif
-		}
-
-		copy_page((char *) copybuff, (char *) PAGEDIR_ENTRY(pagedir, i)->address2);
-		__flush_tlb();
+			//if ((i < PRINTTHRESH) || ((i + PRINTTHRESH) > size))
+				PRINTNOLOG(SUSPEND_VERBOSE, 0, "!%d! %s[2]%lx -> %lx -> [sw]%lx",
+						i,
+						PageHighMem(PAGEDIR_ENTRY(pagedir, i)->origaddress) ? "[HighMem]" : "",
+						PageHighMem(PAGEDIR_ENTRY(pagedir, i)->origaddress) ? (unsigned long) PAGEDIR_ENTRY(pagedir, i)->origaddress : (unsigned long) page_address(PAGEDIR_ENTRY(pagedir, i)->origaddress),
+						cur[i%NUM_SYNC_WRITES].buffer.address,
+						entry.val);
+#endif
+			/* Don't waste time checking PageHighMem - kmap/unmap do the right thing */
+			virt = kmap(PAGEDIR_ENTRY(pagedir, i)->origaddress);
+			copy_page((char *) cur[i%NUM_SYNC_WRITES].buffer.address, virt);
+			kunmap(PAGEDIR_ENTRY(pagedir, i)->origaddress);
+		}
+
+		syncnow = ((!((i + 1)%NUM_SYNC_WRITES)) | ((i+1) == size));
+		ret = start_rw_swap_page_nofree(WRITE, entry, cur[i%NUM_SYNC_WRITES], syncnow);
+		if (ret)
+			abort_and_exit("Failed writing page to swap. Error number was %d.\n", ret);
+		
+		check_shift_keys(0);
+		if (SUSPEND_ABORTING)
+			abort_and_exit("Aborting as requested.\n");
 
-		if (RW_SWAP_PAGE_SYNC(WRITE, entry, cur))
-			abort_and_exit("Failed writing page to swap.\n");
+		if (!((i + 1)%NUM_SYNC_WRITES))
+			for (j=0; j< NUM_SYNC_WRITES; j++)
+				finish_rw_swap_page_nofree(WRITE, cur[j]);
 	}
+
+	for (j=0; j < (i%NUM_SYNC_WRITES); j++)
+		finish_rw_swap_page_nofree(WRITE, cur[j]);
+
 	PRINTK(SUSPEND_DEBUG, "|\n");
+	PRINTNOLOG(SUSPEND_VERBOSE, 1, "[%d free pages.]\n", nr_free_pages());
+write_pageset_free_buffers:
+	for (i = 0; i< NUM_SYNC_WRITES; i++)
+		if (cur[i].buffer.address) {
+			struct page * page = virt_to_page(cur[i].buffer.address);
+			/* PageCount seems to get zeroed sometimes. Make sure free_pages doesn't bug. */
+			if (!page_count(page)) {
+				PRINTK(SUSPEND_VERBOSE, " Page count for buffer %d was zero.\n", i);
+				set_page_count(page, 1);	/* We seek to ensure the page is never freed here */
+			}
+			free_pages(cur[i].buffer.address, 0);
+		}
+
 	drivers_suspend();
-	return 0;
+	return result;
 }
 
-static int  write_pagedir_and_suspend_header(struct pbe **pagedir)
+static int  write_pagedir_and_suspend_header(void)
 {
-	int i, len, *ptr;
+	int i, j, len, *ptr;
 	swp_entry_t entry, prev = { 0 };
 	union p_diskpage cur,  buffer;
+	int total = pagedir1.pageset_size+pagedir2.pageset_size+pagedir1.pagedir_size+2;
+	int progress = total-1;
 
+#undef abort_and_exit
 #define abort_and_exit(f, a...) do { \
 			abort_suspend(f, ## a); \
-			drivers_suspend(); \
-			return -1; \
+			goto write_pagedir_abort; \
 	} while(0)
 	
 	buffer.address = copybuff;
 
 	drivers_unsuspend();
-	PRINTK(SUSPEND_DEBUG, name_suspend "Writing pagedir (%d pages)\n", pagedir_size);
-	for (i=0; i<pagedir_size; i++) {
-		cur.pointer = (union diskpage *) pagedir[i];
+	PRINTK(SUSPEND_DEBUG, name_suspend "Writing pagedir (%d pages)\n", pagedir1.pagedir_size);
+
+	for (i=0; i<pagedir1.pagedir_size; i++) {
+		cur.pointer = (union diskpage *) pagedir1.data[i];
 		PRINTK(SUSPEND_DEBUG, "."); 
-		update_status((int) ((i+pageset1_size+pageset2_size) * 100 / (pageset1_size+pageset2_size+pagedir_size+2)));
+		update_status((int) ((progress + i) * 100 / total));
 		entry = get_swap_page();
 		if (!entry.val)
 			abort_and_exit("Not enough swapspace when writing pgdir\n");		
-		if (swapfile_used[SWP_TYPE(entry)] != SWAPFILE_SUSPEND)
-			abort_and_exit("Not enough swapspace for pagedir on suspend device\n");
 		if (sizeof(swp_entry_t) != sizeof(long))
 			abort_and_exit("Size of swp_entry_t != size of long in write_pagedir_and_suspend_header!\n");
 		if (PAGE_SIZE % sizeof(struct pbe))
 			abort_and_exit("Page size (%ld) mod size of struct pbe (%d) is not 0 (%ld) in write_pagedir_and_suspend_header!\n", PAGE_SIZE, sizeof(struct pbe), PAGE_SIZE % sizeof(struct pbe));
-		cur.pointer->link.next = prev;				
+		check_shift_keys(0);
+		if (SUSPEND_ABORTING)
+			abort_and_exit("Aborting as requested.\n");
+		cur.pointer->link.next = prev;
 		if (rw_swap_page_nofree(WRITE, entry, cur))
 			abort_and_exit("Failed to write pagedir page.\n");
 		prev = entry;
 	}
+
 	PRINTK(SUSPEND_DEBUG, "H");
 #ifdef SOFTWARE_SUSPEND_MTRR
 	ptr = mtrr_suspend();
@@ -941,12 +1010,9 @@
 	if (sizeof(union diskpage) != PAGE_SIZE)
 		abort_and_exit("Size of a disk page is not PAGE_SIZE!\n");
 	entry = get_swap_page();
-	if (!entry.val)
-		abort_and_exit("Not enough swapspace when writing header!\n");
-	if (swapfile_used[SWP_TYPE(entry)] != SWAPFILE_SUSPEND)
-		abort_and_exit("Not enough swapspace for header on suspend device!\n" );
-
+	
 	cur = buffer;
+
 	if (fill_suspend_header(&(cur.pointer->sh)))
 		abort_and_exit("Out of memory while writing header!\n");
 	if(ptr)
@@ -963,7 +1029,8 @@
 	prev = entry;
 
 	PRINTK(SUSPEND_DEBUG, "S");
-	update_status((int) ((pagedir_size+pageset1_size+1) * 100 / (pageset1_size+pagedir_size+2)));
+	update_status((int) ((total - 1) * 100 / total));
+	read_swapfiles();
 	mark_swapfiles(prev, MARK_SWAP_SUSPEND);
 	if (SUSPEND_ABORTING)
 		return -1;
@@ -973,44 +1040,106 @@
 	MDELAY(1000);
 	drivers_suspend();
 	return 0;
+
+write_pagedir_abort:
+	swap_free(entry);
+	swap_free(prev);
+	printk("Attempting to free %d pagedir entries in swap.\n", i);
+	for (j=i-1; j >= 0; j--) {
+		swap_free(((union diskpage *) pagedir1.data[j])->link.next);
+	}
+	drivers_suspend();
+	return -1;
 }
 
-static pageset_sizes_t count_data_pages(struct pbe **pagedir_p)
+static void generate_free_page_map(void) 
 {
-	int chunk_size, excess_pages = 0, loop;
-	int assignedstart1 = -1, assignedend1 = -1;
-	int assignedstart2 = -1, assignedend2 = -1;
+	int i, loop;
+	struct page * page;
+	pg_data_t *pgdat = pgdat_list;
+	unsigned type;
+	unsigned long flags;
+
+	for(i=0; i < max_mapnr; i++)
+		ClearPageFree(mem_map+i);
+	
+	for (type=0;type < MAX_NR_ZONES; type++) {
+		zone_t *zone = pgdat->node_zones + type;
+		int order = MAX_ORDER - 1;
+		free_area_t *area;
+		struct list_head *head, *curr;
+		spin_lock_irqsave(&zone->lock, flags);
+		do {
+			int first_entry = 1;
+			area = zone->free_area + order;
+			head = &area->free_list;
+			curr = head;
+
+			for(;;) {
+				if(!curr) {
+//					printk(KERN_ERR name_suspend "FIXME: this should not happen but it does!!!");
+					break;
+				}
+				if (first_entry) 
+					first_entry--;
+				else {
+					page = list_entry(curr, struct page, list);
+					for(loop=0; loop < (1 << order); loop++)
+						SetPageFree(page+loop);
+				}
+
+				curr = curr->next;
+				if (curr == head)
+					break; 				
+			}
+		} while(order--);
+		spin_unlock_irqrestore(&zone->lock, flags);
+	}
+}
+
+static int is_head_of_free_region(struct page * page)
+{
+	struct page * posn = page;
+
+	while (((posn-mem_map) < max_mapnr) && (PageFree(posn))) 
+		posn++;
+	return (posn - page);
+}
+
+static pageset_sizes_t count_data_pages(int whichpagedir)
+{
+	int chunk_size, loop, numnosave = 0;
+
 	pageset_sizes_t result;
 	result.size1 = 0;
 	result.size2 = 0;
-	
+	result.size2low = 0;
+
 	generate_free_page_map();
 	if (max_mapnr != num_physpages) {
 		abort_suspend("mapnr is not expected");
 		result.size1 = -1;
 		result.size2 = -1;
+		result.size2low = -1;
 		return result;
 	}
 	for (loop = 0; loop < max_mapnr; loop++) {
-		if (PageHighMem(mem_map+loop)) {
-			abort_suspend("Swsusp not supported on highmem boxes. Send 1GB of RAM to <pavel@ucw.cz> and try again ;-).");
-			result.size1 = -1;
-			result.size2 = -1;
-			return result;
-		}
 		if (!PageReserved(mem_map+loop)) {
-			if (PageNosave(mem_map+loop))
+			if (PageNosave(mem_map+loop)) {
+				numnosave++;
 				continue;
+			}
 
 			if ((chunk_size=is_head_of_free_region(mem_map+loop))!=0) {
 				loop += chunk_size - 1;
 				continue;
 			}
-		} else if (PageReserved(mem_map+loop)) {
+		} else {
 			if (PageNosave(mem_map+loop)) {
-				abort_suspend("Reserved page marked nosave!\n");
+				abort_suspend("Reserved page marked nosave! (%lx)\n", ADDRESS(loop));
 				result.size1 = 0;
 				result.size2 = 0;
+				result.size2low = 0;
 				return result;
 			}
 			/*
@@ -1019,130 +1148,225 @@
 			if (ADDRESS(loop) >= (unsigned long)
 				&__nosave_begin && ADDRESS(loop) < 
 				(unsigned long)&__nosave_end) {
+				numnosave++;
 				continue;
 			}
 			/* Hmm, perhaps copying all reserved pages is not too healthy as they may contain 
 			   critical bios data? */
 
-		} else BUG();
+		};
 
-		if (pagedir_p) {
-			if (!PageDontcopy(mem_map+loop)) {
-				PAGEDIR_ENTRY(pagedir_p, result.size1)->address1 = ADDRESS(loop);
-				if (assignedstart1 == -1) {
-					assignedstart1 = result.size1;
-				}
-				assignedend1 = result.size1;
-				if (result.size1 > pagedir_capacity) {
-					PRINTK(SUSPEND_VERBOSE, "@%d@\n", result.size1);
-					excess_pages++;
-					continue;
-				}
+		if (whichpagedir) {
+			int usepagedir2 = (PageHighMem(mem_map+loop) | PagePageset2(mem_map+loop));
+			int * curentry;
+			struct pagedir * pagedir;
+
+			if (usepagedir2) {
+				pagedir = &pagedir2;
+				curentry = &result.size2;
 			} else {
-				PAGEDIR_ENTRY(pagedir_p,result.size2)->address2 = ADDRESS(loop);
-				if (assignedstart2 == -1) {
-					assignedstart2 = result.size2;
+				pagedir = &pagedir1;
+				curentry = &result.size1;
+			}
+
+			if (*curentry <= pagedir->pageset_size) {
+				PAGEDIR_ENTRY(pagedir, *curentry)->origaddress = mem_map+loop;
+				if (usepagedir2) {
+					PAGEDIR_ENTRY(pagedir, *curentry)->address = mem_map+loop;
 				}
-				assignedend2 = result.size2;
 			}
 		}
-		
-		if (PageDontcopy(mem_map+loop))
+
+		if (PagePageset2(mem_map+loop)) {
 			result.size2++;
-		else
+			if (!PageHighMem(mem_map+loop))
+				result.size2low++;
+		} else
 			result.size1++;
 	}
-	if (assignedstart1 != -1) {
-		PRINTK(SUSPEND_VERBOSE,"Address1 set for range %d-%d.\n", assignedstart1, assignedend1);
-	}
-	if (assignedstart2 != -1) {
-		PRINTK(SUSPEND_VERBOSE,"Address2 set for range %d-%d.\n", assignedstart2, assignedend2);
+	
+	/*
+	 * PRINTK(SUSPEND_VERBOSE, "\nResults: %d and %d(%d low). %d marked Nosave\n", 
+	 *		result.size1, result.size2, result.size2low, numnosave);
+	 */
+	if (whichpagedir) {
+		PRINTNOLOG(SUSPEND_VERBOSE, 0, "\n");
+		if (result.size1 > pagedir1.pageset_size)
+			abort_suspend("%d more pages to be copied than were allowed for! Pagedir1 capacity is %d\n", \
+					result.size1 - pagedir1.pageset_size, \
+					pagedir1.pageset_size);
+		if (result.size2 > pagedir2.pageset_size)
+			abort_suspend("%d more pages to be copied than were allowed for! Pagedir2 capacity is %d\n", \
+					result.size2 - pagedir2.pageset_size, \
+					pagedir2.pageset_size);
 	}
-	if (excess_pages)
-		abort_suspend("%d more pages to be copied than were allowed for! Pageset1 size is %d\n", \
-				excess_pages, \
-				result.size1);
 	return result;
 }
 
-static void copy_pageset1(struct pbe **pagedir_p)
+static void copy_pageset1(void)
 {
 	int i;
-	int bugstart = 0, bugend = 0;
-	int progress_max = 0;
 
-	if (pagedir_capacity < pageset1_size) {
-		printk("Pagedir capacity (%d) is less than Pageset1 size! (%d).\n", pagedir_capacity, pageset1_size);
-		return;
-	}
-
-	if (pageset1_size > pageset2_size)
-		progress_max = pageset2_size + ((pageset1_size - pageset2_size) / 8);
-	else
-		progress_max = pageset2_size;
-	
 	prepare_status("Copying pages...");
 	for (i = 0; i < pageset1_size; i++) {
-		if (i < pageset2_size)
-			update_status((int) (i * 100 / progress_max));
-		else 
-			update_status((int) ((pageset2_size + (i - pageset2_size) / 8) * 100 / progress_max));
-		if (PAGEDIR_ENTRY(pagedir_p,i)->address2) {
+		update_status((int) (i * 100 / pageset1_size));
 #ifdef DEBUG_DEFAULT
-			if ((i < PRINTTHRESH) || ((i + PRINTTHRESH) > pageset1_size))  {
-				PRINTNOLOG(SUSPEND_VERBOSE, "!%d! [1]%lx -> [2]%lx.\n", i, PAGEDIR_ENTRY(pagedir_p,i)->address1, PAGEDIR_ENTRY(pagedir_p,i)->address2);
-			}
+		//if ((i < PRINTTHRESH) || ((i + PRINTTHRESH) > pagedir1.pageset_size))
+			PRINTNOLOG(SUSPEND_VERBOSE, 1, "!%d! ", i);
+			PRINTNOLOG(SUSPEND_VERBOSE, 0, "[1]%lx ->", 
+					(unsigned long) page_address(PAGEDIR_ENTRY(&pagedir1,i)->origaddress)); 
+			PRINTNOLOG(SUSPEND_VERBOSE, 0, "[2]%lx.", 
+					(unsigned long) page_address(PAGEDIR_ENTRY(&pagedir1,i)->address));
 #endif
-			copy_page((char *) PAGEDIR_ENTRY(pagedir_p,i)->address2, (char *) PAGEDIR_ENTRY(pagedir_p,i)->address1);
-			__flush_tlb();
-		} else {
-			if (bugstart && !bugend)
-				bugend = i;
-			else if (!bugstart) {
-				bugstart = i;
-				abort_suspend("Address2 pointers NULL!\n");
-			}
-		}
+		copy_page((char *) page_address(PAGEDIR_ENTRY(&pagedir1,i)->address), 
+				(char *) page_address(PAGEDIR_ENTRY(&pagedir1,i)->origaddress));
 	}
-	if (bugstart && !bugend)
-		bugend = i;
-	if (bugstart)
-		printk("Address2 not set for range %d-%d!\n", bugstart, bugend);
+	PRINTNOLOG(SUSPEND_VERBOSE, 0, "\n");
 }
 
-static void free_suspend_pagedir(struct pbe **this_pagedir)
+static void free_pagedir(struct pagedir * p)
 {
 	int i;
-	int rangestart = -1, rangeend = -1;
-	struct pbe **p = this_pagedir;
 
-	if (pagedir_size == 0)
+	if (p->pagedir_size == 0)
 		return;
 
-	for(i = 0; i < pageset1_size; i++) {
-		if (i >= pageset2_size) {
-			if (PAGEDIR_ENTRY(p,i)->address2) {
-				if (rangestart > -1) {
-					printk("Pagedir entry %d-%d address2 not set!\n", rangestart, rangeend);
-					rangestart = -1;
-				}
-				free_page(PAGEDIR_ENTRY(p,i)->address2);
-			} else {
-				if (rangestart == -1)
-					rangestart = i;
-				rangeend = i;
+	/* Entries can validly be NULL. We might be called if creating a pagedir fails to allocate a page */
+	for(i = p->alloc_from; i < p->pageset_size; i++) 
+		if (PAGEDIR_ENTRY(p,i)->address) {
+			ClearPageNosave(PAGEDIR_ENTRY(p,i)->address);
+			free_page((unsigned long) page_address(PAGEDIR_ENTRY(p,i)->address));
+		}
+	
+	PRINTNOLOG(SUSPEND_VERBOSE, 1, "Freeing pagedir: Swap addresses %lx to %lx.",
+			PAGEDIR_ENTRY(p, 0)->swap_address.val,
+			PAGEDIR_ENTRY(p, p->pageset_size-1)->swap_address.val);
+	for(i = 0; i < p->pageset_size; i++) {
+		swp_entry_t entry = PAGEDIR_ENTRY(p,i)->swap_address;
+		if (entry.val) {
+			PRINTNOLOG(SUSPEND_VERBOSE, 1, "(Free %d %p:%lx)", i,
+				PAGEDIR_ENTRY(p, i),
+				entry.val);
+			swap_free(entry);
+			PAGEDIR_ENTRY(p,i)->swap_address.val = 0;
+		}
+	}
+		
+	for(i = 0; i < p->pagedir_size; i++)
+		free_page((unsigned long) *(p->data+i));
+
+	free_page((unsigned long) p->data);
+	p->pagedir_size = 0;
+	p->data = 0;
+}
+
+static int create_pagedir(struct pagedir * p, int pageset_size, int alloc_from)
+{
+	int pagedir_size = calcpagedirsize(pageset_size);
+	int startfree = nr_free_pages(), endfree;
+
+#undef abort_and_exit
+#define abort_and_exit(f, a...) do { \
+			abort_suspend(f, ## a); \
+			goto create_pagedir_abort; \
+	} while(0)
+
+	if (pageset_size == 0) {
+		p->pagedir_size = 0;
+		p->pageset_size = pageset_size;
+		p->alloc_from = alloc_from;
+		return 0;
+	}
+
+	PRINTK(SUSPEND_VERBOSE, "   At the start of create_pagedir, we have %d pages.\n", nr_free_pages());
+
+	p->data = (struct pbe **)get_zeroed_page(GFP_ATOMIC);
+	//PRINTK(SUSPEND_VERBOSE,"   Allocated pagedir header at %p.\n", p->data);
+	
+	if (!p->data) {
+		abort_suspend("Failed to allocate a pagedir!\n");
+		return 1;
+	}
+	
+	/* Ensure saved in pageset 1 */
+	ClearPageNosave(virt_to_page(p->data));
+	ClearPagePageset2(virt_to_page(p->data));
+
+	{
+		int i;
+		for (i = 0; i < pagedir_size; i++) {
+			p->data[i] = (struct pbe *)get_zeroed_page(GFP_ATOMIC);
+			if (!p->data[i]) {
+				int j;
+				for (j = 0; j < i; j++)
+					free_page((unsigned long) p->data[j]);
+				free_page((unsigned long) p->data);
+				abort_suspend("Unable to allocate a pagedir.\n");
+				return 1;
 			}
+			ClearPageNosave(virt_to_page(p->data[i]));
+			ClearPagePageset2(virt_to_page(p->data[i]));
 		}
 	}
+	
+	{
+		int i;
 		
-	if (rangestart > -1)
-		printk("Pagedir entry %d-%d address2 not set!\n", rangestart, pageset1_size - 1);
+		for(i=0; i < pageset_size; i++) {
+			swp_entry_t entry;
+			PAGEDIR_ENTRY(p,i)->origaddress = 0;
+			PAGEDIR_ENTRY(p,i)->address = 0;
+			PRINTNOLOG(SUSPEND_VERBOSE, 1, "");
+			entry = get_swap_page();
+			if (!entry.val)
+				abort_and_exit("Not enough swapspace (this shouldn't happen!)\n");
+			PAGEDIR_ENTRY(p,i)->swap_address = entry;
+			PRINTNOLOG(SUSPEND_VERBOSE, 0, "(Get %d %p -> %lx)", i, 
+				PAGEDIR_ENTRY(p, i),
+				PAGEDIR_ENTRY(p, i)->swap_address.val);
+		}
+		PRINTNOLOG(SUSPEND_VERBOSE, 0, "\n");
+	}
+	
+	{
+		int i, numnosaveallocated=0;
+	
+		for(i=alloc_from; i < pageset_size; i++) {
+			PAGEDIR_ENTRY(p,i)->address = virt_to_page(get_zeroed_page(GFP_ATOMIC));
+			if (!PAGEDIR_ENTRY(p,i)->address) {
+				abort_and_exit("Unable to allocate pages for pagedir!\n");
+				return 1;
+			}
+			SetPageNosave(PAGEDIR_ENTRY(p,i)->address);
+			numnosaveallocated++;
+		}
+		if (pageset_size > alloc_from) {
+			PRINTK(SUSPEND_VERBOSE,"   Allocated memory for pages from %d-%d.\n", alloc_from, pageset_size - 1);
+		}
+	}
 
-	for(i = 0; i < pagedir_size; i++)
-		free_page((unsigned long) *(this_pagedir+i));
+	p->pagedir_size = calcpagedirsize(pageset_size);
+	p->pageset_size = pageset_size;
+	p->alloc_from = alloc_from;
+	endfree = nr_free_pages();
+
+	PRINTK(SUSPEND_VERBOSE,"   Created pagedir of %d pages at %p to store %d pages. %d free pages left.\n",
+			p->pagedir_size,
+			p->data,
+			p->pageset_size,
+			endfree);
+	
+	PRINTK(SUSPEND_VERBOSE,"   Check: %d + 1 + %d + %d = %d %s\n",
+			endfree, pagedir_size, pageset_size - alloc_from, 
+			endfree + pagedir_size + pageset_size - alloc_from + 1,
+			(endfree + pagedir_size + pageset_size - alloc_from + 1 == startfree) ?
+			"Ok" :
+			" ***** DOESN'T MATCH ***** ");
 
-	free_page((unsigned long) this_pagedir);
-	this_pagedir = NULL;
+	return 0;
+create_pagedir_abort:
+	free_pagedir(p);
+	return 0;
 }
 
 static int prepare_suspend_console(void)
@@ -1150,7 +1374,7 @@
 	if ((!swsusp_state[2]) && (!swsusp_state[3]))  /* No output should be produced. 
 							  It would be good if we could tell X to 
 							  refresh the display at resume if it's 
-							  running on the  current VT. (In a script? */
+							  running on the  current VT. (In a script?) */
 		return 0;
 
 	orig_loglevel = console_loglevel;
@@ -1198,29 +1422,9 @@
 		thaw_processes();
 		return 1;
 	}
-	sys_sync();
 	return 0;
 }
 
-#define _calcpagedirsize(size) (SUSPEND_PD_PAGES(size))
-static int calcpagedirsize(int size1, int size2)
-{
-	/*
-	 * Size1 should not already include the pagedir!
-	 * 
-	 * If adding the pagedir will mean we need to store more pages that fit,
-	 * we need to expand the size until it does fit (the pagedir itself might
-	 * require more than one pagedir to store, hence the while).
-	 */
-	int result;
-	int max;
-	max = MAX(size1, size2);
-	result = _calcpagedirsize(max);
-	while (_calcpagedirsize(max + result) > result)
-		result++;
-	return result;
-}
-
 /*
  * We need to eat memory until we can:
  * 1. Perform the save without changing anything (RAM_NEEDED < max_mapnr)
@@ -1235,19 +1439,24 @@
  *
  *    I have seen simply waiting for the user to press SHIFT increase the
  *    number of pages to save by one (logs?), so tests are <, not <= to allow
- *    one extra page. (Final test in save_suspend_image doesn't use EATEN_ENOUGH_MEMORY)
+ *    one extra page. (Final test in save_image doesn't use EATEN_ENOUGH_MEMORY)
  *
- *    RAM_TO_RESUME includes +8 to allow for extra pages allocated during read_primary_suspend_image
+ *    RAM_TO_RESUME includes +8 to allow for extra pages allocated during read_primary_suspend_image.
+ *    pageset1_size includes space for both pagedirs.
  */
 
-#define PAGEDIR_SIZE (calcpagedirsize(pageset1_size, pageset2_size))
-#define SWAP_NEEDED (pageset1_size + pageset2_size + 2 * (PAGEDIR_SIZE + 1))
-#define RAM_TO_SUSPEND (SWAP_NEEDED + - PAGEDIR_SIZE - 1 + MAX((pageset1_size - pageset2_size), 0) + PAGES_FOR_IO)
-#define RAM_TO_RESUME  ((pageset1_size + PAGEDIR_SIZE + 1) * 2 + PAGES_FOR_IO + max_mapnr - orig_mem_free + 8)
-#define EATEN_ENOUGH_MEMORY() ((RAM_TO_SUSPEND < max_mapnr) && \
-		(swapinfo.freeswap > SWAP_NEEDED) && \
-		(RAM_TO_RESUME < max_mapnr ) && \
-		((!swsusp_state[4]) || (SWAP_NEEDED < (swsusp_state[4] << 8))))
+#define SWAP_NEEDED (pageset1_size + pageset2_size + pagedir1_size + 1)
+#define RAM_TO_SUSPEND (pagedir1_size + 1 + pagedir2_size + 1 + MAX((pageset1_size - pageset2_sizelow), 0) + PAGES_FOR_IO)
+#define RAM_TO_RESUME  ((pageset1_size + pagedir1_size + 1) * 2 + 8 + PAGES_FOR_IO + (long) max_mapnr - orig_mem_free)
+
+static inline int amount_needed(void)
+{
+	return (MAX(MAX(MAX(RAM_TO_SUSPEND, RAM_TO_RESUME) - max_mapnr, 
+			       SWAP_NEEDED -  swapinfo.freeswap), 
+			   ((swsusp_state[4]) ? (SWAP_NEEDED - (swsusp_state[4] << 8)) : 0)));
+}
+
+#define EATEN_ENOUGH_MEMORY() (amount_needed() < 1)
 
 static void display_debug_info(void)
 {
@@ -1255,7 +1464,9 @@
 	printk("\n\nPlease include the following information in any bug report:\n");
 	printk("- SWSUSP Version : %s\n", swsusp_version);
 	printk("- Swap available : %ld (amount unused when preparing image).\n", swapforimage);
-	printk("- Pageset sizes  : %d and %d. (Pagedir size: %d)\n", pageset1_size, pageset2_size, calcpagedirsize(pageset1_size, pageset2_size));
+	printk("- Pageset sizes  : %d and %d (%d low). (Pagedir sizes: %d and %d)\n", pageset1_size, pageset2_size,
+			pageset2_sizelow,
+			calcpagedirsize(pageset1_size), calcpagedirsize(pageset2_size));
 	printk("- Expected sizes : %d and %d.\n", expected_size1, expected_size2);
 	printk("- Parameters     : %d %d %d %d %d\n", swsusp_state[0], swsusp_state[1], swsusp_state[2], swsusp_state[3], swsusp_state[4]);
 	printk("- Calculations   : Image size: %d. Ram to suspend: %d. To resume: %ld.\n", SWAP_NEEDED, RAM_TO_SUSPEND, RAM_TO_RESUME);
@@ -1267,12 +1478,14 @@
  */
 static void display_stats(unsigned int eaten)
 { 
-	PRINTK(SUSPEND_VERBOSE, "Free:%d. Sets:%d(%d),%d. PD:%d. Swap:%d/%lu. RAM to suspend:%d; resume:%lu. Limits:%lu,%d\n", 
+	PRINTNOLOG(SUSPEND_VERBOSE, 1, "Free:%d(%d). Sets:%d,%d (%d). PD:(%d,%d). Swap:%d/%lu. RAM s:%d; r:%lu. Limits:%lu,%d", 
 			nr_free_pages() + eaten, 
+			nr_free_pages(),
 			pageset1_size, 
-			pageset1_size + 1 + PAGEDIR_SIZE,
 			pageset2_size,
-			PAGEDIR_SIZE,
+			pageset2_sizelow,
+			pagedir1_size,
+			pagedir2_size,
 			SWAP_NEEDED,
 			swapinfo.freeswap,
 			RAM_TO_SUSPEND,
@@ -1281,90 +1494,168 @@
 			swsusp_state[4] << 8); 
 }
 
-/*
- * Eaten is the number of pages which have been eaten.
- * Pagedirincluded is the number of pages which have been allocated for the pagedir.
- */
-static void recalculate_stats(int pagedirincluded) 
-{ 
-	pageset_sizes_t result;
-	markpagesforpageset2();  /* Need to call this before getting pageset1_size! */
-	result = count_data_pages(NULL);
-	pageset1_size = result.size1 - pagedirincluded;
-	pageset2_size = result.size2;
-	si_swapinfo(&swapinfo);	/* FIXME: si_swapinfo(&i) returns all swap devices information.*/ 
+/*
+ * Eaten is the number of pages which have been eaten.
+ * Pagedirincluded is the number of pages which have been allocated for the pagedir.
+ */
+static void recalculate_stats(int amount_eaten) 
+{ 
+	pageset_sizes_t result;
+	markpagesforpageset2();  /* Need to call this before getting pageset1_size! */
+	result = count_data_pages(0);	/* Only counts because of zero parameter */
+	//PRINTK(SUSPEND_VERBOSE, "Recalculate stats: PD2: %d->%d ", result.size2, calcpagedirsize(result.size2));
+	pageset2_sizelow = result.size2low;
+	pageset2_size = result.size2;
+	pagedir2_size = calcpagedirsize(pageset2_size);
+	//PRINTK(SUSPEND_VERBOSE, "PD1: %d->%d ", result.size1, calcpagedirsize(result.size1));
+	pageset1_size = result.size1 + pagedir2_size + ((pageset2_size > 0) ? 2 : 1);
+	pagedir1_size = calcpagedirsize(pageset1_size + pagedir1_size);
+	pageset1_size += pagedir1_size;
+	/*
+	PRINTK(SUSPEND_VERBOSE, "Pageset1 Total %d + %d + %d + 1 + %d = %d.\n",
+			result.size1,
+			pagedir1_size,
+			pagedir2_size,
+			(pageset2_size > 0) ? 2 : 1,
+			pageset1_size);
+	*/
+	si_swapinfo(&swapinfo);	/* FIXME: si_swapinfo(&i) returns all swap devices information.*/ 
+}
+
+struct eaten_memory_t
+{
+	void * prev;
+	int order; /* Order of _this_ allocation */
+};
+
+struct eaten_memory_t *eaten_memory = NULL;
+
+static int grab_free_memory(void)
+{
+	int order, k, amount_eaten = 0;
+	struct eaten_memory_t *prev = eaten_memory;
+
+	/*
+	 * First, quickly eat all memory that's already free.
+	 */
+	
+	for (order = MAX_ORDER - 1; order > -1; order--) {
+		eaten_memory = (struct eaten_memory_t *) __get_free_pages(__GFP_FAST, order);
+		while (eaten_memory) {
+			struct page * page = virt_to_page(eaten_memory);
+			eaten_memory->prev = prev;
+			eaten_memory->order = order;
+			prev = eaten_memory;
+			amount_eaten += (1 << order);
+			for (k=0; k < (1 << order); k++)
+				SetPageNosave(page + k);
+			eaten_memory = (struct eaten_memory_t *) __get_free_pages(__GFP_FAST, order);
+		}
+	}
+
+	eaten_memory = prev;
+	return amount_eaten;
+}
+
+static void free_grabbed_memory(void)
+{
+	struct eaten_memory_t *prev = NULL;
+	int j, num_freed = 0;
+
+	/* Free all eaten pages immediately */
+	while(eaten_memory) {
+		struct page * page = virt_to_page(eaten_memory);
+		prev = eaten_memory->prev;
+		for (j=0; j < (1 << (eaten_memory->order)); j++) {
+			ClearPageNosave(page + j);
+			num_freed++;
+		}
+		free_pages((unsigned long) eaten_memory, eaten_memory->order);
+		eaten_memory = prev;
+	}
+	PRINTK(SUSPEND_VERBOSE,"Freed %d pages.\n", num_freed);
+
+#ifdef DEBUG_DEFAULT
+	currentstage = 0;
+#endif
 }
 
 static void eat_memory(void)
 {
-	int m = 0, iterations = 0, memory_still_to_eat = 0, orig_memory_still_to_eat = 0;
+	int orig_memory_still_to_eat, last_amount_needed = 0, times_criteria_met = 0;
+	int amount_eaten = 0;
+
+#ifdef DEBUG_DEFAULT
+	currentstage = STAGE_EAT_MEMORY;
+#endif
 
 	PRINTK(SUSPEND_DEBUG, name_suspend "Eating pages\n");
 
 	recalculate_stats(0);
-	if (SUSPEND_ABORTING) 
-		return;
-
 	display_stats(0);
 
-	orig_memory_still_to_eat = MAX(MAX(RAM_TO_SUSPEND, RAM_TO_RESUME) - max_mapnr, swapinfo.freeswap - SWAP_NEEDED);
-	orig_memory_still_to_eat = MAX(orig_memory_still_to_eat, 
-			(!swsusp_state[4]) ? (SWAP_NEEDED - (swsusp_state[4] << 8)) : 0);
-
-	prepare_status("Freeing memory (1)...");
-	// Note that if we have enough swap and enough free memory, we may exit without
-	// eating anything.
-	while ((!EATEN_ENOUGH_MEMORY()) &&
-		(m = try_to_free_pages_zone(&contig_page_data.node_zones[ZONE_HIGHMEM], GFP_KSWAPD))) {
-		recalculate_stats(0);
-		display_stats(0);
-		iterations++; 
-		
-		memory_still_to_eat = MAX(MAX(RAM_TO_SUSPEND, RAM_TO_RESUME) - max_mapnr, swapinfo.freeswap - SWAP_NEEDED);
-		memory_still_to_eat = MAX(memory_still_to_eat, 
-			(!swsusp_state[4]) ? (SWAP_NEEDED - (swsusp_state[4] << 8)) : 0);
+	prepare_status("Freeing memory...");
+
+	orig_memory_still_to_eat = amount_needed();
+	last_amount_needed = orig_memory_still_to_eat + 100;	/* Ensure we don't count the first iteration */
+
+	/*
+	 * Note that if we have enough swap and enough free memory, we may exit without eating anything.
+	 * We give up when the last 10 iterations ate no extra pages because we're not going to get much
+	 * more anyway, but the few pages we get will take a lot of time.
+	 */
+	amount_eaten = grab_free_memory();
 
+	while ((!EATEN_ENOUGH_MEMORY()) && (!SUSPEND_ABORTING) && (times_criteria_met < 10)) {
 		if (orig_memory_still_to_eat)
-			update_status((orig_memory_still_to_eat - memory_still_to_eat) * 100 / orig_memory_still_to_eat);
+			update_status((orig_memory_still_to_eat - amount_needed()) * 100 / orig_memory_still_to_eat);
+		//printk("Amount needed:%d, Orig:%d, Percent: %d\n", amount_needed(), orig_memory_still_to_eat, (orig_memory_still_to_eat - amount_needed()) * 100 / orig_memory_still_to_eat);
+		
+		if ((last_amount_needed - amount_needed()) == 0)
+			times_criteria_met++;
+		else
+			times_criteria_met = 0;
+		last_amount_needed = amount_needed();
+		try_to_free_pages_swsusp(GFP_KSWAPD, amount_needed());
+		amount_eaten += grab_free_memory();
+		recalculate_stats(amount_eaten);
+		display_stats(amount_eaten);
+
+		check_shift_keys(0);
 
 		if (EATEN_ENOUGH_MEMORY())  { /* Make sure we really have eaten enough before we leave the loop */
 			do_suspend_sync();
 			recalculate_stats(0);
 		}
 	}
+
+	PRINTK(SUSPEND_VERBOSE, "\n");
+	
 	update_status(100);
-	PRINTK(SUSPEND_VERBOSE,"\n");
-	if ((!m) && (iterations)) {
+	if ((amount_needed() > 0))
 		PRINTK(SUSPEND_DEBUG, "\n--- UNABLE TO GET ANY MORE PAGES ---\n");
-		recalculate_stats(0);
-	}
 	
-	expected_size1 = pageset1_size + 1 + PAGEDIR_SIZE;
+	
+	expected_size1 = pageset1_size;
 	expected_size2 = pageset2_size;
 
-	WAIT_FOR_SHIFT;
-
+	check_shift_keys(1);
 }
 
 /*
  * Mark unshared pages in processes not needed for suspend as being able to be written
  * out in a separate pagedir. 
+ * HighMem pages are simply marked as pageset2. They won't be needed during suspend.
+ * Since we don't know which highmem pages are free, we can't return a count any more.
  */
 
 static void markpagesforpageset2(void)
 {
-	int usedinsuspend, pass;
-	struct vm_area_struct * vma;
-	struct task_struct *p;
-	struct mm_struct *mm;
 	struct list_head * entry;
 	int i;
 #ifdef DEBUG_DEFAULT
 	int oldstage;
-#endif
-	const int METHOD = 2;
 
-#ifdef DEBUG_DEFAULT
 	oldstage = currentstage;
 	currentstage = STAGE_MARK_PAGESET2;
 #endif
@@ -1377,98 +1668,35 @@
 		return;
 	}
 	
-	for (i = 0; i < max_mapnr; i++) {
-		ClearPageDontcopy(mem_map+i);
-		ClearPageKnowshared(mem_map+i);
-	}
+	for (i = 0; i < max_mapnr; i++)
+		ClearPagePageset2(mem_map+i);
 
-	if (SUSPEND_NOPAGESET2)
+	if (SUSPEND_NOPAGESET2) {
+#ifdef CONFIG_HIGHMEM
+		PRINTK(SUSPEND_DEBUG, "Ignoring request for no pageset2 - HighMem support is configured.\n");
+#else
+#ifdef DEBUG_DEFAULT
+		currentstage = oldstage;
+#endif
 		return;
-
-	read_lock(&tasklist_lock);
-	for (pass = 0; pass < 2; pass++) {
-		for_each_task(p) {
-			usedinsuspend = (!TASK_SUSPENDED(p));
-			if ((pass == 0 && usedinsuspend) || (pass == 1 && !usedinsuspend))
-				continue;
-
-			mm = p->active_mm;
-			if (!mm) continue;
-		
-			if (!mm->mmap) continue;
-		
-			down_read(&mm->mmap_sem);
-			
-			for (vma = mm->mmap; vma ; vma=vma->vm_next) {
-
-				pgd_t * pgd;
-				pmd_t * pmd;
-				pte_t * pte;
-				int mapnr;
-				unsigned long posn;
-				
-				if (!vma->vm_start) continue;
-			
-				for (posn = vma->vm_start; posn < vma->vm_end; posn+= PAGE_SIZE) {
-					pgd = pgd_offset(mm, posn);
-
-					if (!pgd) continue;
-					
-					pmd = pmd_offset(pgd, posn);
-
-					if (!pmd) continue;
-				
-					pte = pte_offset(pmd, posn);
-					mapnr = 0;
-					if (pte) 
-						mapnr = ((*pte).pte_low >> PAGE_SHIFT);
-
-					if ((mapnr) && (mapnr < max_mapnr)) {
-						// Set don't copy flag if all of the following criteria are met:
-						// 1. Not reserved and NoSave flag not set.
-						// 2. Not used in a process that runs during suspend (including swapper).
-						
-						if (PageKnowshared(mem_map+mapnr))
-							continue;
-					
-						if (PageDontcopy(mem_map+mapnr)) {
-							// If already marked, must be shared.
-							// method2: allow shared pages provided not usedinsuspend.
-							if ((METHOD == 1) || (usedinsuspend)) {
-								ClearPageDontcopy(mem_map+mapnr);
-								SetPageKnowshared(mem_map+mapnr);
-								continue;
-							}
-						} else if (!PageReserved(mem_map+mapnr) && \
-							!PageNosave(mem_map+mapnr) && \
-							!usedinsuspend)
-							
-							SetPageDontcopy(mem_map+mapnr);
-					}
-				}
-			}
-			up_read(&(mm->mmap_sem));
-		}
+#endif
 	}
 
 	/*
-	 * Try marking page cache pages as pageset2 too.
+	 * Mark page cache pages as pageset2.
 	 */
 	
 	/*
 	 * Inactive list
 	 */
 	if (!SUSPEND_NOPAGESET2INACTIVE) {
-		{
-			entry = inactive_list.prev;
-			while (entry != &inactive_list) {
-				struct page * page;
-
-				page = list_entry(entry, struct page, lru);
-				if (!PageDontcopy(page) && !PageKnowshared(page))
-					SetPageDontcopy(page);
-				entry = entry->prev;
-			}
+		entry = inactive_list.prev;
+		while (entry != &inactive_list) {
+			struct page * page;
+
+			page = list_entry(entry, struct page, lru);
+			SetPagePageset2(page);
+			entry = entry->prev;
 		}
 	}
 
@@ -1476,42 +1704,43 @@
 	 * Active list
 	 */
 	if (!SUSPEND_NOPAGESET2ACTIVE) {
-		{
-			entry = active_list.prev;
-			while (entry != &active_list) {
-				struct page * page;
-
-				page = list_entry(entry, struct page, lru);
-				if (!PageDontcopy(page) && !PageKnowshared(page))
-					SetPageDontcopy(page);
-				entry = entry->prev;
-			}
+		entry = active_list.prev;
+		while (entry != &active_list) {
+			struct page * page;
+
+			page = list_entry(entry, struct page, lru);
+			SetPagePageset2(page);
+			entry = entry->prev;
 		}
 	}
 
 	/*
-	 * Finally, ensure that pagedir pages aren't marked as pageset 2
+	 * Next, ensure that pagedir pages aren't marked as pageset 2
 	 */
 
-	if (pagedir_save) {
-		if (PageDontcopy(virt_to_page(pagedir_save))) {
+	if (pagedir1.pageset_size) {
+		if (PagePageset2(virt_to_page(pagedir1.data))) {
 			PRINTK(SUSPEND_VERBOSE, "Pagedir was marked as pageset2 - unmarking.\n");
-			ClearPageDontcopy(virt_to_page(pagedir_save));
+			ClearPagePageset2(virt_to_page(pagedir1.data));
 		}
-		for (i = 0; i < pagedir_size; i++)
-			if (PageDontcopy(virt_to_page(pagedir_save[i]))) {	// Must be assigned by the time recalc stats is called
+		for (i = 0; i < pagedir1.pagedir_size; i++)
+			if (PagePageset2(virt_to_page(pagedir1.data[i]))) {	// Must be assigned by the time recalc stats is called
 				PRINTK(SUSPEND_VERBOSE, "Pagedir[%d] was marked as pageset2 - unmarking.\n", i);
-				ClearPageDontcopy(virt_to_page(pagedir_save[i]));
-				pageset2_size--;
+				ClearPagePageset2(virt_to_page(pagedir1.data[i]));
 			}
 	}
 
-	read_unlock(&tasklist_lock);
+	/*
+	 * Finally, ensure HighMem pages are pageset 2
+	 */
+
+	for (i = 0; i < max_mapnr; i++)
+		if (PageHighMem(mem_map+i))
+			SetPagePageset2(mem_map+i);
+
 #ifdef DEBUG_DEFAULT
 	currentstage = oldstage;
 #endif
-	if (pageset2_size < 0)
-		pageset2_size = 0;
 }
 
 /* Make disk drivers accept operations, again */
@@ -1575,158 +1804,175 @@
 	}
 }
 
-static int save_suspend_image(void)
+static int save_image(void)
 {
 	pageset_sizes_t result;
-	int temp_result;
+	int temp_result, alloc_from, i;
 
-#ifdef SUSPEND_CONSOLE
-	//update_screen(fg_console);	/* Hmm, is this the problem? */
-#endif
-	pagedir_nosave = NULL;
 	PRINTK(SUSPEND_DEBUG, "/critical section: Counting pages to copy.\n");
 
 	recalculate_stats(0);
 	display_stats(0);
 	
 	if (SUSPEND_ABORTING) 
-		return 1; 
+		goto abort_saving;
 
 	if ((RAM_TO_SUSPEND > max_mapnr) || (RAM_TO_RESUME > max_mapnr)) {
 		printk(KERN_CRIT name_suspend "Couldn't get enough free pages, on %ld pages short\n",
 			 MAX(RAM_TO_RESUME, RAM_TO_SUSPEND) - max_mapnr);
-		spin_unlock_irq(&suspend_pagedir_lock);
-		return 1;
+		goto abort_saving;
 	}
 	if (swapinfo.freeswap < SWAP_NEEDED)  {
 		printk(KERN_CRIT name_suspend "There's not enough swap space available (%ld pages available, need %d)\n",
 			 swapinfo.freeswap, SWAP_NEEDED);
-		spin_unlock_irq(&suspend_pagedir_lock);
-		return 1;
+		goto abort_saving;
 	}
 
 	swapforimage = swapinfo.freeswap;
+	
+	PRINTK(SUSPEND_VERBOSE,"-- Creating pagedir1 and allocating extra pages (if any)\n");
 
-	pagedir_size = calcpagedirsize(pageset1_size, pageset2_size);
-	pagedir_capacity = PAGEDIR_CAPACITY(pagedir_size);
+	alloc_from = (pageset1_size > pageset2_sizelow ? pageset2_sizelow : pageset1_size);
 	
-	pagedir_save = (suspend_pagedir_t **)__get_free_pages(GFP_ATOMIC, 0);
+	if (create_pagedir(&pagedir1, pageset1_size, alloc_from))
+		goto abort_saving;
+
+	PRINTK(SUSPEND_VERBOSE,"-- Creating pagedir2\n");
+
+	if (create_pagedir(&pagedir2, pageset2_size, pageset2_size))
+		goto abort_saving;
 	
-	if (!pagedir_save) {
-		abort_suspend("Failed to allocate a pagedir!\n");
+	PRINTK(SUSPEND_VERBOSE,"-- Calling count_data_pages to set pageset addresses.\n");
+
+	result = count_data_pages(1);
+	
+	if (SUSPEND_ABORTING)
 		goto abort_saving;
-	}
+
+	PRINTK(SUSPEND_VERBOSE,"--  Result of count_data_pages: %d and %d\n", result.size1, result.size2);
+	pageset2_sizelow = result.size2low;
+	pageset2_size = result.size2;
+	pageset1_size = result.size1;
 	
 	{
-		int i;
-		for (i = 0; i < pagedir_size; i++) {
-			pagedir_save[i] = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, 0);
-			if (!pagedir_save[i]) {
-				int j;
-				for (j = 0; j < i; j++)
-					free_page((unsigned long) pagedir_save[j]);
-				free_page((unsigned long) pagedir_save);
-				abort_suspend("Unable to allocate a pagedir.\n");
-				spin_unlock_irq(&suspend_pagedir_lock);
-				return 1;
+		int i, unsetstart = -1;
+		for (i = 0; i < pageset1_size; i++)
+			if (PAGEDIR_ENTRY(&pagedir1, i)->origaddress) {
+				if (unsetstart != -1) {
+					PRINTK(SUSPEND_VERBOSE,"----  Pagedir1 has unset original address entries from %d-%d.\n",
+							unsetstart, i);
+					unsetstart = -1;
+				}
+			} else {
+				if (unsetstart != -1)
+					unsetstart = i;
 			}
-		}
+		
+		if (unsetstart != -1) 
+			PRINTK(SUSPEND_VERBOSE,"----  Pagedir1 has unset original address entries from %d-%d.\n",
+							unsetstart, i);
+		unsetstart = -1;
+		
+		for (i = 0; i < pageset1_size; i++)
+			if (PAGEDIR_ENTRY(&pagedir1, i)->address) {
+				if (unsetstart != -1) {
+					PRINTK(SUSPEND_VERBOSE,"----  Pagedir1 has unset destination address entries from %d-%d.\n",
+							unsetstart, i);
+					unsetstart = -1;
+				}
+			} else {
+				if (unsetstart != -1)
+					unsetstart = i;
+			}
+		
+		if (unsetstart != -1) 
+			PRINTK(SUSPEND_VERBOSE,"----  Pagedir1 has unset entries from %d-%d.\n",
+							unsetstart, i);
+		unsetstart = -1;
+
+		for (i = 0; i < pageset2_size; i++)
+			if (PAGEDIR_ENTRY(&pagedir2, i)->address) {
+				if (unsetstart != -1) {
+					PRINTK(SUSPEND_VERBOSE,"----  Pagedir2 has unset destination address entries from %d-%d.\n",
+							unsetstart, i);
+					unsetstart = -1;
+				}
+			} else {
+				if (!unsetstart)
+					unsetstart = i;
+			}
+		
+		if (unsetstart !=-1)
+			PRINTK(SUSPEND_VERBOSE,"----  Pagedir2 has unset desination addresses entries from %d-%d.\n",
+							unsetstart, i);
 	}
-	
+
+	PRINTK(SUSPEND_VERBOSE,"-- Copying pageset2 destination addresses from 0 to %d\n", alloc_from - 1);
+
 	{
-		int i;
-		
-		for(i=0; i < pagedir_capacity; i++) {
-			PAGEDIR_ENTRY(pagedir_save,i)->address1 = 0;
-			PAGEDIR_ENTRY(pagedir_save,i)->address2 = 0;
-			PAGEDIR_ENTRY(pagedir_save,i)->address3 = 0;
-			PAGEDIR_ENTRY(pagedir_save,i)->swap_address1.val = 0;
-			PAGEDIR_ENTRY(pagedir_save,i)->swap_address2.val = 0;
+		/* 
+		 * We know that we don't need to check that we have enough valid pagedir2
+		 * entries because this was handled above
+		 */
+
+		int pageset2index=0;
+		for(i=0; i < alloc_from; i++) {
+			while (PageHighMem(PAGEDIR_ENTRY(&pagedir2, pageset2index)->address))
+				pageset2index++;
+			PAGEDIR_ENTRY(&pagedir1,i)->address = PAGEDIR_ENTRY(&pagedir2,pageset2index)->address;
+			PRINTNOLOG(SUSPEND_VERBOSE, 1, "   Entry %d: %lx", 
+					i, 
+					(unsigned long) page_address(PAGEDIR_ENTRY(&pagedir1,i)->address));
+			pageset2index++;
 		}
+		PRINTNOLOG(SUSPEND_VERBOSE, 0, "\n");
 	}
-	
-	PRINTK(SUSPEND_VERBOSE,"Created pagedir of %d pages at %p to store a maximum of %d pages.\n",
-			pagedir_size,
-			pagedir_save,
-			pagedir_capacity);
 
-	recalculate_stats(pagedir_size);  // Get stats including pagedir (which we save to use in reloading secondary pagedir)
+	PRINTK(SUSPEND_VERBOSE,"-- Actual values: %d (%d + %d + 1) and %d.\n", 
+			pageset1_size, 
+			result.size1,
+			calcpagedirsize(pageset1_size),
+			pageset2_size);
 
-	PRINTK(SUSPEND_VERBOSE,"-- Calling count_data_pages to set pageset addresses.\n");
-	
-	result = count_data_pages(pagedir_save);
-	pageset2_size = pageset2_size_check = result.size2;
-	pageset1_size = result.size1;
-	
-	PRINTK(SUSPEND_VERBOSE,"-- Actual values: %d and %d.", pageset1_size, pageset2_size);
-	
-	if (SUSPEND_ABORTING) 
-		goto abort_saving;
+	PRINTK(SUSPEND_VERBOSE,"-- Preparing to write pages\n");
 
-	PRINTK(SUSPEND_VERBOSE, "Capacity:%d Size:%d\n", pagedir_capacity, pagedir_size);
+	spin_unlock_irq(&suspend_pagedir_lock); 
 
-	pageset1_size_check = pageset1_size;
-	pageset2_size_check = pageset2_size;
-	pagedir_size_check = pagedir_size;
+	check_shift_keys(1);
 
-	if ((pageset1_size > pagedir_capacity) || (pageset2_size > pagedir_capacity)) {
-		abort_suspend(KERN_CRIT name_suspend "Number of pages (%d & %d) grown too much! I only allowed for %d.", pageset1_size, pageset2_size, pagedir_capacity);
+	if (SUSPEND_ABORTING)
 		goto abort_saving;
-	}
-	
-	PRINTK(SUSPEND_VERBOSE,"-- Allocating pages for pagedir\n");
-	
-	pagedir_alloc_from = (pageset1_size > pageset2_size ? pageset2_size : pageset1_size);
-	
-	{
-		int i, numnosaveallocated=0;
-	
-		for(i=pagedir_alloc_from; i < pageset1_size; i++) {
-			PAGEDIR_ENTRY(pagedir_save,i)->address2 = get_zeroed_page(GFP_ATOMIC);
-			if (!PAGEDIR_ENTRY(pagedir_save,i)->address2) {
-				abort_suspend("Unable to allocate pages for pagedir!\n");
-				goto abort_saving;
-			}
-			SetPageNosave(virt_to_page(PAGEDIR_ENTRY(pagedir_save,i)->address2));
-			numnosaveallocated++;
-		}
-		if (pageset1_size > pagedir_alloc_from) {
-			PRINTK(SUSPEND_VERBOSE,"Allocated memory for pages from %d-%d.\n", pagedir_alloc_from, pageset1_size - 1);
-		}
-	}
-	PRINTK(SUSPEND_VERBOSE,"-- Allocating pages for pagedir done\n");
 
-	PRINTK(SUSPEND_VERBOSE,"-- Preparing to write pages\n");
+	/*
+	 *  ---------------------   FROM HERE ON, NEED TO REREAD PAGESET2 IF ABORTING!!! -----------------
+	 *  (We need to ensure saved pages are freed even if memory is still correct).
+	 */
 	
-	WAIT_FOR_SHIFT;
-
-	spin_unlock_irq(&suspend_pagedir_lock); 
+	temp_result = write_pageset(&pagedir2, 2);
 	
-	PRINTK(SUSPEND_VERBOSE,"-- Write pageset2\n");
-	lock_swapdevices(); /* this locks every swap device except the one set by resume= option */
-	temp_result = write_pageset(pagedir_save, 2);
-	lock_swapdevices();	/* This will unlock ignored swap devices since writing is finished */
+	if (temp_result || SUSPEND_ABORTING) {
+		goto abort_saving;
+	}
 
 	PRINTK(SUSPEND_VERBOSE,"-- Written pageset2\n");
 	
-	WAIT_FOR_SHIFT;
+	check_shift_keys(1);
 
 	spin_lock_irq(&suspend_pagedir_lock); 
 
-	if (temp_result || SUSPEND_ABORTING) {
+	if (SUSPEND_ABORTING)
 		goto abort_saving;
-	}
+
 	PRINTK(SUSPEND_VERBOSE,"-- Copying pageset1\n");
 
-	copy_pageset1(pagedir_save);
+	copy_pageset1();
 
-	WAIT_FOR_SHIFT;
-	/*
-	 *  ---------------------   FROM HERE ON, NEED TO REREAD PAGESET2 IF ABORTING!!! -----------------
-	 */
-	
 	PRINTK(SUSPEND_VERBOSE,"-- Done\n");
 	
+	spin_unlock_irq(&suspend_pagedir_lock);
+	
+	check_shift_keys(1);
+
 	if (SUSPEND_ABORTING)
 		goto abort_reloading_pagedir_two;
 
@@ -1737,41 +1983,45 @@
 	 *
 	 */
 	
-	spin_unlock_irq(&suspend_pagedir_lock);
-	
 	PRINTK(SUSPEND_VERBOSE,"-- Writing pagset1\n");
-	lock_swapdevices(); /* this locks every swap device except the one set by resume= option */
-	temp_result = write_pageset(pagedir_save, 1);
-	lock_swapdevices();	/* This will unlock ignored swap devices since writing is finished */
-	PRINTK(SUSPEND_VERBOSE,"-- Done\n");
 
-	WAIT_FOR_SHIFT;
-	if (temp_result)
+	temp_result = write_pageset(&pagedir1, 1);
+
+	if (temp_result || SUSPEND_ABORTING)
 		goto abort_reloading_pagedir_two;
 
-	lock_swapdevices(); /* this locks every swap device except the one set by resume= option */
-	temp_result = write_pagedir_and_suspend_header(pagedir_save);
-	lock_swapdevices();	/* This will unlock ignored swap devices since writing is finished */
-	if (temp_result)
+	PRINTK(SUSPEND_VERBOSE,"-- Done\n");
+
+	check_shift_keys(1);
+
+	if (temp_result || (SUSPEND_ABORTING))
 		goto abort_reloading_pagedir_two;
 
-	WAIT_FOR_SHIFT;
+	temp_result = write_pagedir_and_suspend_header();
+
+	if (temp_result || (SUSPEND_ABORTING))
+		goto abort_reloading_pagedir_two;
 
 	si_swapinfo(&swapinfo);
 	PRINTK(SUSPEND_VERBOSE, "Finished writing image. %ld pages of swap left.\n", swapinfo.freeswap);
 	return 0;
 
 abort_reloading_pagedir_two:
-	temp_result = read_secondary_pagedir(pagedir_save);
-	if (!temp_result)
+	printk("Reloading secondary pagedir.\n");
+	temp_result = read_secondary_pagedir(1);
+	if (temp_result)
 		panic("Attempt to reload pagedir 2 while aborting a suspend failed.");
 abort_saving:
-	free_suspend_pagedir(pagedir_save);
-	pagedir_save = NULL;
-	spin_unlock_irq(&suspend_pagedir_lock);
+	free_pagedir(&pagedir2);
+	free_pagedir(&pagedir1);
+
+	{
+		int was_locked = (spin_is_locked(&suspend_pagedir_lock));
+		if (was_locked)
+			spin_unlock_irq(&suspend_pagedir_lock);
+	}
 
-	PRINTK(SUSPEND_VERBOSE, " ***** AT END ABORT ***** ");
-	WAIT_FOR_SHIFT;
+	check_shift_keys(1);
 
 	return -1;		
 
@@ -1782,12 +2032,12 @@
 void suspend_power_down(void)
 {
 	C_A_D = 0;
+	/* No delay should be needed - ide_disk_suspend purges cache now */
 	PRINTK(SUSPEND_DEBUG, name_suspend "Trying to power down.\n");
 	beepOK;			/* last beep */
-	mdelay(1000);		/* FIXME: we need to flush hw disk cache
-				   in a more certain way */
+	prepare_status("Ready to power down.");
 #ifdef CONFIG_VT
-	WAIT_FOR_SHIFT;
+	check_shift_keys(1);
 	if (SUSPEND_REBOOT ^ (!!(shift_state & (1 << KG_CTRL))))
 		machine_restart(NULL);
 	else
@@ -1817,11 +2067,11 @@
 	barrier();
 	mb();
 	spin_lock_irq(&suspend_pagedir_lock);	/* Done to disable interrupts */ 
-
 	PRINTK(SUSPEND_DEBUG, name_resume "Waiting for DMAs to settle down...\n");
 	mdelay(1000);	/* We do not want some readahead with DMA to corrupt our memory, right?
 			   Do it with disabled interrupts for best effect. That way, if some
 			   driver scheduled DMA, we have good chance for DMA to finish ;-). */
+	PRINTK(SUSPEND_DEBUG, name_resume "About to copy pageset1 back...\n");
 }
 
 inline void restore_processor_context (void);
@@ -1835,15 +2085,13 @@
 	update_screen(fg_console);	/* Hmm, is this the problem? */
 #endif
 
-	WAIT_FOR_SHIFT;
-
-	read_secondary_pagedir(pagedir_save);	
+	read_secondary_pagedir(0);
 	prepare_status("Cleaning up...");
 	update_status(100);
 	
 	PRINTK(SUSPEND_DEBUG, name_resume "Freeing prev allocated pagedir.\n");
-	free_suspend_pagedir(pagedir_save);
-	pagedir_save = NULL;
+	free_pagedir(&pagedir2);
+	free_pagedir(&pagedir1);
 	PRINTK(SUSPEND_DEBUG, name_resume "Resume drivers.\n");
 	drivers_resume(RESUME_ALL_PHASES);
 	PRINTK(SUSPEND_DEBUG, name_resume "Free pagedir lock.\n");
@@ -1872,13 +2120,14 @@
 	PRINTK(SUSPEND_DEBUG, "-- AT START OF DO_MAGIC_SUSPEND_2 --\n");
 	PRINTK(SUSPEND_VERBOSE,"-- read_swapfiles()\n");
 	read_swapfiles();
-	if (!save_suspend_image())
+	if (!save_image())
 		suspend_power_down ();
 	
 #ifdef DEBUG_DEFAULT
 	currentstage = 0;
 #endif
-	printk(KERN_EMERG name_suspend "Suspend failed, trying to recover...\n");
+	if (!abort_requested)
+		printk(KERN_EMERG name_suspend "Suspend failed, trying to recover...\n");
 	MDELAY(1000); /* So user can wait and report us messages if armageddon comes :-) */
 
 	barrier();
@@ -1893,7 +2142,7 @@
 static void do_software_suspend(void)
 {
 	unsigned long flags;
-	int i;
+	int i, result;
 
 	si_swapinfo(&swapinfo);	/* FIXME: si_swapinfo(&i) returns all swap devices information.*/ 
 	if (!swapinfo.freeswap) {
@@ -1901,11 +2150,11 @@
 		return;
 	}
 	now_resuming = 0;
+	abort_requested = 0;
+	suspend_swap_bh = NULL;
+	last_suspend_cache_page = NULL;
 	if (prepare_suspend_console())
 		printk(name_suspend "Can't allocate a console... proceeding\n");
-#ifdef DEBUG_DEFAULT
-	currentstage = STAGE_FREEZER;
-#endif
 	beepOK;			/* first beep */
 	/* Order is significant. 
 	 * We set up the nosavemap first so all maps are recorded as nosave.
@@ -1913,58 +2162,66 @@
 	PRINTK(SUSPEND_DEBUG, "Allocating nosavemap\n");
 	for (i = 0; i < max_mapnr; i++) {
 		ClearPageNosave(mem_map+i);
-		ClearPageDontcopy(mem_map+i);
-		ClearPageKnowshared(mem_map+i);
+		ClearPagePageset2(mem_map+i);
 	}
 	SetPageNosave(virt_to_page(copybuff));
 	
 	// Tasks allowed to run while eating memory.
-	prepare_status("Getting tq_disk lock...");
-	spin_lock_irqsave(&tq_disk, flags);
-	prepare_status("Calling prepare_suspend_processes...");
-	if (prepare_suspend_processes()) {
-		spin_unlock_irqrestore(&tq_disk, flags);
-		prepare_status("Failed to freeze all processes!");
-	} else {
-		spin_unlock_irqrestore(&tq_disk, flags);
-		prepare_status("Processes frozen, lock released...");
-		/* Get the tq_disk lock before freezing processes */
-		prepare_status("Processes frozen...");
-#ifdef DEBUG_DEFAULT
-		currentstage = STAGE_EAT_MEMORY;
-#endif
-		beepOK;		/* second beep */
-		eat_memory();
+	prepare_status("Getting io_request lock...");
+	spin_lock_irqsave(&io_request_lock, flags);
+	prepare_status("Syncing data...");
+	PRINTK(SUSPEND_VERBOSE, "Calling sys_sync.\n");
+	sys_sync();
 #ifdef DEBUG_DEFAULT
-		currentstage = 0;
-#endif
-#if 0
-		free_memory(nb);
+	currentstage = STAGE_FREEZER;
 #endif
-		if (!SUSPEND_ABORTING) {
-			/* No need to invalidate any vfsmnt list -- they will be valid after resume, anyway.
-			 *
-			 * We sync here -- so you have consistent filesystem state when things go wrong.
-			 * -- so that noone writes to disk after we do atomic copy of data.
-			 */
-			PRINTK(SUSPEND_VERBOSE, name_suspend "Syncing disks before copy\n" );
-			prepare_status("Syncing disks...");
-			do_suspend_sync();
-			if (drivers_suspend()==0) { 
-				prepare_status("Starting low level suspend...");
-				do_suspend_lowlevel(0);			/* This function returns after machine woken up from resume */
-				beepOK;
-			}
+	prepare_status("Calling prepare_suspend_processes...");
+	result =  prepare_suspend_processes();
+	spin_unlock_irqrestore(&io_request_lock, flags);
+	if (result) {
+		prepare_status("Failed to freeze all processes!");
+		PRINTK(SUSPEND_VERBOSE, "Failed to freeze all processes.\n");
+	}
+	if (result || SUSPEND_ABORTING) {
+		goto out;
+	}
+	PRINTK(SUSPEND_VERBOSE, "Eating memory.\n");
+	eat_memory();
+	free_grabbed_memory();
+	if (SUSPEND_ABORTING)
+		goto out;
+	beepOK;		/* second beep */
+
+	if (!SUSPEND_ABORTING) {
+		/* No need to invalidate any vfsmnt list -- they will be valid after resume, anyway.
+		 *
+		 * We sync here -- so you have consistent filesystem state when things go wrong.
+		 * -- so that noone writes to disk after we do atomic copy of data.
+		 */
+		PRINTK(SUSPEND_VERBOSE, name_suspend "Syncing disks before copy\n" );
+		prepare_status("Syncing disks...");
+		do_suspend_sync();
+		if (drivers_suspend()==0) { 
+			prepare_status("Starting low level suspend...");
+			do_suspend_lowlevel(0);
+			beepOK;
 		}
 	}
 	if (SUSPEND_VERBOSE)
 		display_debug_info();
 
 	PRINTK(SUSPEND_VERBOSE, name_resume "Restarting processes with %d pages free...\n", nr_free_pages());
+out:
 	thaw_processes();
-	
+
 	MDELAY(1000);
-	restore_console ();
+	restore_console();
+#if defined(CONFIG_LPP)
+	PROGRESS(150, currentmessage);
+#endif
+#ifdef DEBUG_DEFAULT
+	currentstage = 0;
+#endif
 }
 
 void software_suspend(void)
@@ -1978,8 +2235,7 @@
 		printk(name_swsusp "pse or pse36 is required, disabling software suspend");
 		return;
 	}
-	if (in_interrupt())
-		BUG();
+	BUG_ON(in_interrupt());
 	software_suspend_enabled = 0;
 	do_software_suspend();
 	software_suspend_enabled = 1;
@@ -2001,15 +2257,15 @@
 	}
 }
 
-static void warmup_collision_cache(suspend_pagedir_t **pagedir) {
+static void warmup_collision_cache(void) {
 	int i;
 	
 	PRINTK(SUSPEND_DEBUG, "Setting up pagedir cache...");
 	for (i = 0; i < max_mapnr; i++)
 		ClearPageCollides(mem_map+i);
 
-	for(i=0; i < pageset1_size; i++) {
-		SetPageCollides(virt_to_page(PAGEDIR_ENTRY(pagedir, i)->address1));
+	for(i=0; i < pagedir_nosave.pageset_size; i++) {
+		SetPageCollides(PAGEDIR_ENTRY(&pagedir_nosave, i)->origaddress);
 		if (!(i%800)) {
 			PRINTK(SUSPEND_DEBUG, ".");
 		}
@@ -2020,11 +2276,6 @@
 
 
 /*
- * Returns true if given address collides with any orig_address 
- */
-#define does_collide(pagedir, address) (PageCollides(virt_to_page(address)))
-
-/*
  * We check here that pagedir & pages it points to won't collide with pages
  * where we're going to restore from the loaded pages later
  */
@@ -2034,23 +2285,24 @@
 	int i, nrdone = 0;
 	void **eaten_memory = NULL;
 	void **c = eaten_memory, *f, *addr;
+	struct page * pageaddr = NULL;
 
 	// Because we're trying to make this work when we're saving as much memory as possible
 	// we need to remember the pages we reject here and then free them when we're done.
 	
 	PRINTK(SUSPEND_VERBOSE,"\nAllocating memory.\n");
-	for(i=0; i < MAX(pageset1_size, pageset2_size); i++) {
-
+	for(i=0; i < pagedir_nosave.pageset_size; i++) {
 		while ((addr = (void *) get_zeroed_page(GFP_ATOMIC))) {
 			memset(addr, 0, PAGE_SIZE);
-			if (!does_collide(pagedir_nosave, (unsigned long) addr)) {
+			pageaddr = virt_to_page(addr);
+			if (!PageCollides(pageaddr)) {
 				break;
 			}
 			eaten_memory = addr;
 			*eaten_memory = c;
 			c = eaten_memory;
 		}
-		PAGEDIR_ENTRY(pagedir_nosave,i)->address3 = (unsigned long) addr;
+		PAGEDIR_ENTRY(&pagedir_nosave,i)->address = pageaddr;
 		nrdone++;
 	}
 
@@ -2065,9 +2317,9 @@
 	}
 	eaten_memory = NULL;
 	
-	PRINTK(SUSPEND_VERBOSE,"Check_pagedir: Prepared %d of %d pages.\n", nrdone, MAX(pageset1_size, pageset2_size));
+	PRINTK(SUSPEND_VERBOSE,"Check_pagedir: Prepared %d of %d pages.\n", nrdone, pagedir_nosave.pageset_size);
 	
-	WAIT_FOR_SHIFT;
+	check_shift_keys(1);
 
 	return 0;
 }
@@ -2079,47 +2331,48 @@
 	int oom = 0, i, numeaten = 0;
 
 	PRINTK(SUSPEND_VERBOSE,"Relocating conflicting parts of pagedir.\n");
-	for (i = -1; i < pagedir_size; i++) {
+
+	for (i = -1; i < pagedir_nosave.pagedir_size; i++) {
 		int this_collides = 0;
 
 		if (i == -1)
-			this_collides = does_collide(pagedir_nosave, (unsigned long) pagedir_nosave);
+			this_collides = PageCollides(virt_to_page(pagedir_nosave.data));
 		else
-			this_collides = does_collide(pagedir_nosave, (unsigned long) pagedir_nosave[i]);
+			this_collides = PageCollides(virt_to_page(pagedir_nosave.data[i]));
 
 		if (this_collides) {
-			while ((m = (void *) __get_free_pages(GFP_ATOMIC, 0))) {
+			while ((m = (void *) get_zeroed_page(GFP_ATOMIC))) {
 				memset(m, 0, PAGE_SIZE);
-				if (!does_collide(pagedir_nosave, (unsigned long)m)) {
+				if (!PageCollides(virt_to_page(m))) {
 					if (i == -1) {
-						copy_page(m, pagedir_nosave);
-						free_page((unsigned long) pagedir_nosave);
-						pagedir_nosave = m;
+						copy_page(m, pagedir_nosave.data);
+						free_page((unsigned long) pagedir_nosave.data);
+						pagedir_nosave.data = m;
 					}
 					else {
-						copy_page(m, (void *) pagedir_nosave[i]);
-						free_page((unsigned long) pagedir_nosave[i]);
-						pagedir_nosave[i] = m;
+						copy_page(m, (void *) pagedir_nosave.data[i]);
+						free_page((unsigned long) pagedir_nosave.data[i]);
+						pagedir_nosave.data[i] = m;
 					}
-					//__flush_tlb();
 					break;
 				}
 				numeaten ++;
 				eaten_memory = m;
-				PRINTNOLOG(SUSPEND_VERBOSE,"Eaten: %d. Still to try:%d\r", numeaten, nr_free_pages()); 
+				PRINTNOLOG(SUSPEND_VERBOSE, 0, "Eaten: %d. Still to try:%d\r", numeaten, nr_free_pages()); 
 				*eaten_memory = c;
 				c = eaten_memory;
 			}
+
 			if (!m) {
 				printk("\nRan out of memory trying to relocate pagedir (tried %d pages).\n", numeaten);
 				oom = 1;
 				break;
 			}
 		}
-
 	}
 		
 	PRINTK(SUSPEND_VERBOSE,"\nFreeing rejected memory locations...");
+
 	c = eaten_memory;
 	while(c) {
 		f = c;
@@ -2131,7 +2384,7 @@
 	
 	PRINTK(SUSPEND_VERBOSE,"\n");
 	
-	WAIT_FOR_SHIFT;
+	check_shift_keys(1);
 
 	if (oom) 
 		return -ENOMEM;
@@ -2180,14 +2433,15 @@
 	return 0;
 }
 
-static int bdev_read_page(long pos, void *buf)
+static int bdev_read_page(kdev_t bdev, long pos, void *buf)
 {
 	struct buffer_head *bh;
+	PRINTNOLOG(SUSPEND_VERBOSE, 0, "{%x %lx -> %p}", bdev, pos, buf);
 	if (pos%PAGE_SIZE) {
 		abort_suspend("pos > pagesize!\n");
 		return -EIO;
 	}
-	BREAD_BUFFER_HEAD(pos);
+	BREAD_BUFFER_HEAD(bdev, pos);
 	memcpy(buf, bh->b_data, PAGE_SIZE);
 	if (!buffer_uptodate(bh)) {
 		abort_suspend("buffer_uptodate false in bdev_read_page!\n");
@@ -2200,6 +2454,13 @@
 	return 0;
 } 
 
+static inline int bdev_read_swap(swp_entry_t posn, void *buf)
+{
+	kdev_t bdev = swap_info[SWP_TYPE(posn)].swap_device;
+	int pos = SWP_OFFSET(posn) * PAGE_SIZE; 
+	return bdev_read_page(bdev, pos, buf);
+} 
+
 static int swsusp_mainloop(void *unused)
 {
   	printk(name_swsusp "kswsuspd starting\n"); /* This will print the current version on boot */
@@ -2225,14 +2486,16 @@
 
 static int noresume_fix_signature(union p_diskpage cur)
 {
+	/* 
+	 * We don't do a sanity check here: we want to restore the swap 
+	 * whatever version of kernel made the suspend image
+	 */
 	struct buffer_head *bh;
-				/* We don't do a sanity check here: we want to restore the swap
-				   whatever version of kernel made the suspend image */
 	printk(name_resume "%s: Fixing swap signatures...\n", resume_file);
 				/* We need to write swap, but swap is *not* enabled so
 				   we must write the device directly */
 	PRINTK(SUSPEND_VERBOSE,"Swap signature will be set to %10s\n",cur.pointer->swh.magic.magic);
-	BREAD_BUFFER_HEAD(0);
+	BREAD_BUFFER_HEAD(resume_device, 0);
 	if (is_read_only(bh->b_dev)) {
 		printk(KERN_ERR name_resume "Can't write to read-only device %s\n",
 		       kdevname(bh->b_dev));
@@ -2246,9 +2509,9 @@
 	if (buffer_uptodate(bh)) {
 		brelse(bh);
 		BFREE(bh);
-		free_suspend_cache_page();
+		free_suspend_cache_page(last_suspend_cache_page);
 		/* As with fix_signature, try to read to ensure its written. */
-		BREAD_BUFFER_HEAD(0);
+		BREAD_BUFFER_HEAD(resume_device, 0);
 		brelse(bh);
 		BFREE(bh);
 		return 0;
@@ -2268,28 +2531,28 @@
 				   other non zero values are errors. */
 	swp_entry_t next;
 	int i, tries = 0, len, *ptr;
-	unsigned long pagedirend = 0;
 	void **eaten_memory = NULL;
 	void **c = eaten_memory, *f;
-	//int orig_free = nr_free_pages();
-
-#define PREPARENEXT do { \
-		next = cur.pointer->link.next; \
-		next.val = SWP_OFFSET(next) * PAGE_SIZE; \
-        } while(0)
+	kdev_t header_block_device = 0;
 
-	if (bdev_read_page(0, cur.ptr)) return -EIO;
+	if (bdev_read_page(resume_device, 0, cur.ptr)) return -EIO;
 
 	if ((!memcmp("SWAP-SPACE",cur.pointer->swh.magic.magic,10)) ||
 	    (!memcmp("SWAPSPACE2",cur.pointer->swh.magic.magic,10))) {
 		printk(KERN_ERR name_resume "This is normal swap space\n" );
 		return -EINVAL;	/* non fatal error */
 	}
-	PREPARENEXT; /* We have to read next position before we overwrite it */
+	/* Put bdev of suspend header in last byte of swap header (unsigned short) */
+	{
+		kdev_t * header_ptr = (kdev_t *) (&(cur.pointer->swh.magic.magic[2]));
+		header_block_device = *header_ptr;
+	}
 
-	if (!memcmp("SUSP1R",cur.pointer->swh.magic.magic,6))
+	next = cur.pointer->link.next;
+
+	if (!memcmp("1R",cur.pointer->swh.magic.magic,2))
 		memcpy(cur.pointer->swh.magic.magic,"SWAP-SPACE",10);
-	else if (!memcmp("SUSP2R",cur.pointer->swh.magic.magic,6))
+	else if (!memcmp("2R",cur.pointer->swh.magic.magic,2))
 		memcpy(cur.pointer->swh.magic.magic,"SWAPSPACE2",10);
 	else {
 		printk("%sUnable to find suspended-data signature (%.10s - misspelled?\n", 
@@ -2304,7 +2567,22 @@
 	PRINTK(SUSPEND_VERBOSE, name_resume "Signature found, resuming\n");
 	MDELAY(1000);
 	
-	if (bdev_read_page(next.val, cur.ptr)) return -EIO;
+	/* 
+	 * If the header is not on the resume_device, get the resume device first.
+	 */
+	if (header_block_device != resume_device) {
+		int blksize = 0;
+		if (!blksize_size[MAJOR(header_block_device)]) {
+			PRINTK(SUSPEND_DEBUG, name_resume "%x: Blocksize not known?", header_block_device);
+		} else blksize = blksize_size[MAJOR(header_block_device)][MINOR(header_block_device)];
+		if (!blksize) {
+			PRINTK(SUSPEND_DEBUG, name_resume "%x: Blocksize not set?", header_block_device);
+			blksize = PAGE_SIZE;
+		}
+		set_blocksize(header_block_device, PAGE_SIZE);
+	}
+
+	if (bdev_read_page(header_block_device, SWP_OFFSET(next) * PAGE_SIZE, cur.ptr)) return -EIO;
 	if (sanity_check(&(cur.pointer->sh))) /* Is this the same machine? */
 		return -EPERM;
 #ifdef SOFTWARE_SUSPEND_MTRR
@@ -2313,21 +2591,19 @@
 	if(len)
 		mtrr_resume(ptr);
 #endif
-	PREPARENEXT;
+	next = cur.pointer->link.next;
 
 	base_mem_free = cur.pointer->sh.base_mem_free;
 	swapforimage = cur.pointer->sh.swapforimage;
-	pagedir_save = cur.pointer->sh.suspend_pagedir;
+	pagedir_nosave.pageset_size = cur.pointer->sh.pagedir.pageset_size;
+	pagedir_nosave.pagedir_size = cur.pointer->sh.pagedir.pagedir_size;
 	expected_size1 = cur.pointer->sh.expected_size1;
 	expected_size2 = cur.pointer->sh.expected_size2;
-	pageset1_size = cur.pointer->sh.pageset1_size;
-	pageset2_size = cur.pointer->sh.pageset2_size;
-	pagedir_capacity = MAX(pageset1_size, pageset2_size);
-	pagedir_size = cur.pointer->sh.pagedir_size;
 	swsusp_state[0] = cur.pointer->sh.param0;
 	swsusp_state[1] = cur.pointer->sh.param1;
 	swsusp_state[2] = cur.pointer->sh.param2;
 	swsusp_state[3] = cur.pointer->sh.param3;
+
 	/*
 	 * We don't need to save swsusp_state[4] because it's not used during resume
 	 * and will be restored with the image anyway
@@ -2337,20 +2613,57 @@
 	if (prepare_suspend_console())
 		printk(name_resume "Can't allocate a console... proceeding\n");
 
-	pagedir_nosave = (suspend_pagedir_t **)__get_free_pages(GFP_ATOMIC, 0);
-	
-	if (!pagedir_nosave)
+	/* 
+	 * Swap hasn't been turned on yet, so we can safely store our kdevs in the original array.
+	 * We're going to be reloading the same values anyway, so nothing will be broken by this.
+	 */
+
+	for (i = 0; i < MAX_SWAPFILES; i++) {
+		kdev_t thisdevice = cur.pointer->sh.kdevs[i];
+		int blksize = 0;
+
+		PRINTNOLOG(SUSPEND_VERBOSE, 1, "Swap device %d is %x.", i, cur.pointer->sh.kdevs[i]);
+
+		swap_info[i].swap_device = thisdevice;
+		
+		if (!thisdevice)
+			continue;
+
+		if (thisdevice == resume_device) {
+			PRINTNOLOG(SUSPEND_VERBOSE, 0, "Resume root device %x", thisdevice);
+			resume_root_index = i;
+			continue;
+		}
+
+		PRINTNOLOG(SUSPEND_VERBOSE, 0, "Resume secondary device %x", thisdevice);
+		if (!blksize_size[MAJOR(thisdevice)]) {
+			PRINTK(SUSPEND_DEBUG, name_resume "%x: Blocksize not known?", thisdevice);
+		} else blksize = blksize_size[MAJOR(thisdevice)][MINOR(thisdevice)];
+		if (!blksize) {
+			PRINTK(SUSPEND_DEBUG, name_resume "%x: Blocksize not set?", thisdevice);
+			blksize = PAGE_SIZE;
+		}
+		set_blocksize(thisdevice, PAGE_SIZE);
+	}
+
+	PRINTNOLOG(SUSPEND_VERBOSE, 1, "");
+
+	check_shift_keys(1);
+
+	pagedir_nosave.data = (struct pbe **)get_zeroed_page(GFP_ATOMIC);
+
+	if (!pagedir_nosave.data)
 		return -ENOMEM;
 
 	{
 		int i;
-		for (i = 0; i < pagedir_size; i++) {
-			pagedir_nosave[i] = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, 0);
-			if (!pagedir_nosave[i]) {
+		for (i = 0; i < pagedir_nosave.pagedir_size; i++) {
+			pagedir_nosave.data[i] = (struct pbe *)get_zeroed_page(GFP_ATOMIC);
+			if (!pagedir_nosave.data[i]) {
 				int j;
 				for (j = 0; j < i; j++)
-					free_page((unsigned long) pagedir_nosave[j]);
-				free_page((unsigned long) pagedir_nosave);
+					free_page((unsigned long) pagedir_nosave.data[j]);
+				free_page((unsigned long) pagedir_nosave.data);
 				abort_suspend("Unable to allocate a pagedir.\n");
 				spin_unlock_irq(&suspend_pagedir_lock);
 				return -ENOMEM;
@@ -2358,33 +2671,32 @@
 		}
 	}
 	
-	PRINTK(SUSPEND_VERBOSE,name_resume "Reading pagedir of %d pages, %lx", pagedir_size, next.val);
+	PRINTK(SUSPEND_VERBOSE,name_resume "Reading pagedir of %d pages, %lx", pagedir_nosave.pagedir_size, next.val);
 
 	/* We get pages in reverse order of saving! */
-	for (i=pagedir_size-1; i>=0; i--) {
-		PRINTNOLOG(SUSPEND_VERBOSE,"\r%d to read, %d free. ", i+1, nr_free_pages());
+	for (i=pagedir_nosave.pagedir_size-1; i>=0; i--) {
+		PRINTNOLOG(SUSPEND_VERBOSE, 1, "%d to read, %d free. ", i+1, nr_free_pages());
 #ifdef DEBUG_DEFAULT
-		if ((i < PRINTTHRESH) || ((i+PRINTTHRESH) > pagedir_size))
-			PRINTNOLOG(SUSPEND_VERBOSE, "[sw]%lx -> [pd%d]%p.\n", next.val, i, pagedir_nosave[i]);
+		if ((i < PRINTTHRESH) || ((i+PRINTTHRESH) > pagedir_nosave.pagedir_size))
+			PRINTNOLOG(SUSPEND_VERBOSE, 0, "[sw]%lx -> [pd%d]%p.\n", next.val, i, pagedir_nosave.data[i]);
 #endif
 		if (!next.val) {
 			abort_suspend("next.val NULL in __read_primary_suspend_image!\n");
 			return -EPERM;
 		}
-		cur.pointer = (union diskpage *) pagedir_nosave[i];
-		if (bdev_read_page(next.val, cur.ptr)) return -EIO;
-		pagedirend = next.val;
-		PREPARENEXT;
+		cur.pointer = (union diskpage *) pagedir_nosave.data[i];
+		if (bdev_read_swap(next, cur.ptr)) return -EIO;
+		next = cur.pointer->link.next;
 	}
-	PRINTK(SUSPEND_VERBOSE,"\r%d read, %d free.   ", pagedir_size, nr_free_pages());
+	PRINTK(SUSPEND_VERBOSE,"\r%d read, %d free.   ", pagedir_nosave.pagedir_size, nr_free_pages());
 	if (next.val) {
 		abort_suspend("next.val not NULL in __read_primary_suspend_image!\n");
 		return -EPERM;
 	}
 
-	WAIT_FOR_SHIFT;
+	check_shift_keys(1);
 
- 	warmup_collision_cache(pagedir_nosave);
+ 	warmup_collision_cache();
  
 	PRINTK(SUSPEND_VERBOSE, "Attempting to relocate pagedir with %d pages free.\n", nr_free_pages());
 	if (relocate_pagedir())
@@ -2392,12 +2704,12 @@
 	if (check_pagedir())
 		return -ENOMEM;
 
-	PRINTK(SUSPEND_VERBOSE,name_resume "Reading primary pageset data (%d pages). Pagedir at %lx\n", pageset1_size, (unsigned long) pagedir_nosave);
+	PRINTK(SUSPEND_VERBOSE,name_resume "Reading primary pageset data (%d pages). Pagedir at %lx\n", pagedir_nosave.pageset_size, (unsigned long) pagedir_nosave.data);
 	
 	PRINTK(SUSPEND_VERBOSE,"Seeking copy buffer location.\n");
 	while ((copybuff = (unsigned long) get_zeroed_page(GFP_ATOMIC))) {
 		memset((void *) copybuff, 0, PAGE_SIZE);
-		if (!does_collide(pagedir_nosave, copybuff)) {
+		if (!PageCollides(virt_to_page(copybuff))) {
 			break;
 		}
 		tries++;
@@ -2421,29 +2733,33 @@
 	PRINTK(SUSPEND_VERBOSE,"\n");
 	eaten_memory = NULL;
 
-	WAIT_FOR_SHIFT;
+	check_shift_keys(1);
 
 	prepare_status("Reading suspend image (1)...");
 
-	for(i=0; i < pageset1_size; i++) {
-		swp_entry_t swap_address = PAGEDIR_ENTRY(pagedir_nosave,i)->swap_address1;
-		PRINTNOLOG(SUSPEND_VERBOSE,"\r%d/%d read. %d free.", i+1, pageset1_size, nr_free_pages());
+	for(i=0; i < pagedir_nosave.pageset_size; i++) {
+		swp_entry_t swap_address = PAGEDIR_ENTRY(&pagedir_nosave,i)->swap_address;
+		PRINTNOLOG(SUSPEND_VERBOSE, 1,"%d/%d read. %d free.", i+1, pagedir_nosave.pageset_size, nr_free_pages());
 #ifdef DEBUG_DEFAULT
-		if ((i+PRINTTHRESH) > pageset1_size)
-			PRINTNOLOG(SUSPEND_VERBOSE, "!%d! [sw]%lx -> [3]%lx (->[1]%lx).\n", i, next.val, PAGEDIR_ENTRY(pagedir_nosave,i)->address3, PAGEDIR_ENTRY(pagedir_nosave,i)->address1);
+		//if ((i+PRINTTHRESH) > pagedir_nosave.pageset_size)
+			PRINTNOLOG(SUSPEND_VERBOSE, 0, "!%d! [sw]%lx -> [3]%lx (->[1]%lx).", 
+					i,
+					swap_address.val,
+					(unsigned long) page_address(PAGEDIR_ENTRY(&pagedir_nosave,i)->address),
+					(unsigned long) page_address(PAGEDIR_ENTRY(&pagedir_nosave,i)->origaddress));
 #endif
-		update_status((int) (i * 100 / pageset1_size));
-		next.val = SWP_OFFSET (swap_address) * PAGE_SIZE;
+		update_status((int) (i * 100 / pagedir_nosave.pageset_size));
 		/* You do not need to check for overlaps...
 		   ... check_pagedir already did this work */
-		if (bdev_read_page(next.val, (char *)(PAGEDIR_ENTRY(pagedir_nosave,i)->address3))) return -EIO;
-		//__flush_tlb();
+		if (bdev_read_swap(swap_address, (char *)(page_address(PAGEDIR_ENTRY(&pagedir_nosave,i)->address)))) return -EIO;
+		check_shift_keys(0);
+
 	}
 	PRINTK(SUSPEND_VERBOSE,"\n|\n");
 	
 	update_status(100);
 	
-	WAIT_FOR_SHIFT;
+	check_shift_keys(1);
 
 	return 0;
 }
@@ -2479,7 +2795,6 @@
 		case -EINVAL:	/* non fatal error */
 			set_blocksize(resume_device, blksize);
 			software_suspend_enabled = 1;
-
 			MDELAY(1000);
 			return(error);
 			break;
@@ -2504,10 +2819,9 @@
 	return(error);
 }
 
-static int read_secondary_pagedir(suspend_pagedir_t ** pagedir)
+static int read_secondary_pagedir(int overwrittenpagesonly)
 {
-	swp_entry_t next;
-	int i, result = 0, loop;
+	int i, result = 0, finish_at = 0;
 
 	if (!pageset2_size)
 		return 0;
@@ -2515,27 +2829,36 @@
 	PRINTK(SUSPEND_DEBUG, "Beginning of read_secondary_pagedir: "); 
 	drivers_unsuspend(); 
 
-	PRINTK(SUSPEND_VERBOSE,name_resume "Reading secondary pageset data (%d pages). Pagedir at %lx.\n", pageset2_size, (unsigned long) pagedir);
+	PRINTK(SUSPEND_VERBOSE,name_resume "Reading secondary pageset data (%d pages). Pagedir at %lx.\n", pageset2_size, (unsigned long) pagedir2.data);
 	if (!copybuff)
 		abort_suspend("Copy buffer isn't allocated!\n");
 
 	prepare_status("Reading suspend image (2)...");
 	beepOK;
 	
-	for(i=0; i < pageset2_size; i++) {
+	if (overwrittenpagesonly)
+		finish_at = MIN(pageset1_size, pageset2_size);
+	else
+		finish_at = pageset2_size;
+
+	for(i=0; i < finish_at; i++) {
 		pte_t * pte;
 		int pageprot = 0, restore = 0;
-		swp_entry_t swap_address = PAGEDIR_ENTRY(pagedir, i)->swap_address2;
-		next.val = SWP_OFFSET (swap_address) * PAGE_SIZE;
-		PRINTNOLOG(SUSPEND_VERBOSE,"\r%d/%d. ", i+1, pageset2_size);
+		char * dest_address;
+
+		swp_entry_t swap_address = PAGEDIR_ENTRY(&pagedir2, i)->swap_address;
+		PRINTNOLOG(SUSPEND_DEBUG, 1, "%d/%d. ", i+1, pageset2_size);
 		update_status((int) (i * 100 / pageset2_size));
-		if (bdev_read_page(next.val, (char *)(copybuff))) {
+		PRINTNOLOG(SUSPEND_DEBUG, 0, "%lx->%lx", swap_address.val, copybuff);
+		if (bdev_read_swap(swap_address, (char *)(copybuff))) {
 			result = -EIO;
 			break;
 		}
-		swap_free(swap_address);
 		
-		pte = lookup_address(PAGEDIR_ENTRY(pagedir, i)->address2);
+		/* Don't waste time checking PageHighMem - kmap/unmap do the right thing */
+		dest_address = kmap(PAGEDIR_ENTRY(&pagedir2, i)->address);
+		
+		pte = lookup_address((unsigned long) dest_address);
 		if (pte) {
 			pageprot = pte->pte_low & ~_PAGE_CHG_MASK;
 			if (pageprot != pgprot_val(PAGE_KERNEL_NOCACHE)) {
@@ -2544,24 +2867,24 @@
 			}
 		}
 
-		for (loop=0; loop < PAGE_SIZE; loop++) {
-			*(((char *)(PAGEDIR_ENTRY(pagedir, i)->address2))+loop) =
-				*(((char *)(copybuff))+loop);
-		}
-
-		__flush_tlb_one(PAGEDIR_ENTRY(pagedir, i)->address2);
-		__flush_tlb_one(copybuff);
+		PRINTNOLOG(SUSPEND_DEBUG, 0, "->%p%s", dest_address,
+				PageHighMem(PAGEDIR_ENTRY(&pagedir2, i)->address) ? " (HighMem)" : "");
+		copy_page((char*) (dest_address),
+				(char*)(copybuff));
 
 		if (restore) {
 			set_pte(pte, pte_modify(*pte, __pgprot(pageprot)));
 		}
+		
+		kunmap(PAGEDIR_ENTRY(&pagedir2, i)->address);
+		
+		check_shift_keys(0);
 	}
 	
 	update_status(100);
-	WAIT_FOR_SHIFT;
+	check_shift_keys(1);
 
 	PRINTK(SUSPEND_VERBOSE,"\n");
-	__flush_tlb_global();		/* Flush again after reading secondary pagedir */
 	drivers_suspend(); 
 	return result;
 }
@@ -2574,7 +2897,7 @@
 void software_resume(void)
 {
 #ifdef CONFIG_SMP
-	printk(name_swsusp "malfunctioning SMP support. Disabled :(\n");
+	printk(KERN_WARNING "Software Suspend has malfunctioning SMP support. Disabled :(\n");
 #else
 	/* We enable the possibility of machine suspend */
 	software_suspend_enabled = 1;
@@ -2647,7 +2970,7 @@
 	return 0;
 }
 
-static int __init software_noresume(char *str)
+static int __init noresume_setup(char *str)
 {
 	resume_status = NORESUME;
 	if (!resume_status) {
@@ -2659,7 +2982,7 @@
 
 __setup("resume=", resume_setup);
 __setup("swsusp_dbg=", swsusp_dbg_setup);
-__setup("noresume", software_noresume);
+__setup("noresume", noresume_setup);
 
 EXPORT_SYMBOL(software_suspend_pending);
 EXPORT_SYMBOL(swsusp_state);
diff -ruN beta19-original/kernel/sysctl.c beta19-19/kernel/sysctl.c
--- beta19-original/kernel/sysctl.c	2003-03-21 11:57:58.000000000 +1200
+++ beta19-19/kernel/sysctl.c	2003-03-21 20:31:38.000000000 +1200
@@ -205,7 +205,7 @@
 	{KERN_CTLALTDEL, "ctrl-alt-del", &C_A_D, sizeof(int),
 	 0644, NULL, &proc_dointvec},
 #ifdef CONFIG_SOFTWARE_SUSPEND
-	{KERN_SWSUSP, "swsusp", &swsusp_state, 5*sizeof(int),
+	{KERN_SWSUSP, "swsusp", &swsusp_state, 6*sizeof(int),
 	 0644, NULL, &proc_dointvec},
 #endif
 	{KERN_PRINTK, "printk", &console_loglevel, 4*sizeof(int),
diff -ruN beta19-original/mm/page_alloc.c beta19-19/mm/page_alloc.c
--- beta19-original/mm/page_alloc.c	2003-03-04 13:09:45.000000000 +1300
+++ beta19-19/mm/page_alloc.c	2003-03-29 18:52:49.000000000 +1200
@@ -394,7 +394,7 @@
 		loopcount++;
 		//if(!(loopcount%10))
 		//	printk("_");
-		if(loopcount > 800)
+		if(loopcount > 5)
 			return NULL;
 	}
 #endif
diff -ruN beta19-original/mm/page_io.c beta19-19/mm/page_io.c
--- beta19-original/mm/page_io.c	2003-03-04 13:09:45.000000000 +1300
+++ beta19-19/mm/page_io.c	2003-03-24 16:17:02.000000000 +1200
@@ -33,7 +33,7 @@
  * that shared pages stay shared while being swapped.
  */
 
-static int rw_swap_page_base(int rw, swp_entry_t entry, struct page *page)
+int rw_swap_page_base(int rw, swp_entry_t entry, struct page *page)
 {
 	unsigned long offset;
 	int zones[PAGE_SIZE/512];
diff -ruN beta19-original/mm/swapfile.c beta19-19/mm/swapfile.c
--- beta19-original/mm/swapfile.c	2003-03-04 13:09:02.000000000 +1300
+++ beta19-19/mm/swapfile.c	2003-03-29 17:45:48.000000000 +1200
@@ -14,6 +14,7 @@
 #include <linux/vmalloc.h>
 #include <linux/pagemap.h>
 #include <linux/shm.h>
+#include <linux/suspend-debug.h>
 
 #include <asm/pgtable.h>
 
@@ -141,6 +142,8 @@
 	}
 out:
 	swap_list_unlock();
+	if (suspend_task && (currentstage != STAGE_EAT_MEMORY))
+		PRINTNOLOG(SUSPEND_VERBOSE, 0, "[Get SWAP %lx]", entry.val);
 	return entry;
 }
 
@@ -202,7 +205,9 @@
 			if (offset > p->highest_bit)
 				p->highest_bit = offset;
 			nr_swap_pages++;
-		}
+		} else
+			if (suspend_task && (currentstage != STAGE_EAT_MEMORY))
+				PRINTK(SUSPEND_VERBOSE, "[Free SWAP %x/%lx entry's count not zero!]\n", p->swap_device, offset);
 	}
 	return count;
 }
@@ -217,6 +222,8 @@
 
 	p = swap_info_get(entry);
 	if (p) {
+		if ((suspend_task) && (currentstage != STAGE_EAT_MEMORY))
+			PRINTNOLOG(SUSPEND_VERBOSE, 0, "[Free SWAP %lx]", entry);
 		swap_entry_free(p, SWP_OFFSET(entry));
 		swap_info_put(p);
 	}
diff -ruN beta19-original/mm/vmscan.c beta19-19/mm/vmscan.c
--- beta19-original/mm/vmscan.c	2003-03-04 13:10:03.000000000 +1300
+++ beta19-19/mm/vmscan.c	2003-03-31 16:06:35.000000000 +1200
@@ -613,6 +613,38 @@
 	return 0;
 }
 
+int try_to_free_pages_zone_swsusp(zone_t *classzone, unsigned int gfp_mask, int priority, int nr_pages)
+{
+	gfp_mask = pf_gfp_mask(gfp_mask);
+	do {
+		nr_pages = shrink_caches(classzone, priority, gfp_mask, nr_pages);
+		if (nr_pages <= 0)
+			return 1;
+	} while (--priority);
+
+	/* No out of memory call for us! */
+	return 0;
+}
+
+int try_to_free_pages_swsusp(unsigned int gfp_mask, int amount_needed)
+{
+	pg_data_t *pgdat;
+	zonelist_t *zonelist;
+	unsigned long pf_free_pages;
+	int error = 0;
+
+	pf_free_pages = current->flags & PF_FREE_PAGES;
+	current->flags &= ~PF_FREE_PAGES;
+
+	for_each_pgdat(pgdat) {
+		zonelist = pgdat->node_zonelists + (gfp_mask & GFP_ZONEMASK);
+		error |= try_to_free_pages_zone_swsusp(zonelist->zones[0], gfp_mask, 1, amount_needed);
+	}
+
+	current->flags |= pf_free_pages;
+	return error;
+}
+
 int try_to_free_pages(unsigned int gfp_mask)
 {
 	pg_data_t *pgdat;
@@ -786,17 +818,11 @@
 }
 
 #ifdef CONFIG_SOFTWARE_SUSPEND
-void free_suspend_cache_page(void)
+void free_suspend_cache_page(struct page * page)
 {
-	struct page * page;
-
-	page = last_suspend_cache_page;
-	
 	if (!page)
 		return;
 
-	last_suspend_cache_page = NULL;
-
 	spin_lock(&pagemap_lru_lock);
 
 	if (unlikely(!PageLRU(page)))
