diff -ruNp 832-core-old/kernel/power/suspend2_core/suspend.c 832-core-new/kernel/power/suspend2_core/suspend.c
--- 832-core-old/kernel/power/suspend2_core/suspend.c	1970-01-01 10:00:00.000000000 +1000
+++ 832-core-new/kernel/power/suspend2_core/suspend.c	2004-12-24 12:45:44.000000000 +1100
@@ -0,0 +1,1321 @@
+/*
+ * kernel/power/suspend2.c
+ *
+ * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu>
+ * Copyright (C) 1998,2001,2002 Pavel Machek <pavel@suse.cz>
+ * Copyright (C) 2002-2003 Florent Chabaud <fchabaud@free.fr>
+ * Copyright (C) 2002-2004 Nigel Cunningham <ncunningham@linuxmail.org>
+ *
+ * This file is released under the GPLv2.
+ *
+ * This file is to realize architecture-independent
+ * machine suspend feature using pretty near only high-level routines
+ *
+ * We'd like to thank the following people for their work:
+ * 
+ * Pavel Machek <pavel@ucw.cz>:
+ * Modifications, defectiveness pointing, being with Gabor at the very beginning,
+ * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17.
+ *
+ * Steve Doddi <dirk@loth.demon.co.uk>: 
+ * Support the possibility of hardware state restoring.
+ *
+ * Raph <grey.havens@earthling.net>:
+ * Support for preserving states of network devices and virtual console
+ * (including X and svgatextmode)
+ *
+ * Kurt Garloff <garloff@suse.de>:
+ * Straightened the critical function in order to prevent compilers from
+ * playing tricks with local variables.
+ *
+ * Andreas Mohr <a.mohr@mailto.de>
+ *
+ * Alex Badea <vampire@go.ro>:
+ * Fixed runaway init
+ *
+ * Jeff Snyder <je4d@pobox.com>
+ * ACPI patch
+ *
+ * Nathan Friess <natmanz@shaw.ca>
+ * Some patches.
+ *
+ * Michael Frank <mhf@linuxmail.org>
+ * Extensive testing and help with improving stability.
+ *
+ * Variable definitions which are needed if PM is enabled but 
+ * SOFTWARE_SUSPEND is disabled are found near the top of process.c.
+ */
+
+#define SUSPEND_MAIN_C
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/version.h>
+#include <linux/highmem.h>
+#include <asm/uaccess.h>
+
+#include "version.h"
+#include "suspend.h"
+#include "driver_model.h"
+#include "../plugins.h"
+#include "../proc.h"
+#include "pageflags.h"
+#include "debug.h"
+
+#ifdef  CONFIG_X86
+#include <asm/i387.h> /* for kernel_fpu_end */
+#endif
+
+#ifdef CONFIG_SMP
+static void ensure_on_processor_zero(void)
+{
+	set_cpus_allowed(current, cpumask_of_cpu(0));
+	BUG_ON(smp_processor_id() != 0);
+}
+#else
+#define ensure_on_processor_zero() do { } while(0)
+#endif
+
+/* -------------------------------------------------------------------------- */
+
+static int suspend_version_specific_initialise(void)
+{
+
+	save_avenrun_values();
+
+	PRINTFREEMEM("after draining local pages");
+	suspend_store_free_mem(SUSPEND_FREE_DRAIN_PCP, 0);
+
+	cond_show_pcp_lists();
+
+	return suspend_drivers_init();
+}
+
+static void suspend_version_specific_cleanup(void)
+{
+	restore_avenrun_values();
+
+	suspend_drivers_cleanup();
+}
+/* Variables to be preserved over suspend */
+int pageset1_sizelow = 0, pageset2_sizelow = 0;
+
+unsigned long orig_mem_free = 0;
+
+extern void do_suspend2_lowlevel(int resume);
+extern unsigned long header_storage_for_plugins(void);
+extern int suspend_initialise_plugin_lists(void);
+extern void suspend_relinquish_console(void);
+extern volatile int suspend_io_time[2][2];
+int read_primary_suspend_image(void);
+extern void display_nosave_pages(void);
+extern int num_writers;
+
+extern void suspend_console_proc_init(void);
+extern void suspend_console_proc_exit(void);
+extern int suspend2_prepare_console(void);
+extern void suspend2_cleanup_console(void);
+
+char * debug_info_buffer;
+
+int image_size_limit = 0;
+int max_async_ios = 128;
+
+/* Pagedir.c */
+extern void copy_pageset1(void);
+extern void free_pagedir_data(void);
+
+/* Prepare_image.c */
+
+extern int prepare_image(void);
+
+unsigned long forced_ps1_size = 0, forced_ps2_size = 0;
+
+/* proc.c */
+extern int suspend_cleanup_proc(void);
+
+extern int suspend2_register_core(struct suspend2_core_ops * ops_pointer);
+extern void suspend2_unregister_core(void);
+
+/*
+ * save_image
+ * Result code (int): Zero on success, non zero on failure.
+ * Functionality    : High level routine which performs the steps necessary
+ *                    to prepare and save the image after preparatory steps
+ *                    have been taken.
+ * Key Assumptions  : Processes frozen, sufficient memory available, drivers
+ *                    suspended.
+ * Called from      : do_suspend2_suspend_2
+ */
+extern int write_pageset(struct pagedir * pagedir, int whichtowrite);
+extern int write_image_header(void);
+extern int read_secondary_pagedir(int overwrittenpagesonly);
+
+static int save_image(void)
+{
+	int temp_result;
+
+	if (RAM_TO_SUSPEND > max_mapnr) {
+		prepare_status(1, 1,
+			"Couldn't get enough free pages, on %ld pages short",
+			 RAM_TO_SUSPEND - max_mapnr);
+		goto abort_saving;
+	}
+	
+	suspend_message(SUSPEND_ANY_SECTION, SUSPEND_LOW, 1,
+		" - Final values: %d and %d.\n",
+		pageset1_size, 
+		pageset2_size);
+
+	check_shift_keys(1, "About to write pagedir2.");
+
+	temp_result = write_pageset(&pagedir2, 2);
+	
+	if (temp_result == -1 || TEST_RESULT_STATE(SUSPEND_ABORTED))
+		goto abort_saving;
+
+	check_shift_keys(1, "About to copy pageset 1.");
+
+	prepare_status(1, 0, "Suspending drivers.");
+	
+	do_suspend2_lowlevel(0);
+
+	return 0;
+abort_saving:
+	return -1;		
+}
+
+extern struct pageset_sizes_result recalculate_stats(void);
+
+int save_image_part1(void)
+{
+	int temp_result;
+	
+	recalculate_stats();
+
+	suspend_map_atomic_copy_pages();
+
+	suspend_checksum_calculate_checksums();
+	
+	BUG_ON(!irqs_disabled());
+
+	if (!TEST_ACTION_STATE(SUSPEND_TEST_FILTER_SPEED)) {
+		prepare_status(1, 0, "Doing atomic copy...");
+
+		copy_pageset1();
+	}
+
+	/*
+	 *  ----   FROM HERE ON, NEED TO REREAD PAGESET2 IF ABORTING!!! -----
+	 *  
+	 */
+	
+	prepare_status(1, 0, "Unmapping pages");
+
+	suspend_unmap_atomic_copy_pages();
+
+	/* 
+	 * Other processors have waited for me to make the atomic copy of the 
+	 * kernel
+	 */
+
+	prepare_status(1, 0, "Thawing other processors");
+
+	smp_continue();
+	
+#ifdef CONFIG_X86
+	kernel_fpu_end();
+#endif
+
+#ifdef CONFIG_PREEMPT
+	preempt_enable_no_resched();
+#endif
+
+	suspend_drivers_resume(SUSPEND_DRIVERS_IRQS_DISABLED);
+	
+	local_irq_enable();
+
+	suspend_drivers_resume(SUSPEND_DRIVERS_IRQS_ENABLED);
+
+	update_status(pageset2_size, pageset1_size + pageset2_size, NULL);
+	
+	if (TEST_RESULT_STATE(SUSPEND_ABORTED))
+		goto abort_reloading_pagedir_two;
+
+	check_shift_keys(1, "About to write pageset1.");
+
+	/*
+	 * End of critical section.
+	 */
+	
+	suspend_message(SUSPEND_ANY_SECTION, SUSPEND_LOW, 1,
+			"-- Writing pageset1\n");
+
+	temp_result = write_pageset(&pagedir1, 1);
+
+	if (TEST_ACTION_STATE(SUSPEND_TEST_FILTER_SPEED)) {
+		/* We didn't overwrite any memory, so no reread needs to be done. */
+		return -1;
+	}
+
+	if (temp_result == -1 || TEST_RESULT_STATE(SUSPEND_ABORTED))
+		goto abort_reloading_pagedir_two;
+
+	check_shift_keys(1, "About to write header.");
+
+	if (TEST_RESULT_STATE(SUSPEND_ABORTED))
+		goto abort_reloading_pagedir_two;
+
+	temp_result = write_image_header();
+
+	if (temp_result || (TEST_RESULT_STATE(SUSPEND_ABORTED)))
+		goto abort_reloading_pagedir_two;
+
+	check_shift_keys(1, "About to power down or reboot.");
+
+	return 0;
+
+abort_reloading_pagedir_two:
+	temp_result = read_secondary_pagedir(1);
+
+	/* If that failed, we're sunk. Panic! */
+	if (temp_result)
+		panic("Attempt to reload pagedir 2 while aborting "
+				"a suspend failed.");
+
+	return -1;		
+
+}
+
+/*
+ * do_suspend2_resume_1
+ * Functionality   : Preparatory steps for copying the original kernel back.
+ * Called From     : do_suspend2_lowlevel
+ */
+
+static void do_suspend2_resume_1(void)
+{
+	suspend_message(SUSPEND_ANY_SECTION, SUSPEND_LOW, 1,
+			name_suspend "About to copy pageset1 back...\n");
+
+	suspend_drivers_suspend(SUSPEND_DRIVERS_IRQS_ENABLED);
+	local_irq_disable(); /* irqs might have been re-enabled on us */
+
+	suspend_drivers_suspend(SUSPEND_DRIVERS_IRQS_DISABLED);
+	local_irq_enable();
+
+	suspend_map_atomic_copy_pages();
+
+	/* Get other cpus ready to restore their original contexts */
+	smp_suspend();
+
+	local_irq_disable();
+
+#ifdef CONFIG_PREEMPT
+	preempt_disable();
+#endif
+
+	barrier();
+	mb();
+
+	MDELAY(2000);
+}
+
+/*
+ * do_suspend2_resume_2
+ * Functionality   : Steps taken after copying back the original kernel at
+ *                   resume.
+ * Key Assumptions : Will be able to read back secondary pagedir (if 
+ *                   applicable).
+ * Called From     : do_suspend2_lowlevel
+ */
+
+static void do_suspend2_resume_2(void)
+{
+	set_suspend_state(SUSPEND_NOW_RESUMING);
+	set_suspend_state(SUSPEND_PAGESET2_NOT_LOADED);
+
+	suspend_unmap_atomic_copy_pages();
+
+#ifdef CONFIG_PREEMPT
+	preempt_enable();
+#endif
+
+	local_irq_disable();
+	suspend_drivers_resume(SUSPEND_DRIVERS_IRQS_DISABLED);
+	local_irq_enable();
+
+	suspend_drivers_resume(SUSPEND_DRIVERS_IRQS_ENABLED);
+
+	suspend_post_restore_redraw();
+
+	check_shift_keys(1, "About to reload secondary pagedir.");
+
+	read_secondary_pagedir(0);
+	clear_suspend_state(SUSPEND_PAGESET2_NOT_LOADED);
+	
+	suspend_checksum_print_differences();
+
+	prepare_status(0, 0, "Cleaning up...");
+}
+
+/*
+ * do_suspend2_suspend_1
+ * Functionality   : Steps taken prior to saving CPU state and the image
+ *                   itself.
+ * Called From     : do_suspend2_lowlevel
+ */
+
+static void do_suspend2_suspend_1(void)
+{
+	/* Save other cpu contexts */
+	smp_suspend();
+
+	suspend_drivers_suspend(SUSPEND_DRIVERS_IRQS_ENABLED);
+
+	mb();
+	barrier();
+
+#ifdef CONFIG_PREEMPT
+	preempt_disable();
+#endif
+	local_irq_disable();
+
+	suspend_drivers_suspend(SUSPEND_DRIVERS_IRQS_DISABLED);
+}
+
+/*
+ * do_suspend2_suspend_2
+ * Functionality   : Steps taken after saving CPU state to save the
+ *                   image and powerdown/reboot or recover on failure.
+ * Key Assumptions : save_image returns zero on success; otherwise we need to
+ *                   clean up and exit. The state on exiting this routine 
+ *                   should be essentially the same as if we have suspended,
+ *                   resumed and reached the end of do_suspend2_resume_2.
+ * Called From     : do_suspend2_lowlevel
+ */
+extern void suspend_power_down(void);
+
+static void do_suspend2_suspend_2(void)
+{
+	if (!save_image_part1())
+		suspend_power_down();
+
+	if (suspend_powerdown_method == 3) {
+		int temp_result;
+
+		temp_result = read_secondary_pagedir(1);
+
+		/* If that failed, we're sunk. Panic! */
+		if (temp_result)
+			panic("Attempt to reload pagedir 2 failed. Try rebooting.");
+	}
+
+	if (!TEST_RESULT_STATE(SUSPEND_ABORT_REQUESTED) &&
+	    !TEST_ACTION_STATE(SUSPEND_TEST_FILTER_SPEED) &&
+	    suspend_powerdown_method != 3)
+		printk(KERN_EMERG name_suspend
+			"Suspend failed, trying to recover...\n");
+	MDELAY(1000);
+
+	barrier();
+	mb();
+}
+
+static inline void lru_check_page(struct page * page)
+{
+	if (!PageLRU(page))
+		printk("Page %p/%p in inactivelist but not marked LRU.\n",
+			page, page_address(page));
+}
+
+/* get_debug_info
+ * Functionality:	Store debug info in a buffer.
+ * Called from:		suspend_try_suspend.
+ */
+
+#define SNPRINTF(a...) 	len += suspend_snprintf(debug_info_buffer + len, \
+		PAGE_SIZE - len - 1, ## a)
+
+static int get_suspend_debug_info(void)
+{
+	int len = 0;
+	if (!debug_info_buffer) {
+		debug_info_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+		if (!debug_info_buffer) {
+			printk("Error! Unable to allocate buffer for"
+					"software suspend debug info.\n");
+			return 0;
+		}
+	}
+
+	SNPRINTF("Please include the following information in bug reports:\n");
+	SNPRINTF("- SUSPEND core   : %s\n", SUSPEND_CORE_VERSION);
+	SNPRINTF("- Kernel Version : %s\n", UTS_RELEASE);
+	SNPRINTF("- Compiler vers. : %d.%d\n", __GNUC__, __GNUC_MINOR__);
+#ifdef CONFIG_MODULES
+	SNPRINTF("- Modules loaded : ");
+	{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+		struct module *this_mod;
+		extern struct module *module_list;
+		this_mod = module_list;
+		while (this_mod) {
+			if (this_mod->name)
+				SNPRINTF("%s ", this_mod->name);
+			this_mod = this_mod->next;
+		}
+#else
+		extern int print_module_list_to_buffer(char * buffer, int size);
+		len+= print_module_list_to_buffer(debug_info_buffer + len,
+				PAGE_SIZE - len - 1);
+#endif
+	}
+	SNPRINTF("\n");
+#else
+	SNPRINTF("- No module support.\n");
+#endif
+	SNPRINTF("- Attempt number : %d\n", nr_suspends);
+	if (num_extent_pages)
+		SNPRINTF("- Pageset sizes  : %d (%d low) and %d (%d low).\n",
+			pagedir1.lastpageset_size, 
+			pageset1_sizelow,
+			pagedir2.lastpageset_size, 
+			pageset2_sizelow);
+	SNPRINTF("- Parameters     : %ld %ld %ld %d %d %d %ld\n",
+			suspend_result,
+			suspend_action,
+			suspend_debug_state,
+			suspend_default_console_level,
+			image_size_limit,
+			max_async_ios,
+			suspend_powerdown_method);
+	if (num_extent_pages)
+		SNPRINTF("- Calculations   : Image size: %lu. "
+			"Ram to suspend: %ld.\n",
+			STORAGE_NEEDED(1), RAM_TO_SUSPEND);
+	SNPRINTF("- Limits         : %lu pages RAM. Initial boot: %lu.\n",
+		max_mapnr, orig_mem_free);
+	SNPRINTF("- Overall expected compression percentage: %d.\n",
+			100 - expected_compression_ratio());
+	len+= print_plugin_debug_info(debug_info_buffer + len, 
+			PAGE_SIZE - len - 1);
+#ifdef CONFIG_SOFTWARE_SUSPEND_DEBUG
+	SNPRINTF("- Debugging compiled in.\n");
+#endif
+#ifdef CONFIG_PREEMPT
+	SNPRINTF("- Preemptive kernel.\n");
+#endif
+#ifdef CONFIG_SMP
+	SNPRINTF("- SMP kernel.\n");
+#endif
+#ifdef CONFIG_HIGHMEM
+	SNPRINTF("- Highmem Support.\n");
+#endif
+	if (num_extent_pages)
+		SNPRINTF("- Max extents used: %d extents in %d pages.\n",
+			max_extents_used, num_extent_pages);
+	if (suspend_io_time[0][1]) {
+		SNPRINTF("- I/O speed: Write %d MB/s",
+			(MB((unsigned long) suspend_io_time[0][0]) * HZ /
+			 suspend_io_time[0][1]));
+		if (suspend_io_time[1][1])
+			SNPRINTF(", Read %d MB/s",
+				(MB((unsigned long) suspend_io_time[1][0]) * HZ /
+				 suspend_io_time[1][1]));
+		SNPRINTF(".\n");
+	}
+	else if (num_extent_pages)
+		SNPRINTF("- Suspend cancelled. No I/O speed stats.\n");
+
+	return len;
+}
+
+extern int PageInPagedir(struct pagedir * p, struct page * page);
+static unsigned long display_metadata_page;
+
+static char * state_string[3] = { "Source", "Destination", "Dest/Allocd" };
+
+void __display_metadata_state(int pagedir, int state)
+{
+	int i;
+
+	if (!state)
+		return;
+
+	printk("[ Pagedir %d:", pagedir);
+	for (i=0; i < 3; i++)
+		if (state & (1 << i))
+			printk("%s ", state_string[i]);
+	printk("]");
+}
+
+void display_metadata_state(struct page * page)
+{
+	__display_metadata_state(1, PageInPagedir(&pagedir1, page));
+	__display_metadata_state(2, PageInPagedir(&pagedir2, page));
+	if (PageNosave(page))
+		printk("[ NoSave ]");
+	if (PageReserved(page))
+		printk("[ Reserved ]");
+	if (PageHighMem(page))
+		printk("[ Highmem ]");
+}
+
+void proc_display_metadata_state(void)
+{
+	printk("Page number %lu. Struct page at %p, virt address %p:",
+			display_metadata_page,
+			pfn_to_page(display_metadata_page),
+			page_address(pfn_to_page(display_metadata_page)));
+	display_metadata_state(pfn_to_page(display_metadata_page));
+	printk("\n");
+}
+
+/*
+ * debuginfo_read_proc
+ * Functionality   : Displays information that may be helpful in debugging
+ *                   software suspend.
+ */
+int debuginfo_read_proc(char * page, char ** start, off_t off, int count,
+		int *eof, void *data)
+{
+	int info_len, copy_len;
+
+	initialise_suspend_plugins();
+	info_len = get_suspend_debug_info();
+	cleanup_suspend_plugins();
+
+	copy_len = min(info_len - (int) off, count);
+	if (copy_len < 0)
+		copy_len = 0;
+
+	if (copy_len) {
+		memcpy(page, debug_info_buffer + off, copy_len);
+		*start = page;
+	} 
+
+	if (copy_len + off == info_len)
+		*eof = 1;
+
+	free_pages((unsigned long) debug_info_buffer, 0);
+	debug_info_buffer = NULL;
+	return copy_len;
+}
+
+static int get_suspend_debug_info(void);
+
+static int allocate_bitmaps(void)
+{
+	suspend_message(SUSPEND_MEMORY, SUSPEND_VERBOSE, 1,
+			"Allocating in_use_map\n");
+	if (allocate_local_pageflags(&in_use_map))
+		return 1;
+	
+	suspend_store_free_mem(SUSPEND_FREE_IN_USE_MAP, 0);
+	PRINTFREEMEM("after allocating in_use_map");
+	
+	if (allocate_local_pageflags(&pageset1_map))
+		return 1;
+
+	suspend_store_free_mem(SUSPEND_FREE_PS1_MAP, 0);
+	PRINTFREEMEM("after allocating pageset1 map");
+	
+	if (allocate_local_pageflags(&pageset1_copy_map))
+		return 1;
+
+	suspend_store_free_mem(SUSPEND_FREE_ALLOCD_PAGES_MAP, 0);
+	PRINTFREEMEM("after allocating allocd pages map");
+	
+	if (allocate_local_pageflags(&allocd_pages_map))
+		return 1;
+
+	suspend_store_free_mem(SUSPEND_FREE_PS1COPY_MAP, 0);
+	PRINTFREEMEM("after allocating pageset1copy map");
+	
+	if (allocate_local_pageflags(&pageset2_map))
+		return 1;
+
+	suspend_store_free_mem(SUSPEND_FREE_PS2_MAP, 0);
+	PRINTFREEMEM("after allocating pageset2 map");
+	
+#ifdef CONFIG_DEBUG_PAGEALLOC
+	if (allocate_local_pageflags(&unmap_map))
+		return 1;
+
+	suspend_store_free_mem(SUSPEND_FREE_UNMAP_MAP, 0);
+	PRINTFREEMEM("after allocating unmap map");
+#endif	
+	
+	suspend_store_free_mem(4, 0);
+	
+	return 0;
+}
+
+static void free_metadata(void)
+{
+	free_extents();
+	suspend_store_free_mem(SUSPEND_FREE_EXTENT_PAGES, 1);
+	PRINTFREEMEM("after freeing extents");
+
+	free_local_pageflags(&pageset1_map);
+	PRINTFREEMEM("after freeing pageset1 map");
+	suspend_store_free_mem(SUSPEND_FREE_PS1_MAP, 1);
+
+	free_local_pageflags(&pageset1_copy_map);
+	PRINTFREEMEM("after freeing pageset1core map");
+	suspend_store_free_mem(SUSPEND_FREE_PS1COPY_MAP, 1);
+
+	free_local_pageflags(&allocd_pages_map);
+	PRINTFREEMEM("after freeing allocd pages map");
+	suspend_store_free_mem(SUSPEND_FREE_ALLOCD_PAGES_MAP, 1);
+
+	free_local_pageflags(&pageset2_map);
+	PRINTFREEMEM("after freeing pageset2 map");
+	suspend_store_free_mem(SUSPEND_FREE_PS2_MAP, 1);
+
+	free_local_pageflags(&in_use_map);
+	PRINTFREEMEM("after freeing inuse map");
+	suspend_store_free_mem(SUSPEND_FREE_IN_USE_MAP, 1);
+}
+
+/*
+ * software_suspend_pending
+ * Functionality   : First level of code for software suspend invocations.
+ *                   Stores and restores load averages (to avoid a spike),
+ *                   allocates bitmaps, freezes processes and eats memory
+ *                   as required before suspending drivers and invoking
+ *                   the 'low level' code to save the state to disk.
+ *                   By the time we return from do_suspend2_lowlevel, we
+ *                   have either failed to save the image or successfully
+ *                   suspended and reloaded the image. The difference can
+ *                   be discerned by checking SUSPEND_ABORTED.
+ * Called From     : 
+ */
+
+void do_activate(void)
+{
+	int i;
+
+        /* Suspend always runs on processor 0 */ 
+	ensure_on_processor_zero();
+
+	display_nosave_pages();
+
+#ifdef CONFIG_SOFTWARE_SUSPEND_KEEP_IMAGE
+	if (TEST_RESULT_STATE(SUSPEND_KEPT_IMAGE)) {
+		if (TEST_ACTION_STATE(SUSPEND_KEEP_IMAGE)) {
+			printk("Image already stored:"
+				" powering down immediately.");
+			suspend_power_down();
+			return;	/* It might now, but just in case we're using S3*/
+		} else {
+			printk("Invalidating previous image.\n");
+			active_writer->ops.writer.invalidate_image();
+		}
+	}
+#endif
+
+	printk(name_suspend "Initiating a software suspend cycle.\n");
+	set_suspend_state(SUSPEND_RUNNING);
+
+	max_extents_used = 0;
+	nr_suspends++;
+	clear_suspend_state(SUSPEND_NOW_RESUMING);
+	
+	suspend_io_time[0][0] = suspend_io_time[0][1] = suspend_io_time[1][0] =
+		suspend_io_time[1][1] = 0;
+
+	PRINTFREEMEM("at start of do_activate");
+	suspend_store_free_mem(SUSPEND_FREE_BASE, 0);
+
+	suspend2_prepare_console();
+
+	free_metadata();	/* We might have kept it */
+
+	if (suspend_version_specific_initialise())
+		goto out;
+
+	if (allocate_bitmaps())
+		goto out;
+	
+	PRINTFREEMEM("after allocating bitmaps");
+
+	display_nosave_pages();
+
+	if (initialise_suspend_plugins())
+		goto out;
+
+	PRINTFREEMEM("after initialising plugins");
+	suspend_store_free_mem(SUSPEND_FREE_INIT_PLUGINS, 0);
+
+	/* Free up memory if necessary */
+	suspend_message(SUSPEND_ANY_SECTION, SUSPEND_VERBOSE, 1,
+			"Preparing image.\n");
+	if (prepare_image() || TEST_RESULT_STATE(SUSPEND_ABORTED))
+		goto out;
+
+	PRINTFREEMEM("after preparing image");
+
+	if (TEST_ACTION_STATE(SUSPEND_FREEZER_TEST))
+		goto out;
+
+	display_nosave_pages();
+
+	if (!TEST_RESULT_STATE(SUSPEND_ABORTED)) {
+		prepare_status(1, 0, "Starting to save the image..");
+		save_image();
+	}
+	
+out:
+	PRINTFREEMEM("at 'out'");
+
+	i = get_suspend_debug_info();
+
+	suspend_store_free_mem(SUSPEND_FREE_DEBUG_INFO, 0);
+
+	free_pagedir_data();
+	PRINTFREEMEM("after freeing pagedir data");
+	
+#ifdef CONFIG_SOFTWARE_SUSPEND_KEEP_IMAGE
+	if (TEST_ACTION_STATE(SUSPEND_KEEP_IMAGE) &&
+	    !TEST_ACTION_STATE(SUSPEND_ABORTED)) {
+		suspend_message(SUSPEND_ANY_SECTION, SUSPEND_LOW, 1,
+			name_suspend "Not invalidating the image due "
+			"to Keep Image being enabled.\n");
+		SET_RESULT_STATE(SUSPEND_KEPT_IMAGE);
+	} else
+#endif
+		active_writer->ops.writer.invalidate_image();
+
+	if (!TEST_ACTION_STATE(SUSPEND_KEEP_METADATA))
+		free_metadata();
+
+#ifdef CONFIG_DEBUG_PAGE_ALLOC
+	free_local_pageflags(&unmap_map);
+	PRINTFREEMEM("after freeing unmap map");
+	suspend_store_free_mem(SUSPEND_UNMAP_MAP, 1);
+#endif
+
+	if (debug_info_buffer) {
+		/* Printk can only handle 1023 bytes, including
+		 * its level mangling. */
+		for (i = 0; i < 3; i++)
+			printk("%s", debug_info_buffer + (1023 * i));
+		free_pages((unsigned long) debug_info_buffer, 0);
+		debug_info_buffer = NULL;
+	}
+
+	PRINTFREEMEM("after freeing debug info buffer");
+	suspend_store_free_mem(SUSPEND_FREE_DEBUG_INFO, 1);
+	
+	cleanup_suspend_plugins();
+
+	PRINTFREEMEM("after cleaning up suspend plugins");
+	suspend_store_free_mem(SUSPEND_FREE_INIT_PLUGINS, 1);
+	
+	suspend_version_specific_cleanup();
+
+	display_nosave_pages();
+
+	thaw_processes(FREEZER_ALL_THREADS);
+
+	PRINTFREEMEM("after thawing processes");
+	suspend_store_free_mem(SUSPEND_FREE_FREEZER, 1);
+
+	suspend_store_free_mem(SUSPEND_FREE_BASE, 1);
+
+	suspend_display_free_mem();
+
+	clear_suspend_state(SUSPEND_LRU_FREEZE);
+	clear_suspend_state(SUSPEND_RUNNING);
+	PRINTFREEMEM("at end of do_activate");
+	suspend2_cleanup_console();
+}
+
+int attempt_to_parse_resume_device(void)
+{
+	struct list_head *writer;
+	struct suspend_plugin_ops * this_writer;
+	int result = 0;
+	mm_segment_t oldfs;
+
+	oldfs = get_fs(); set_fs(KERNEL_DS);
+
+	active_writer = NULL;
+	clear_suspend_state(SUSPEND_RESUME_DEVICE_OK);
+	set_suspend_state(SUSPEND_DISABLED);
+
+	if (!num_writers) {
+		printk(name_suspend "No writers have been registered.\n");
+		goto out;
+	}
+	
+	if (!resume2_file[0]) {
+		result = -EINVAL;
+		goto out;
+	}
+
+	list_for_each(writer, &suspend_writers) {
+		this_writer = list_entry(writer, struct suspend_plugin_ops,
+				ops.writer.writer_list);
+
+		/* 
+		 * Not sure why you'd want to disable a writer, but
+		 * we should honour the flag if we're providing it
+		 */
+		if (this_writer->disabled) {
+			printk(name_suspend
+					"Writer '%s' is disabled. Ignoring it.\n",
+					this_writer->name);
+			continue;
+		}
+
+		result = this_writer->ops.writer.parse_image_location(
+				resume2_file, (num_writers == 1));
+
+		switch (result) {
+			case -EINVAL:
+				/* 
+				 * For this writer, but not a valid 
+				 * configuration
+				 */
+
+				printk(name_suspend
+					"Not able to successfully parse this "
+					"resume device. Suspending disabled.\n");
+				goto out;
+
+			case 0:
+				/*
+				 * For this writer and valid.
+				 */
+
+				active_writer = this_writer;
+
+				set_suspend_state(SUSPEND_RESUME_DEVICE_OK);
+				clear_suspend_state(SUSPEND_DISABLED);
+				printk(name_suspend "Suspending enabled.\n");
+				goto out;
+
+			case 1:
+				/*
+				 * Not for this writer. Try the next one.
+				 */
+
+				break;
+		}
+	}
+	printk(name_suspend "No matching writer found. Suspending disabled.\n");
+	result = -EINVAL;
+out:
+	clear_suspend_state(SUSPEND_RUNNING);
+	set_fs(oldfs);
+	return result;
+}
+
+/* 
+ * 
+ */
+
+static void __suspend2_verify_checksums(void)
+{
+	if (checksum_plugin)
+		checksum_plugin->ops.checksum.check_checksums();
+}
+
+extern int initialise_suspend_plugins(void);
+extern void cleanup_suspend_plugins(void);
+static char suspend_core_version[] = SUSPEND_CORE_VERSION;
+
+static int resume2_write_proc(void)
+{
+	mm_segment_t	oldfs;
+
+	oldfs = get_fs(); set_fs(KERNEL_DS);
+	initialise_suspend_plugins();
+	attempt_to_parse_resume_device();
+	cleanup_suspend_plugins();
+	set_fs(oldfs);
+	return 0;
+}
+
+/*
+ * Core proc entries that aren't built in.
+ *
+ * This array contains entries that are automatically registered at
+ * boot. Plugins and the console code register their own entries separately.
+ */
+
+static struct suspend_proc_data proc_params[] = {
+	{ .filename			= "debug_info",
+	  .permissions			= PROC_READONLY,
+	  .type				= SUSPEND_PROC_DATA_CUSTOM,
+	  .data = {
+		  .special = {
+			  .read_proc	= debuginfo_read_proc,
+		  }
+	  }
+	},
+	
+	{ .filename			= "freeze_timers",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_BIT,
+	  .data = {
+		  .bit = {
+			  .bit_vector	= &suspend_action,
+			  .bit		= SUSPEND_FREEZE_TIMERS,
+		  }
+	  }
+	},
+
+	{ .filename			= "image_size_limit",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_INTEGER,
+	  .data = {
+		  .integer = {
+			  .variable	= &image_size_limit,
+			  .minimum	= -2,
+			  .maximum	= 32767,
+		  }
+	  }
+	},
+
+	{ .filename			= "last_result",
+	  .permissions			= PROC_READONLY,
+	  .type				= SUSPEND_PROC_DATA_UL,
+	  .data = {
+		  .ul = {
+			  .variable	=  &suspend_result,
+		  }
+	  }
+	},
+	
+	{ .filename			= "reboot",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_BIT,
+	  .data = {
+		  .bit = {
+			  .bit_vector	= &suspend_action,
+			  .bit		= SUSPEND_REBOOT,
+		  }
+	  }
+	},
+	  
+	{ .filename			= "resume2",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_STRING,
+	  .data = {
+		  .string = {
+			  .variable	= resume2_file,
+			  .max_length	= 255,
+		  }
+	  },
+	  .write_proc	= resume2_write_proc,
+	},
+
+
+	{ .filename			= "version",
+	  .permissions			= PROC_READONLY,
+	  .type				= SUSPEND_PROC_DATA_STRING,
+	  .data = {
+		  .string = {
+			  .variable	= suspend_core_version,
+		  }
+	  }
+	},
+
+#ifdef CONFIG_SOFTWARE_SUSPEND_DEBUG
+	{ .filename			= "freezer_test",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_BIT,
+	  .data = {
+		  .bit = {
+			  .bit_vector	= &suspend_action,
+			  .bit		= SUSPEND_FREEZER_TEST,
+		  }
+	  }
+	},
+
+	{ .filename			= "keep_metadata",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_BIT,
+	  .data = {
+		  .bit = {
+			  .bit_vector	= &suspend_action,
+			  .bit		= SUSPEND_KEEP_METADATA,
+		  }
+	  }
+	},
+
+	{ .filename			= "test_filter_speed",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_BIT,
+	  .data = {
+		  .bit = {
+			  .bit_vector	= &suspend_action,
+			  .bit		= SUSPEND_TEST_FILTER_SPEED,
+		  }
+	  }
+	},
+
+	{ .filename			= "slow",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_BIT,
+	  .data = {
+		  .bit = {
+			  .bit_vector	= &suspend_action,
+			  .bit		= SUSPEND_SLOW,
+		  }
+	  }
+	},
+	
+	{ .filename			= "display_metadata_page",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_UL,
+	  .data = {
+		  .ul = {
+			  .variable	= &display_metadata_page,
+		  }
+	  }
+	},
+#endif
+	  
+	{ .filename			= "forced_pageset1_size",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_UL,
+	  .data = {
+		  .ul = {
+			  .variable	= &forced_ps1_size,
+		  }
+	  }
+	},
+
+	{ .filename			= "forced_pageset2_size",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_UL,
+	  .data = {
+		  .ul = {
+			  .variable	= &forced_ps2_size,
+		  }
+	  }
+	},
+
+#if defined(CONFIG_ACPI)
+	{ .filename			= "powerdown_method",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_UL,
+	  .data = {
+		  .ul = {
+			  .variable	= &suspend_powerdown_method,
+			  .minimum	= 3,
+			  .maximum	= 5,
+		  }
+	  }
+	},
+#endif
+
+#ifdef CONFIG_SOFTWARE_SUSPEND_KEEP_IMAGE
+	{ .filename			= "keep_image",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_BIT,
+	  .data = {
+		  .bit = {
+			  .bit_vector	= &suspend_action,
+			  .bit		= SUSPEND_KEEP_IMAGE,
+		  }
+	  }
+	},
+#endif
+};
+
+extern int debuginfo_read_proc(char * page, char ** start, off_t off, int count,
+		int *eof, void *data);
+
+/*
+ * Called from init kernel_thread.
+ * We check if we have an image and if so we try to resume.
+ * We also start ksuspendd if configuration looks right.
+ */
+
+extern int freeze_processes(int no_progress);
+
+static int do_resume(void)
+{
+	int ret = 0;
+	int read_image_result = 0;
+
+	/* Suspend always runs on processor 0 */
+	ensure_on_processor_zero();
+
+	if (sizeof(swp_entry_t) != sizeof(long)) {
+		printk(KERN_WARNING name_suspend
+			"The size of swp_entry_t != size of long. "
+			"Please report this!\n");
+		return ret;
+	}
+	
+	set_suspend_state(SUSPEND_RUNNING);
+
+	if (!resume2_file[0])
+		printk(KERN_WARNING name_suspend
+			"You need to use a resume2= command line parameter to "
+			"tell Software Suspend 2 where to look for an image.\n");
+
+	if (!(test_suspend_state(SUSPEND_RESUME_DEVICE_OK)))
+		attempt_to_parse_resume_device();
+
+	if (!(test_suspend_state(SUSPEND_RESUME_DEVICE_OK))) {
+		/* 
+		 * Without a usable storage device we can do nothing - 
+		 * even if noresume is given
+		 */
+
+		if (!num_writers)
+			printk(KERN_ALERT name_suspend
+				"No writers have been registered.\n");
+		else
+			printk(KERN_ALERT name_suspend
+				"Missing or invalid storage location "
+				"(resume2= parameter). Please correct and "
+				"rerun lilo (or equivalent) before "
+				"suspending.\n");
+		clear_suspend_state(SUSPEND_RUNNING);
+		return ret;
+	}
+
+	/* We enable the possibility of machine suspend */
+	orig_mem_free = real_nr_free_pages();
+
+	suspend_task = current->pid;
+
+	read_image_result = read_primary_suspend_image(); /* non fatal error ignored */
+
+	if (test_suspend_state(SUSPEND_NORESUME_SPECIFIED))
+		printk(KERN_WARNING name_suspend "Resuming disabled as requested.\n");
+
+	if (read_image_result) {
+		suspend_task = 0;
+		clear_suspend_state(SUSPEND_RUNNING);
+		return ret;
+	}
+
+	/* 
+	 * Ensure our suspend device tree is configured (2.6) as
+	 * at suspend time
+	 */
+	
+	suspend_version_specific_initialise();
+
+	freeze_processes(1);
+
+	prepare_status(0, 0,
+		"Copying original kernel back (no status - sensitive!)...");
+	
+	do_suspend2_lowlevel(1);
+	BUG();
+
+	return ret;
+}
+
+extern int suspend_plugin_keypress(unsigned int keycode);
+extern void request_abort_suspend(void);
+extern void schedule_suspend_message(int message_number);
+
+int suspend_keypress(unsigned int keycode)
+{
+	/* These keys work even if no output is enabled.
+	 * (To get this far, we must be suspending or resuming).
+	 */
+	switch (keycode) {
+		case 27:
+			/* Abort suspend */
+			if (TEST_ACTION_STATE(SUSPEND_CAN_CANCEL))
+				request_abort_suspend();
+			break;
+		case 114:
+			/* Otherwise, if R pressed, toggle rebooting */
+			suspend_action ^= (1 << SUSPEND_REBOOT);
+			schedule_suspend_message(2);
+			break;
+		default:
+			return suspend_plugin_keypress(keycode);
+	}
+	return 1;
+}
+
+extern void suspend_early_boot_message_plugins(void);
+extern void cleanup_finished_suspend_io(void);
+
+struct suspend2_core_ops core_ops = {
+	.do_suspend = do_activate,
+	.do_resume = do_resume,
+	.resume1 = do_suspend2_resume_1, 
+	.resume2 = do_suspend2_resume_2, 
+	.suspend1 = do_suspend2_suspend_1, 
+	.suspend2 = do_suspend2_suspend_2, 
+	.get_grabbed_pages = get_grabbed_pages,
+	.cleanup_finished_io = cleanup_finished_suspend_io,
+	.suspend_message = __suspend_message,
+	.update_status = update_status,
+	.prepare_status = prepare_status,
+	.schedule_message = schedule_suspend_message,
+	.early_boot_plugins = suspend_early_boot_message_plugins,
+	.keypress = suspend_keypress,
+	.verify_checksums = __suspend2_verify_checksums,
+};
+
+static struct proc_dir_entry *compat_parent;
+extern void suspend_core_proc_init(void);
+
+static __init int core_load(void)
+{
+	int i, numfiles = sizeof(proc_params) / sizeof(struct suspend_proc_data);
+
+	if (suspend2_register_core(&core_ops))
+		return -EBUSY;
+
+	printk("Software Suspend Core.\n");
+	for (i=0; i< numfiles; i++)
+		suspend_register_procfile(&proc_params[i]);
+
+	suspend_console_proc_init();
+
+	suspend_core_proc_init();
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+	return 0;
+#else
+	return (!!compat_parent);
+#endif
+}
+
+#ifdef MODULE
+extern void suspend_core_proc_exit(void);
+
+static __exit void core_unload(void)
+{
+	int i, numfiles = sizeof(proc_params) / sizeof(struct suspend_proc_data);
+
+	printk("Software Suspend Core unloading.\n");
+
+	suspend_console_proc_exit();
+	suspend_core_proc_exit();
+	
+	for (i=0; i< numfiles; i++)
+		suspend_unregister_procfile(&proc_params[i]);
+
+	suspend2_unregister_core();
+}
+
+module_init(core_load);
+module_exit(core_unload);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nigel Cunningham");
+MODULE_DESCRIPTION("Suspend2 core");
+#else
+late_initcall(core_load);
+#endif
+EXPORT_SYMBOL_GPL(checksum_map);
+EXPORT_SYMBOL_GPL(attempt_to_parse_resume_device);
