diff -urN oldtree/Documentation/kernel-parameters.txt newtree/Documentation/kernel-parameters.txt
--- oldtree/Documentation/kernel-parameters.txt	2006-10-05 15:26:55.000000000 -0400
+++ newtree/Documentation/kernel-parameters.txt	2006-10-06 17:28:20.000000000 -0400
@@ -81,6 +81,7 @@
 	SERIAL	Serial support is enabled.
 	SMP	The kernel is an SMP kernel.
 	SPARC	Sparc architecture is enabled.
+	SUSPEND2 Suspend2 is enabled.
 	SWSUSP	Software suspend is enabled.
 	TS	Appropriate touchscreen support is enabled.
 	USB	USB support is enabled.
@@ -1080,6 +1081,8 @@
 	noresume	[SWSUSP] Disables resume and restores original swap
 			space.
 
+	noresume2	[SUSPEND2] Disables resuming and restores original swap signature.
+ 
 	no-scroll	[VGA] Disables scrollback.
 			This is required for the Braillex ib80-piezo Braille
 			reader made by F.H. Papenmeier (Germany).
@@ -1382,6 +1385,11 @@
 			in <PAGE_SIZE> units (needed only for swap files).
 			See  Documentation/power/swsusp-and-swap-files.txt
 
+ 	resume2=	[SUSPEND2] Specify the storage device for Suspend2.
+			Format: <writer>:<writer-parameters>.
+			See Documentation/power/suspend2.txt for details of the
+			formats	for available image writers.
+
 	rhash_entries=	[KNL,NET]
 			Set number of hash buckets for route cache
 
diff -urN oldtree/Documentation/power/Suspend2-Changelog.txt newtree/Documentation/power/Suspend2-Changelog.txt
--- oldtree/Documentation/power/Suspend2-Changelog.txt	1969-12-31 19:00:00.000000000 -0500
+++ newtree/Documentation/power/Suspend2-Changelog.txt	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,775 @@
+Suspend2 changelog.
+
+2.2.8 - 20 September 2006
+
+- Add Changelog as Documentation/power/Suspend2-Changelog.txt
+- Don't replace swsusp completely when the do-it-by-default option is on.
+
+2.2.7.6 - 15 September 2006
+
+- Let swsusp work. The readding of BUG_ON(non suspend2 io) test
+  wasn't targetted enough.
+- Fix the swsusp replacement code to work with initramfses.
+- Explicitly case the sizeof(struct suspend_header) as int. One case
+  reported where the lack of a cast seems to have caused huge header
+  size values.
+- Make the suspend_file routine parse_signature static.
+- Fix handling of swapwriter code so that the filewriter can still work
+  with the new swsusp replacement functionality.
+
+2.2.7.5 - 5 September 2006
+
+- Documentation updates
+- Add support for beeping at signficant lowlevel suspending points,
+  to help in diagnosing resume failures.
+  suspend_beep=1 on commandline to activate.
+- Add support for beeping while invoking driver suspend and resume
+  routines, to help in diagnosing resume failures.
+  Boot with device_beep=1 to activate.
+- Add a timer_resume fix that I'm hoping to see John Stultz submit
+  upstream.
+- Readd BUG_ON(non suspend2 io during suspend) in response to a
+  recent corruption report.
+- Remove deflate fix. We were the only ones needing it, and I don't
+  recommend the use of glacially slow compression anyway.
+- Remove PPC SOFTWARE_REPLACE_SLEEP configuration option.
+- Better typing of pageflags variables.
+- Use max_pfn rather than -1 to indicate the start/end of a scan.
+- Remove LRU paranoia. No reports of it being hit.
+- Consolidate Suspend2 specific kernel/power/*.h.
+- Unlink LRU pages instead of avoiding vmscanning during suspend.
+- Make Suspend2 a drop-in replacement for swsusp (compile time
+  option). This doesn't stop swsusp from working. Instead it lets you
+  * use resume= instead of resume2= (resume= takes precedence if set)
+  * use noresume instead of noresume2
+  * use echo disk > /sys/power/state to activate suspend2.
+  By default suspend2 is configured to use lzf compression, if available,
+  and no encryption.
+  Overriding swsusp can by disabled via /sys/power/suspend2.
+- Add further printks to help diagnose resume issues.
+- Replace zone->spanned_pages with populated_zone() test.
+- Some initial work on code to prove/ensure LRU pages aren't modified
+  while being written. Relies on DEBUG_PAGEALLOC, so shouldn't interfere
+  with normal use of this release, but it isn't working yet. x86 only.
+- Some further cleanups from sysfs conversion.
+
+2.2.7.4 - 19 July 2006
+
+- Update Documentation to refer to /sys/power/suspend2.
+- Make invocation of pm_console_suspend/resume optional
+  and disabled by default.
+- Add some printks for -EBUSY error messages.
+- Rename proc.c to sysfs.c.
+- Apply Rafael's suggestion of using the zone table info.
+- Change .disabled to .enabled in the module structure.
+- Change all references to proc to be sysfs.
+- Add one second delay between powering down devices and
+  doing the acpi state enter/machine_power_off/machine_halt.
+- Add result code flag for drivers failing to suspend.
+
+2.2.7.3 - 11 July 2006
+
+- Add subdirectories for modules.
+- Fix do_suspend and do_resume.
+
+2.2.7.2 - 11 July 2006
+
+- Console suspend and resume fix.
+- Initial sysfs support. /proc support retained for the interim. Note that the
+  sysfs layout is subject to change. I may make subdirectories for the modules
+  (compressor etc) and put their entries in there.
+- Modifed debugging info for compressor and encryptor so it's clearer when the
+  name of the algorithm to be used hasn't been set.
+- Try kernel_halt() if kernel_power_off() fails.
+
+2.2.7.1 - 6 July 2006
+
+- Remove Linus patches.
+- Remove some debugging code from atomic_copy.c
+- Change quotes and remove leading spaces in Kconfig
+- Shift resetting accounting of module header storage usage.
+- Add a missing reset of suspend2_running.
+- Remove some extra whitespace that was used when splitting the patches.
+- Enable some code in dyn_pageflags.c that was disabled and correct a typo
+  in it.
+
+2.2.7 - 2 July 2006
+
+- Fix test debugging patch to run on powering down devices instead of
+  when suspending them, and re-disable interrupts when we find something
+  to complain about.
+
+2.2.6.3 - 2 July 2006
+
+- Add Bernard's 2.2.6.2 fix patch (thanks, Bernard!)
+- Add test debugging patch to capture drivers that reenable irqs when they
+  ought not.
+
+2.2.6.2 - 1 July 2006
+
+- Fix date on last release :)
+- Fix freeze when eating memory that I forgot about before doing 2.2.6.1
+- Small commenting fix.
+- Quieten pdflush when thawed.
+
+2.2.6.1 - 1 July 2006
+
+- Cleanups: Whitespace/line wrapping, removal of asm-ppc/cpu_context.h,
+  old/unnecessary declarations in dyn_pageflags.h, freezer.h. Atomic_copy
+  prepare_pbe_list.
+- Commenting: dyn_pageflags.h
+- Rename: pageflags_space_needed -> suspend_*
+- Add zones_in_use variable, to save recalculating how many zones are in use
+  all the time (ta Rafael).
+- !(CONFIG_VT && VT_CONSOLE) compile fix from LKML.
+- Fix SYSTEM_BOOTING->RUNNING, from old version of Linus' console patch.
+- Quoting of KConfig options. Addresses warnings that have appeared in
+  post 2.6.17 git.
+- Reverse early pm_ops->prepare and late ->finish invocations.
+- Handle errors if bdev freezing kmalloc fails or suspend2_suspend fails.
+- Correct error handling in suspend_main().
+- Remove block_dump debugging in file_writer.
+- Re-enable populate_block_list printk condition.
+- Disable pm_prepare_console and pm_restore_console calls for now. Still
+  seem to be problematic.
+- Add MAINTAINERS file entry.
+
+2.2.6 - 23 June 2006
+
+- No problems reported so far with 2.2.5.3, so I'm
+  renaming it to 2.2.6.
+
+2.2.5.3 - 22 June 2006
+
+- Update to 2.6.17
+- Bring figuring out which pages to save closer to the
+  algorithm used by swsusp.
+- Move allocation of image header storage last in updating
+  the image contents.
+- Reset the header bytes used by each module when getting
+  the request so S3 powerdown doesn't only work once.
+- Tidy header allocation code.
+- Tidy process refreezing when updating the image metadata.
+- Add debugging code to check that LRU pages are not modified
+  while the freezer is on.
+- Do pm_state->prepare and finish right at the start and
+  end of a cycle.
+- Add Ben Collin's patch for a trace data section for ppc
+  (needed for Linus' debugging patch, added in 2.2.5.2).
+- Rework extra pageset1 pages allowance so that we have a
+  hardcoded minimum and default (now 500) that is more
+  easily modified (prepare_image.h).
+- Take pm_sem when suspending and add PM_SEM failure bit
+  to the result code.
+- Use console suspend/resume code.
+
+2.2.5.2 - 16 June 2006
+
+- Remove x86_64 e820 code.
+- Add #include to Linus' tracing code to fix compilation in
+  Ubuntu backport.
+- Add display of number of extra pageset 1 pages used.
+- Modify the code which tracks our position in storage so that
+  it sits at the start of a page and not the end. Simplifies
+  finding where to start when resuming.
+- Add pdflush post-resume fix from LKML.
+
+2.2.5.1 - 15 June 2006
+
+- Updated to 2.6.17-rc6.
+- Fixed filewriter issue when header is not at the lowest block
+  number in the file.
+- Moved refrigerator closer to the vanilla implementation.
+- Removed per-workqueue freezing.
+- Carefully audited header space usage and added debugging
+  support.
+- Fixed compilation issues with sharing code with swsusp.
+- Removed SUSPEND_TEST_BIO flag.
+- Added paragraph to suspend2.txt. Pavel forked, not us :)
+- Removed arm code. No recent updates.
+- Added (temporary) e820 amd64 code.
+- Fix BITMAP_FOR_EACH_SET to check for -1 instead of max_pfn.
+- Add ifdefs around some highmem code.
+- Fix some line length issues.
+- Rename attempt_to_parse_resume_device to suspend_...
+- Remove DEBUG_PAGEALLOC support.
+- Prepare to power off with POWER_OFF, not SUSPEND_DISK.
+- Max extra pages allowance should be INT_MAX, not 32768.
+
+2.2.5 - 21 April 2006
+
+- Cleanups from ongoing preparation of the git tree.
+- Correction to x86_64 page_is_ram function.
+- New FREEZING_COMPLETE status variable, used in debugging the
+  new shrink_all_memory implementation.
+- Improvements to calculations of header space required. More
+  work to be done.
+- Fix compilation with !CONFIG_NET
+- Some new image calculation paranoia. Shouldn't be needed, but
+  you can't always know everything that changes from one kernel
+  version to the next.
+- Use Con Kolivas and Rafael Wysocki's shrink_all_memory rework,
+  with a couple of bug fixes of my own. In combination with this,
+  simplify the memory freeing in suspend2 to use a single pass.
+- Fix suspend_running not being set at resume time, which caused
+  spontaneous reboots when the temporary pagedir allocator then
+  got unsafe pages.
+- Remove setting swap info struct data during resume.
+- Update hooks in the pagealloc routines that prevented memory
+  freeing while we're writing the image.
+
+2.2.4.1 - 13 April 2006
+
+- Removed use of max_pfn to make life simpler on ARM.
+- Fix line length in atomic_copy.c.
+- Rename suspend_recalculate_stats to suspend_recalculate_image_contents
+- Remove checksumming code.
+- Apply Jens Gustedt's checks for which filesystems are mounted
+  on resume.
+- Fix encryption where PAGE_SIZE % key length != 0.
+- Move swsusp Makefile entries below Suspend2 again - it doesn't play
+  nicely with Suspend2, and so needs to have it's boot time routines
+  run later. noresume2 will also not work with noresume being compiled
+  in first.
+- Rework the allocation of extra pages to use PageNosave instead of
+  PageAllocd and don't completely clear Pageset1Copy on every iteration
+  through count_data_pages. 
+- Remove the ConflictingPages bitmap.
+- Remove a bogus comment that said we remember conflicting pages and
+  free them.
+- Remove DEBUG_PAGEALLOC support.
+- Add extra message for when powering off.
+- Rework and cleanup code which determines which pages to save.
+- Correction to ram_to_suspend function - it should halve the
+  amount calculated and take account of extra pagedir pages that have
+  been allocated.
+- Correct handling of proc entries that use longs. This is particularly
+  relevant to the image_size_limit. It wasn't previously correctly set
+  to -1 or -2. (This would have broken the nocaches functionality).
+- Move suspend_default_console_level to ui.c.
+- Remove unused suspend_bio_task in block_io.c.
+- Use progress granularity that was saved in the image header, even
+  if the userui helper program stored in the image header won't be
+  used because it was overridden by the initrd/ramfs script.
+- Don't update the load average while the freezer is on. This fixes
+  an unintended sideeffect wherein mail services will refuse to
+  operate post-resume because the load average had been pushed up
+  by Suspend's intensive CPU use.
+
+2.2.4 - 2 April 2006
+
+- Fixed what are hopefully the last outstanding issues with
+  the switchover to using the swsusp lowlevel code:
+  * Corrected compilation issues with the difference
+    combinations of swsusp and Suspend2.
+  * Make x86_64 work correctly - get_safe_page() was using
+    the swsusp code instead of Suspend2 code.
+
+2.2.3 - 1 April 2006
+
+- Add better powerdown_methods documentation (thanks
+  Jens Gustedt).
+- Further work addressing issues with using the swsusp
+  lowlevel code.
+- Abort on resume if we fail to initialise the compression
+  transform used when suspending.
+- Switch filewriter to use add_to_extent_chain instead of
+  append_extent_to_extent_chain and remove now the now
+  unused function.
+- Fix compilation errors if swsusp and Suspend2 are both
+  disabled.
+- Fix PPC compilation error with !rtas.
+
+2.2.2.1 - 28 March 2006
+
+- Missed a part of the backport, inadvertently making
+  a requirement that you also select
+  CONFIG_SOFTWARE_SUSPEND. Fix that by making Suspend2
+  select SOFTWARE_SUSPEND automatically.
+
+2.2.2 - 28 March 2006
+
+- Backported to 2.6.16.
+- Added a couple of printks for when we fail to write a
+  block of the image. Saw this in unusual circumstances
+  while testing, and need to seek to reproduce and fix.
+
+2.2.1.3 - 27 March 2006
+Prepared against current git as at 7.30pm AEST
+- Further cleanups as I begin to prepare the git tree
+  in earnest.
+- Highmem fixes I almost forgot for this release!
+
+2.2.1.2 - 23 March 2006
+Prepared against current git as at 11pm AEST.
+
+- Further cleanups from the switch to swsusp lowlevel
+  code. Should not longer require swsusp to be selected.
+
+2.2.1.1 - 23 March 2006
+Prepared against current git as at 4:20pm AEST.
+
+- Switch suspend2 to use the swsusp lowlevel code.
+- Rework freeing of extra pages allocated for the
+  atomic copy.
+- Misc cleanups.
+
+2.2.1 - 17 March 2006
+
+- Remove debug_pagealloc support
+- Documentation update
+- Move device_suspend at resume time prior to setting
+  up temporary page tables (Lamarque Souza).
+
+2.2.0.8 - 15 March 2006
+
+- Convert pageset sizes to longs.
+- Convert [read|write]_header_chunk to
+  rw_header_chunk([READ|WRITE]...) and do similar
+  cleanups with other i/o routines.
+- Remove some unused module structure members.
+- Flatten the module ops.
+- Fix a couple of harmless compile warnings.
+
+2.2.0.7 - 15 March 2006
+
+- Further fixes from 2.2.0.6. Now tested with modifying
+  resume2= in an initrd and checking image_exists prior
+  to do the echo > do_resume.
+
+2.2.0.6 - 13 March 2006
+
+- Split logic for determining whether we can suspend from
+  that for figuring out whether we can resume. This is
+  useful for the filewriter, where resume2= can be valid
+  (allowing us to resume) while filewriter_target isn't
+  set (prohibiting suspending).
+- Update file headers.
+
+2.2.0.5 - 13 March 2006
+
+- Oh dear. I somehow reversed the 2.2.0.3 fix in 2.2.0.4.
+  Let's try and get them both in the same patch, shall we?
+
+2.2.0.4 - 13 March 2006
+
+- Fix missing incrementation of # extents freed in new
+  swap storage code.
+
+2.2.0.3 - 11 March 2006
+
+- Add x86 compilation error fix I missed.
+
+2.2.0.2 - 11 March 2006
+
+- Fix minimum free ram at 2000 pages rather than nr pages >> 7.
+- Remove command line suspend_act, suspend_lvl and suspend_dbg
+  options.
+- Fix issues with swap allocation that made suspend not work
+  properly with multiple swap storage at the same priority or
+  highly fragmented storage.
+- Add Randy Dunlap's libata patches (sata).
+- Removed time patches.
+- Documentation updates
+- Remove un-needed x86_64 fix processor context patch.
+- Remove x86_64 mcheck_init invocation.
+- Separate workqueue freezer, dynamic pageflags and
+  lzf patches.
+- Remove obsolete configuration flags from the enums in suspend2.h.
+- Correct various typos in headers.
+- Various cleanups.
+- Remove command line parameters suspend_act, suspend_dbg and
+  suspend_lvl.
+- Remove HALT_ON debugging macro from pagedir.c.
+- Add tags for my new patch splitting utility.
+
+2.2.0.1 - 15 February 2006
+
+- Update to 2.6.16-rc3.
+- Further cleanups, removing fragments that are no longer needed
+  and applying suggestions from code review.
+- Remove Christoph's todo list code. Not because I don't like it,
+  but because it was originally designed to help SMP cleanness,
+  and cpu hotplug has taken over that need.
+
+2.2 - 24 January 2006 (Happy Birthday, Dad!)
+
+- Fix amd64 page_is_ram typo.
+- Fix unintentional dependence on swsusp (amd64).
+- Make swapwriter select swap support instead of depending
+  on it already being selected.
+- Make a new common function for writing the last (partial)
+  page of a header.
+- Remove temporary enabling of block dump when reading pageset1.
+- Extend have_image to also display some of the details from
+  the header.
+- Cleanup count_data_pages changes.
+- Save and restore the block_dump setting when starting/finishing
+  a cycle.
+- Add support for debugging the image header reading & writing.
+- A few fixes to the filewriter.
+- Cleanups in swapwriter and arch specific code.
+
+2.2-rc16 - 8 January 2006
+
+- Small updates to documentation.
+- Fix amd64 support.
+- Remove no-unneeded modifications to fs/super.c & mtrr code.
+- Cleanup dyn pageflags include files.
+- Clean freezer header.
+- Move swsusp to bottom of Makefile to make interoperability work better.
+- Move pm_ops prepare/finish calls back -moving the calls to pre/post
+  hotplugging doesn't seem to be the right thing to do at the moment.
+- Save zone info that's used during atomic restore in memory that can
+  be safely accessed during that period.
+- Modify swsusp so it can be used with the new freezer without causing
+  a deadlock.
+- Reinstate the ability to cancel a suspend part way through writing a
+  pageset.
+- Fix some duplicated allowance for extra pd1 pages.
+- Further work on new PageNoSave code.
+- Increase max outstanding I/O to 2048.
+- Remove some unnecessary forced inlining.
+
+2.2-rc15 - 26 December 2005
+
+- Call the correct writer init routine (read/write were transposed!)
+- Freeze filesystems in reverse order so loopback doesn't result in
+  a hang.
+- Select HOTPLUG_CPU instead of depending on it (SMP).
+- Further switchovers from assuming pfns start at 0 to using per zone
+  ranges.
+- Lots of architecture specific fixes for PPC, x86_64 and ARM.
+- Work on removing PageNosave dependency and changes outside kernel/power.
+- Support new read-only section patches.
+- Remove b44 patch.
+- Cleanups (as always!). This time, we remove driver_model.[ch], and
+  remove some now unused MTRR modifications and lowlevel x86 code.
+- Small modifications to lowlevel code routine.
+- Reintroduce /proc/suspend2/no_pageset2 for testing.
+- Compile fix for !SUSPEND2.
+- Fixups to powerdown code (yes, again!).
+- Bug fix to rw_init_plugins.
+- Small rework of sanity checking.
+- Make num_pcp_pages() numa compatible.
+- Cleanup filesystem freezing code.
+- More consolidation of swapwriter and filewriter code into bio.c.
+- Attempt to parse resume device when checking if can suspend and
+  suspend is currently disabled.
+- Rename noresume_setup to noresume2_setup so it works with swsusp
+  compiled in.
+- Fix filewriter so it again tries to stat the file if name_to_dev_t
+  fails.
+
+2.2-rc14 - 2 December 2005
+
+- Correct header_locations in documentation to headerlocations.
+- Remove BUG_ON() in ll_rw_blk.c.
+- Correction to nvidia agp suspend/resume support.
+- Complete removal of syncthread code.
+- Consolidate all of the page i/o code from swapwriter and
+  filewriter into one version, moved to block_io.c.
+- Combine the io.c read & write pageset routines and make
+  separate routines of plugin setup/cleanup and the main loop.
+- Fix for AMD64 reading wrong block for header at resume time
+  (and thus reporting 0.0.0 kernel version in the image).
+
+2.2-rc13 - 25 November 2005
+
+- Add ARM support, thanks to Hiroki Kaminaga at Sony.
+- Replace remounting with freezing and thawing filesystems.
+- Remove now unneeded syncthread code (more to be done).
+- Don't sys_sync after freezing filesystems. That way lies deadlocks.
+- Beginnings of cleanups for writers.
+- Make number of extra pagedir 1 pages allowed for tunable via a
+  new proc entry. Set it to 0 for autotuning.
+- Fix an oops in the filewriter.
+- Fix for typo in power off code (thanks, _damjan_).
+
+2.2-rc12 - 22 November 2005
+
+- New test_bio proc debugging entry; don't actually write anything,
+  just say where the data would be put.
+- Further filewriter fixes. It should be perfectly usable now,
+  but I would still recommend care and backups!
+- Don't use smp_processor_id in lowlevel code. With cpuhotplug, we
+  know we're on processor zero.
+- Fix some indentation in the lowlevel code.
+- Touch softlockup watchdog when resuming. Shouldn't be needed and
+  doesn't seem to make any difference, but I'll seek consistency
+  with Pavel at this point.
+- Show what processes enter and leave the fridge and when iff
+  PM Debug is enabled (wRAR).
+- Modify remounting logic introduced in rc11 so that syslog etc
+  continue to work postresume.
+- Remove some extra debugging code in the freezer.
+- Prune the number of calls to attempt_to_parse so that we can
+  properly figure out whether suspending should be enabled.
+- Don't unblank the console from the kernel code. Doesn't play
+  nicely with vesafb-tng. 
+
+2.2-rc11 - 15 November 2005
+
+- Remount filesystems readonly while suspending so as to ensure
+  xfs flushes all pending writes. This also means things should
+  be nice and clean if you fail to resume (for whatever reason).
+- Fix highmem support that I broke with rc10.
+- Fix typo in 2nd+ attempt at resuming message.
+
+2.2-rc10 - 14 November 2005
+
+- Increased allowance for extra pagedir1 pages. This is primarily
+  for the new flgrx driver. If you don't use it, you'll see a
+  bigger drop in the total image size after the atomic copy. This
+  is nothing to be concerned about.
+- Some debugging code has been temporarily modified to always
+  print, to help address some particular users' issues.
+- More helpful debugging for AMD64 preemption problems.
+- Rework of powerdown code. Powerdown method can now also be 0,
+  meaning Suspend won't try to use ACPI to powerdown. 1 & 2 are
+  also accepted values. You might find Standby (1) useful, but 2
+  will probably only make the computer resume again immediately.
+- Big rework of blocksize handling. Suspend2 is now tested with the
+  swap files and ordinary files on ext3, xfs and reiser with each
+  blocksize allowed by the filesystem type. Other filesystems should
+  work without a problem because the algorithm is basically the same
+  as that used for swapfiles.
+- Initial version of discontiguous memory support.
+- Replace /proc/software_suspend with /proc/suspend2.
+- Make Suspend2 explicitly depend upon cpu hotplug for SMP.
+- Leave interrupts enabled when powering down.
+- Make storage manager testing proc entry depend on PM_DEBUG.
+- Remove unnecessary check at the start of save image.
+- Small cleanups in the swapwriter (swapfile management).
+
+2.2-rc9 - 5 November 2005
+
+- Filewriter cleanups and bug fixes.
+- Display stats when unable to allocate enough storage, to help diagnosing
+  this issue.
+- Some cleanups to e820 table support.
+- Various small fixes to PPC support.
+- Modifed BIO submission check to be smarter (use new Suspend2 flag in BIO
+  flags).
+- Add userspace storage manager (usm) support. Requires userspace program.
+  None are available yet. This change included a separation of the netlink
+  code from the userui code into a new file.
+- Replace type * name with type *name everywhere.
+- Made debugging code for driver suspend/resume calls more gentle
+  (delay/print instead of BUG_ON().
+- Don't mark image as resumed before when in KEEP_IMAGE made.
+- Seek to address BUG_ONs being hit in marking a task as pageset1.
+- Address some arch/config specific compilation warnings.
+- Use kernel_power_off_prepare instead of device_shutdown when not entering
+  suspend to ram.
+- Thaw kernel processes before beginning to eat memory.
+- Make freezer quiet by default.
+- Export freezer state for when ext3 (eg) built as a module.
+- Add news_storage_manager to proc entry info so that entries that needs
+  access to storage for reading and/or writing can enable it as required.
+  There is also a new activate_storage entry that can be used in a script to
+  avoid having it repetitively enabled and disabled by a number of instructions.
+	echo 1 >  /proc/suspend2/activate_storage
+  at the start of the sequence and
+	echo 0 > /proc/suspend2/activate_storage
+  and the end.
+- Correct bits in result code so hibernate script gives the correct
+  interpretation again.
+- Cleanup and simplify file management in the file writer. (Debugging printks
+  left in for this release).
+- Poke blanked console when resuming.
+ 
+2.2-rc8 - October 3, 2005.
+
+REQUIRES hibernate script 1.12 or newer. 
+
+- Changed name of proc directory to suspend2.
+- Removed suspend_drivers_init/cleanup
+- Cleaned up debug_info entry
+- Remove atomic_restore conflict check
+- Stop tracking max extents used.
+- put_extent. Just BUG_ON(!extent). Or don't handle.
+- put_extent_chain remove if (!this) return
+- Removed references to chain optimisation info
+- Remove error messages in <load|serialise>_extent_chain (should
+  come from real cause of err)
+- attempt_to_parse_resume_device should return whether successful
+- Merge plugin read/write inits/cleanups into one loop.
+- Merge read & write routines into one generic I/O loop?
+- Sanity check kernel version - macros for digits?
+- Rename free_pagedir_data to free_extra_pagedir_memory. Don't reset pageset
+  sizes there.
+- Removed SetPageConflicting
+- release_conflicting_pages: Delete. Not used.
+- remove version info in proc.c
+- Remove #ifdef CONFIG_SUSPEND2_DEFAULT_RESUME2 in suspend2
+- compress <allocate|free>_bitmaps functions
+- Remove #ifdef DEBUG_PAGE_ALLOC in suspend2.c
+- can_suspend: if(DISABLED && !attempt_to_parse())
+- Remove keep_metadata proc entry
+- Use (DISABLED && !attempt_to_parse() in suspend2_resume, too.
+- Removed unused fields used in io_info struct
+- flush_dcache_page needed?
+- Use put_page to free pages.
+- Some extraneous comments removed.
+- Removed header_data comment
+- /*^N *^N *^N* ^N* ^N */
+- Remove PRINTK in swapwriter
+- Remove swapwriter file management?
+- Cleaned up get_blocks_per_page in swapwriter
+- Integrated get_path_for_swapfile to caller
+- Parse signature - remove old pmdisk version support
+- Removed unneeded extern int signature_check? (swapwriter:478)
+- Cleaned up swapwriter_storage_allocated
+- Remove space_requests = ((long) space_really_requested) << PAGE_SHIFT
+- Remove allocate_header_space comment "was going to be in bytes"
+- Simplified header space allocated calculation.
+- Remove extra escape action in request_abort_suspend (not used and would
+  be a problem anyway).
+- Fix compile error for AMD64 SMP.
+- Fix error in the filewriter.
+
+2.2-rc7 - September 16 2005
+
+- Further fixing of powerdown code. If !pm_ops, just use
+  kernel_power_off.
+- Use return code when freezing failure occurs.
+- Make userui disabling effective.
+
+2.2-rc6 - September 6 2005 (The Hamilton Release)
+
+- Fix for corruption caused when failing to power down.
+- Fix for failing to power down.
+- Separated refrigerator changes from Suspend2 for separate
+  merge.
+- Misc other cleanups.
+
+2.2-rc5 - September 2 2005
+- Add more detailed debugging information regarding which
+  driver re-enables interrupts and recover.
+- Handle wrong userui interface version by resetting userui
+  and continuing without it.
+- Fix for powerdown issues.
+- Ensure resumed before flag isn't unintentionally set when
+  writing header.
+- Seek to address trace given in refrigerator code.
+- Complain if can't find the userui process number given
+  for setting nofreeze.
+- Fixed a bug in the swap allocation routine that would
+  result in swap being allocated but not freed.
+
+2.2-rc4 - August 30 2005
+
+- Fixed bounding error in swapwriter device closing. This
+  had 'interesting' side effects.
+- Moved storing the console log level used to before asking
+  the userui to cleanup, so that after suspending.
+  default_console_loglevel contains the last one used, and
+  not the original value.
+- Fixed a mistake in include/asm-i386/suspend2.h that broke
+  compilation for gcc 4 users.
+- Made the refrigerator ignore Stopped processes again. Mea
+  culpa. As always :|
+
+2.2-rc3 - August 29 2005
+
+- Modified error message for setting the blocksize to give
+  some useful information.
+- Fixed typo in todo_list_active calls in jfs_txnmgr.c
+- Fixed size of blocksize storage in header. Thanks Ola!
+- Merged in initramfs changes to filewriter. Added new state
+  flag for resuming from initramfs and call from do_mounts_rd.c.
+  Yet to test.
+- Implemented communication of the netlink socket number to use
+  to the userui program when invoking it. (Changes to commit to
+  userui tree)
+- Give more information and invalidate the image when we fail to
+  initialise a filter at resume.
+- Clean up console when failing to resume.
+- Make escape and toggling pause and single step take effect
+  immediately.
+- Fixed log everything so that it does again.
+- Expand userui message buffer from 80 to 255 characters.
+- Fixed a silly bug when the header is not stored on the resume
+  block device.
+- Modify freezer implementation to signal all processes once,
+  separate to waiting for them to enter the refrigerator. This
+  will be more robust and quicker if/when the todo list gets used
+  for other things.
+- Modify userui version to 6.
+- Move freeing of freezer notifier entry to post spinlock release.
+- Fix compilation error when !SUSPEND2 & CRYPTO
+- Fix missing ; in macro for !SUSPEND2
+
+2.2-rc2 - August 24 2005
+- Apply LZF 64 bit from Anders Fugmann.
+- Complete transition to todo list and fix up jffs2.
+- Temporary fix for gcc 4, telling it lies that we're
+  not clobbering ebp. Something similar needed for other
+  archs?
+
+2.2-rc1 - August 23 2005
+- Fixed problem with swapwriter buffer offset not being reset
+  when beginning to write the image header.
+- PPC fixes from Johannes Berg.
+- Give a better message when can't initialise a compressor.
+- Remove an unused variable from the pageset_sizes_result struct.
+- Make Suspend still compile ok when networking is disabled.
+  Userui won't work (it uses netlink), but suspending will.
+  This will also be handy for embedded support.
+- Say why we can't do userui when we can't open /dev/console
+  (entirely possible when resuming from an initrd).
+
+2.1.9.14 - August 22 2005
+- Fixes for previous patch - base tree was slightly corrupted.
+- Also fixed agp driver cleanups - humble apologies to ATI and
+  Nvidia users.
+- Small fixes for new bdev management code in swapwriter.
+
+2.1.9.13 - August 20 2005
+- Added support for Christoph Lameter's todo list changes.
+- Reworked device setup at resume time.
+- Began splitting up patches into many more changesets for
+  the (to be made) git tree. I'll now ship the patch as one
+  big patch.
+- Miscellaneous other bug fixes and cleanups.
+
+2.1.9.12 - August 4 2005
+- Bug fixes for the refrigerator code
+- Fixed bug in dyn_pageflags code that would cause an oops if
+  allocating pageflags ever failed.
+- Changed block io code to use kblockd_flush.
+- Reworking of semantics for workqueue calls (merge work).
+
+2.1.9.11 - July 30 2005
+- More cleanups.
+
+2.1.9.10 - July 23 2005
+- Lots more cleanups and little fixes.
+- Changed workqueue freezer implementation from using an extra parameter
+  to using new routines with nofreeze in the name. Greatly reduces the
+  number of files we touch.
+- Remove SYNCTHREAD flag from kernel threads. It really means nothing.
+  (They are frozen anyway, after real syncthreads).
+- In the process of re-adding checksumming as a standalone patch. Not
+  to be submitted.
+- Cleaned up LZF. Removed macros and ifdefs.
+- Started pushing patches.
+- Renamed arch specific functions to include _arch_.
+- Removed all_settings after asking how many people use it.
+- Fixed up copyback thread spawning so as to ensure that the stack used
+  is not in a conflicting page. We spwan new threads until one is ok.
+  Each thread spawned will keep spawning other threads (with a delay)
+  until one has a non conflicting page. This means you can end up with
+  a large number of threads quickly. We guarantee that only one will
+  continue on to the copyback. All others enter the freezer.
+
+2.1.9.9 - July 8 2005
+- Move suspend2_core directory contents up to kernel/power.
+- Further work on x86_64 (more to be done before it can be tested).
+- Lots of cleanups in response to premerge (I hope) comments. As part
+  of this, converted a number of lists of #defined bit indices to
+  enums. This will break saved all_settings files, so I bumped the
+  version.
+- Added debugging info to swapwriter to try and diagnose set block size
+  oops some people are seeing.
diff -urN oldtree/Documentation/power/internals.txt newtree/Documentation/power/internals.txt
--- oldtree/Documentation/power/internals.txt	1969-12-31 19:00:00.000000000 -0500
+++ newtree/Documentation/power/internals.txt	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,238 @@
+		Software Suspend 2.2 Internal Documentation.
+				Version 1
+
+1.  Introduction.
+
+    Software Suspend 2.2 is an addition to the Linux Kernel, designed to
+    allow the user to quickly shutdown and quickly boot a computer, without
+    needing to close documents or programs. It is equivalent to the
+    hibernate facility in some laptops. This implementation, however,
+    requires no special BIOS or hardware support.
+
+    The code in these files is based upon the original implementation
+    prepared by Gabor Kuti and additional work by Pavel Machek and a
+    host of others. This code has been substantially reworked by Nigel
+    Cunningham, again with the help and testing of many others, not the
+    least of whom is Michael Frank, At its heart, however, the operation is
+    essentially the same as Gabor's version.
+
+2.  Overview of operation.
+
+    The basic sequence of operations is as follows:
+
+	a. Quiesce all other activity.
+	b. Ensure enough memory and storage space are available, and attempt
+	   to free memory/storage if necessary.
+	c. Allocate the required memory and storage space.
+	d. Write the image.
+	e. Power down.
+
+    There are a number of complicating factors which mean that things are
+    not as simple as the above would imply, however...
+
+    o The activity of each process must be stopped at a point where it will
+    not be holding locks necessary for saving the image, or unexpectedly
+    restart operations due to something like a timeout and thereby make
+    our image inconsistent.
+
+    o It is desirous that we sync outstanding I/O to disk before calculating
+    image statistics. This reduces corruption if one should suspend but
+    then not resume, and also makes later parts of the operation safer (see
+    below).
+
+    o We need to get as close as we can to an atomic copy of the data.
+    Inconsistencies in the image will result in inconsistent memory contents at
+    resume time, and thus in instability of the system and/or file system
+    corruption. This would appear to imply a maximum image size of one half of
+    the amount of RAM, but we have a solution... (again, below).
+
+    o In 2.6, we choose to play nicely with the other suspend-to-disk
+    implementations.
+
+3.  Detailed description of internals.
+
+    a. Quiescing activity.
+
+    Safely quiescing the system is achieved using two methods.
+
+    First, we note that the vast majority of processes don't need to run during
+    suspend. They can be 'frozen'. We therefore implement a refrigerator
+    routine, which processes enter and in which they remain until the cycle is
+    complete. Processes enter the refrigerator via try_to_freeze() invocations
+    at appropriate places.  A process cannot be frozen in any old place. It
+    must not be holding locks that will be needed for writing the image or
+    freezing other processes. For this reason, userspace processes generally
+    enter the refrigerator via the signal handling code, and kernel threads at
+    the place in their event loops where they drop locks and yield to other
+    processes or sleep.
+
+    The second part of our method for quisescing the system involves freezing
+    the filesystems. We use the standard freeze_bdev and thaw_bdev functions to
+    ensure that all of the user's data is synced to disk before we begin to
+    write the image.
+
+    Quiescing the system works most quickly and reliably when we add one more
+    element to the algorithm: separating the freezing of userspace processes
+    from the freezing of kernel space processes, and doing the filesystem freeze
+    in between. The filesystem freeze needs to be done while kernel threads such
+    as kjournald can still run.  At the same time, though, everything will be
+    less racy and run more quickly if we stop userspace submitting more I/O work
+    while we're trying to quiesce.
+
+    Quiescing the system is therefore done in three steps:
+	- Freeze userspace
+	- Freeze filesystems
+	- Freeze kernel threads
+
+    If we need to free memory, we thaw kernel threads and filesystems, but not
+    userspace. We can then free caches without worrying about deadlocks due to
+    swap files being on frozen filesystems or such like.
+
+    b. Ensure enough memory & storage are available.
+
+    We have a number of constraints to meet in order to be able to successfully
+    suspend and resume.
+
+    First, the image will be written in two parts, described below. One of these
+    parts needs to have an atomic copy made, which of course implies a maximum
+    size of one half of the amount of system memory. The other part ('pageset')
+    is not atomically copied, and can therefore be as large or small as desired.
+
+    Second, we have constraints on the amount of storage available. In these
+    calculations, we may also consider any compression that will be done. The
+    cryptoapi module allows the user to configure an expected compression ratio.
+   
+    Third, the user can specify an arbitrary limit on the image size, in
+    megabytes. This limit is treated as a soft limit, so that we don't fail the
+    attempt to suspend if we cannot meet this constraint.
+
+    c. Allocate the required memory and storage space.
+
+    Having done the initial freeze, we determine whether the above constraints
+    are met, and seek to allocate the metadata for the image. If the constraints
+    are not met, or we fail to allocate the required space for the metadata, we
+    seek to free the amount of memory that we calculate is needed and try again.
+    We allow up to four iterations of this loop before aborting the cycle. If we
+    do fail, it should only be because of a bug in Suspend's calculations.
+    
+    These steps are merged together in the prepare_image function, found in
+    prepare_image.c. The functions are merged because of the cyclical nature
+    of the problem of calculating how much memory and storage is needed. Since
+    the data structures containing the information about the image must
+    themselves take memory and use storage, the amount of memory and storage
+    required changes as we prepare the image. Since the changes are not large,
+    only one or two iterations will be required to achieve a solution.
+
+    d. Write the image.
+
+    We previously mentioned the need to create an atomic copy of the data, and
+    the half-of-memory limitation that is implied in this. This limitation is
+    circumvented by dividing the memory to be saved into two parts, called
+    pagesets.
+
+    Pageset2 contains the page cache - the pages on the active and inactive
+    lists. These pages are saved first and reloaded last. While saving these
+    pages, the swapwriter module carefully ensures that the work of writing
+    the pages doesn't make the image inconsistent. No atomic copy of these
+    pages needs to be made.
+
+    Writing the image requires memory, of course, and at this point we have
+    also not yet suspended the drivers. We therefore ensure that some memory is
+    available for both writing the image and letting drivers allocate memory
+    for saving state. A sysfs entry lets the user tune the amount of memory
+    kept free, since if DRI is enabled, this requirement can be quite large. 
+
+    Once pageset2 has been saved, we suspend the drivers and save the CPU
+    context before making an atomic copy of pageset1, resuming the drivers
+    and saving the atomic copy. After saving the two pagesets, we just need to
+    save our metadata before powering down.
+
+    Having saved pageset2 pages, we can safely overwrite their contents with
+    the atomic copy of pageset1. This is how we manage to overcome the half of
+    memory limitation. Pageset2 is normally far larger than pageset1, and
+    pageset1 is normally much smaller than half of the memory, with the result
+    that pageset2 pages can be safely overwritten with the atomic copy of
+    pageset1. This is where we need to be careful about syncing, however.
+    Pageset2 will probably contain filesystem meta data. If this is overwritten
+    with pageset1 and then a sync occurs, the filesystem will be corrupted -
+    at least until resume time and another sync of the restored data. Since
+    there is a possibility that the user might not resume or (may it never be!)
+    that suspend might oops, we do our utmost to avoid syncing filesystems after
+    copying pageset1.
+
+    e. Power down.
+
+    Powering down uses standard kernel routines. Prior to this, however, we
+    suspend drivers again, ensuring that write caches are flushed.
+
+4.  Data Structures.
+
+- For recording which pages are in each pageset, and where pageset1 will be copied to. Also need to be stored easily and quickly in the image header... pageflags.
+- For recording which devices and blocks the image will be stored in ... extents.
+- For recording the configuration in the image header and making enable/disabling/replacing parts simple... modules.
+- For letting the user configure options... sysfs data structures.
+
+
+4.  The method of writing the image.
+
+    Suspend2 contains an internal API which is designed to simplify the
+    implementation of new methods of transforming the image to be written and
+    writing the image itself. In early versions of Suspend2, compression support
+    was inlined in the image writing code, and the data structures and code for
+    managing swap were intertwined with the rest of the code. A number of people
+    had expressed interest in implementing image encryption, and alternative
+    methods of storing the image. This internal API makes that possible by
+    implementing 'modules'.
+
+    A module is a single file which encapsulates the functionality needed
+    to transform a pageset of data (encryption or compression, for example),
+    or to write the pageset to a device. The former type of module is called
+    a 'page-transformer', the later a 'writer'.
+
+    Modules are linked together in pipeline fashion. There may be zero or more
+    page transformers in a pipeline, and there is always exactly one writer.
+    The pipeline follows this pattern:
+
+		---------------------------------
+		|          Suspend2 Core        |
+		---------------------------------
+				|
+				|
+		---------------------------------
+		|	Page transformer 1	|
+		---------------------------------
+				|
+				|
+		---------------------------------
+		|	Page transformer 2	|
+		---------------------------------
+				|
+				|
+		---------------------------------
+		|            Writer		|
+		---------------------------------
+
+    During the writing of an image, the core code feeds pages one at a time
+    to the first module. This module performs whatever transformations it
+    implements on the incoming data, completely consuming the incoming data and
+    feeding output in a similar manner to the next module. A module may buffer
+    its output.
+
+    During reading, the pipeline works in the reverse direction. The core code
+    calls the first module with the address of a buffer which should be filled.
+    (Note that the buffer size is always PAGE_SIZE at this time). This module
+    will in turn request data from the next module and so on down until the
+    writer is made to read from the stored image.
+
+    Part of definition of the structure of a module thus looks like this:
+
+        int (*rw_init) (int rw, int stream_number);
+        int (*rw_cleanup) (int rw);
+        int (*write_chunk) (struct page *buffer_page);
+        int (*read_chunk) (struct page *buffer_page, int sync);
+
+    It should be noted that the _cleanup routine may be called before the
+    full stream of data has been read or written. While writing the image,
+    the user may (depending upon settings) choose to abort suspending, and
+    if we are in the midst of writing the last portion of the image, a portion
+    of the second pageset may be reread.
diff -urN oldtree/Documentation/power/suspend2.txt newtree/Documentation/power/suspend2.txt
--- oldtree/Documentation/power/suspend2.txt	1969-12-31 19:00:00.000000000 -0500
+++ newtree/Documentation/power/suspend2.txt	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,703 @@
+	--- Suspend2, version 2.2 ---
+
+1.  What is it?
+2.  Why would you want it?
+3.  What do you need to use it?
+4.  Why not just use the version already in the kernel?
+5.  How do you use it?
+6.  What do all those entries in /sys/power/suspend2 do?
+7.  How do you get support?
+8.  I think I've found a bug. What should I do?
+9.  When will XXX be supported?
+10  How does it work?
+11. Who wrote Suspend2?
+
+1. What is it?
+
+   Imagine you're sitting at your computer, working away. For some reason, you
+   need to turn off your computer for a while - perhaps it's time to go home
+   for the day. When you come back to your computer next, you're going to want
+   to carry on where you left off. Now imagine that you could push a button and
+   have your computer store the contents of its memory to disk and power down.
+   Then, when you next start up your computer, it loads that image back into
+   memory and you can carry on from where you were, just as if you'd never
+   turned the computer off. Far less time to start up, no reopening
+   applications and finding what directory you put that file in yesterday.
+   That's what Suspend2 does.
+
+   Suspend2 has a long heritage. It began life as work by Gabor Kuti, who,
+   with some help from Pavel Machek, got an early version going in 1999. The
+   project was then taken over by Florent Chabaud while still in alpha version
+   numbers. Nigel Cunningham came on the scene when Florent was unable to
+   continue, moving the project into betas, then 1.0, 2.0 and so on up to
+   the present 2.2 series. Pavel Machek's swsusp code, which was merged around
+   2.5.17 retains the original name, and was essentially a fork of the beta
+   code until Rafael Wysocki came on the scene in 2005 and began to improve it
+   further.
+
+2. Why would you want it?
+
+   Why wouldn't you want it?
+   
+   Being able to save the state of your system and quickly restore it improves
+   your productivity - you get a useful system in far less time than through
+   the normal boot process.
+   
+3. What do you need to use it?
+
+   a. Kernel Support.
+
+   i) The Suspend2 patch.
+   
+   Suspend2 is part of the Linux Kernel. This version is not part of Linus's
+   2.6 tree at the moment, so you will need to download the kernel source and
+   apply the latest patch. Having done that, enable the appropriate options in
+   make [menu|x]config (under Power Management Options), compile and install your
+   kernel. Suspend2 works with SMP, Highmem, preemption, x86-32, PPC and x86_64.
+
+   Suspend2 patches are available from http://suspend2.net.
+
+   ii) Compression and encryption support.
+
+   Compression and encryption support are implemented via the
+   cryptoapi. You will therefore want to select any Cryptoapi transforms that
+   you want to use on your image from the Cryptoapi menu while configuring
+   your kernel.
+
+   You can also tell Suspend to write it's image to an encrypted and/or
+   compressed filesystem/swap partition. In that case, you don't need to do
+   anything special for Suspend2 when it comes to kernel configuration.
+
+   iii) Configuring other options.
+
+   While you're configuring your kernel, try to configure as much as possible
+   to build as modules. We recommend this because there are a number of drivers
+   that are still in the process of implementing proper power management
+   support. In those cases, the best way to work around their current lack is
+   to build them as modules and remove the modules while suspending. You might
+   also bug the driver authors to get their support up to speed, or even help!
+
+   b. Storage.
+
+   i) Swap.
+
+   Suspend2 can store the suspend image in your swap partition, a swap file or
+   a combination thereof. Whichever combination you choose, you will probably
+   want to create enough swap space to store the largest image you could have,
+   plus the space you'd normally use for swap. A good rule of thumb would be
+   to calculate the amount of swap you'd want without using Suspend2, and then
+   add the amount of memory you have. This swapspace can be arranged in any way
+   you'd like. It can be in one partition or file, or spread over a number. The
+   only requirement is that they be active when you start a suspend cycle.
+   
+   There is one exception to this requirement. Suspend2 has the ability to turn
+   on one swap file or partition at the start of suspending and turn it back off
+   at the end. If you want to ensure you have enough memory to store a image
+   when your memory is fully used, you might want to make one swap partition or
+   file for 'normal' use, and another for Suspend2 to activate & deactivate
+   automatically. (Further details below).
+
+   ii) Normal files.
+
+   Suspend2 includes a 'filewriter'. The filewriter can store your image in a
+   simple file. Since Linux has the idea of everything being a file, this is
+   more powerful than it initially sounds. If, for example, you were to set up
+   a network block device file, you could suspend to a network server. This has
+   been tested and works to a point, but nbd itself isn't stateless enough for
+   our purposes.
+
+   Take extra care when setting up the filewriter. If you just type commands
+   without thinking and then try to suspend, you could cause irreversible
+   corruption on your filesystems! Make sure you have backups.
+
+   Most people will only want to suspend to a local file. To achieve that, do
+   something along the lines of:
+
+   echo "Suspend2" > /suspend-file
+   dd if=/dev/zero bs=1M count=512 >> suspend-file
+
+   This will create a 512MB file called /suspend-file. To get Suspend2 to use
+   it:
+
+   echo /suspend-file > /sys/power/suspend2/filewriter/filewriter_target
+
+   Then
+
+   cat /sys/power/suspend2/resume2
+
+   Put the results of this into your bootloader's configuration (see also step
+   C, below:
+
+   ---EXAMPLE-ONLY-DON'T-COPY-AND-PASTE---
+   # cat /sys/power/suspend2/resume2
+   file:/dev/hda2:0x1e001
+   
+   In this example, we would edit the append= line of our lilo.conf|menu.lst
+   so that it included:
+
+   resume2=file:/dev/hda2:0x1e001
+   ---EXAMPLE-ONLY-DON'T-COPY-AND-PASTE---
+ 
+   For those who are thinking 'Could I make the file sparse?', the answer is
+   'No!'. At the moment, there is no way for Suspend2 to fill in the holes in
+   a sparse file while suspending. In the longer term (post merge!), I'd like
+   to change things so that the file could be dynamically resized as needed.
+   Right now, however, that's not possible and not a priority.
+
+   c. Bootloader configuration.
+   
+   Using Suspend2 also requires that you add an extra parameter to 
+   your lilo.conf or equivalent. Here's an example for a swap partition:
+
+   append="resume2=swap:/dev/hda1"
+
+   This would tell Suspend2 that /dev/hda1 is a swap partition you 
+   have. Suspend2 will use the swap signature of this partition as a
+   pointer to your data when you suspend. This means that (in this example)
+   /dev/hda1 doesn't need to be _the_ swap partition where all of your data
+   is actually stored. It just needs to be a swap partition that has a
+   valid signature.
+
+   You don't need to have a swap partition for this purpose. Suspend2
+   can also use a swap file, but usage is a little more complex. Having made
+   your swap file, turn it on and do 
+
+   cat /sys/power/suspend2/swapwriter/headerlocations
+
+   (this assumes you've already compiled your kernel with Suspend2
+   support and booted it). The results of the cat command will tell you
+   what you need to put in lilo.conf:
+
+   For swap partitions like /dev/hda1, simply use resume2=/dev/hda1.
+   For swapfile `swapfile`, use resume2=swap:/dev/hda2:0x242d.
+
+   If the swapfile changes for any reason (it is moved to a different
+   location, it is deleted and recreated, or the filesystem is
+   defragmented) then you will have to check
+   /sys/power/suspend2/swapwriter/headerlocations for a new resume_block value.
+
+   Once you've compiled and installed the kernel and adjusted your bootloader
+   configuration, you should only need to reboot for the most basic part
+   of Suspend2 to be ready.
+
+   If you only compile in the swapwriter, or only compile in the filewriter,
+   you don't need to add the "swap:" part of the resume2= parameters above.
+   resume2=/dev/hda2:0x242d will work just as well.
+
+   d. The hibernate script.
+
+   Since the driver model in 2.6 kernels is still being developed, you may need
+   to do more, however. Users of Suspend2 usually start the process via a script
+   which prepares for the suspend, tells the kernel to do its stuff and then
+   restore things afterwards. This script might involve:
+
+   - Switching to a text console and back if X doesn't like the video card
+     status on resume.
+   - Un/reloading PCMCIA support since it doesn't play well with suspend.
+  
+   Note that you might not be able to unload some drivers if there are 
+   processes using them. You might have to kill off processes that hold
+   devices open. Hint: if your X server accesses an USB mouse, doing a
+   'chvt' to a text console releases the device and you can unload the
+   module.
+
+   Check out the latest script (available on suspend2.net).
+   
+4. Why not just use the version already in the kernel?
+
+   The version in the vanilla kernel has a number of drawbacks. Among these:
+	- it has a maximum image size of 1/2 total memory.
+	- it doesn't allocate storage until after it has snapshotted memory.
+	  This means that you can't be sure suspending will work until you
+	  see it start to write the image.
+	- it performs all of it's I/O synchronously.
+	- it does not allow you to press escape to cancel a cycle
+	- it does not allow you to automatically swapon a file when
+	  starting a cycle.
+	- it does not allow you to use multiple swap partitions.
+	- it does not allow you to use swapfiles.
+	- it does not allow you to use ordinary files.
+	- it just invalidates an image and continues to boot if you
+	  accidentally boot the wrong kernel after suspending.
+	- it doesn't support any sort of nice display while suspending
+	- it is moving toward requiring that you have an initrd/initramfs
+	  to ever have a hope of resuming (uswsusp). While uswsusp will
+	  address some of the concerns above, it won't address all, and
+	  will be more complicated to get set up.
+
+5. How do you use it?
+
+   A suspend cycle can be started directly by doing:
+
+	echo > /sys/power/suspend2/do_resume
+
+   In practice, though, you'll probably want to use the hibernate script
+   to unload modules, configure the kernel the way you like it and so on.
+   In that case, you'd do (as root):
+
+	hibernate
+
+   See the hibernate script's man page for more details on the options it
+   takes.
+
+   If you're using the text or splash user interface modules, one neat feature
+   of Suspend2 that you might find useful is that you can press Escape at any
+   time during suspending, and the process will be aborted.
+   
+   Due to the way suspend works, this means you'll have your system back and
+   perfectly usable almost instantly. The only exception is when it's at the
+   very end of writing the image. Then it will need to reload a small (
+   usually 4-50MBs, depending upon the image characteristics) portion first.
+
+   If you run into problems with resuming, adding the "noresume2" option to
+   the kernel command line will let you skip the resume step and recover your
+   system.
+
+6. What do all those entries in /sys/power/suspend2 do?
+
+   /sys/power/suspend2 is the directory which contains files you can use to
+   tune and configure Suspend2 to your liking. The exact contents of
+   the directory will depend upon the version of Suspend2 you're
+   running and the options you selected at compile time. In the following
+   descriptions, names in brackets refer to compile time options.
+   (Note that they're all dependant upon you having selected CONFIG_SUSPEND2
+   in the first place!).
+
+   Since the values of these settings can open potential security risks, they
+   are usually accessible only to the root user. You can, however, enable a
+   compile time option which makes all of these files world-accessible. This
+   should only be done if you trust everyone with shell access to this
+   computer!
+  
+   - compression/algorithm
+
+   Set the cryptoapi algorithm used for compressing the image.
+
+   - compression/expected_compression
+
+   These values allow you to set an expected compression ratio, which Software
+   Suspend will use in calculating whether it meets constraints on the image
+   size. If this expected compression ratio is not attained, the suspend will
+   abort, so it is wise to allow some spare. You can see what compression
+   ratio is achieved in the logs after suspending.
+
+   - debug_info:
+  
+   This file returns information about your configuration that may be helpful
+   in diagnosing problems with suspending.
+
+   - do_resume:
+
+   When anything is written to this file suspend will attempt to read and
+   restore an image. If there is no image, it will return almost immediately.
+   If an image exists, the echo > will never return. Instead, the original
+   kernel context will be restored and the original echo > do_suspend will
+   return.
+
+   - do_suspend:
+
+   When anything is written to this file, the kernel side of Suspend2 will
+   begin to attempt to write an image to disk and power down. You'll normally
+   want to run the hibernate script instead, to get modules unloaded first.
+
+   - driver_model_beeping
+
+   Enable beeping when suspending and resuming the drivers. Might help with
+   determining where a problem in resuming occurs.
+
+   - */enabled
+
+   These option can be used to temporarily disable various parts of suspend.
+
+   - encryption/*
+
+   The iv, key, save_key_and_iv, mode and algorithm values allow you to
+   select a cryptoapi encryption algoritm, set the iv and key and whether
+   they are saved in the image header. Saving the iv and key in the image
+   header is of course less secure than having them on some external device,
+   such as a USB key. If you want to use a USB key, you'll need to write
+   some scripting in your initrd/ramfs to retrieve the key & iv from your
+   USB key and put them into the entries again prior to doing the echo to
+   do_resume.
+
+   - extra_pages_allowance
+
+   When Suspend2 does its atomic copy, it calls the driver model suspend
+   and resume methods. If you have DRI enabled with a driver such as fglrx,
+   this can result in the driver allocating a substantial amount of memory
+   for storing its state. Extra_pages_allowance tells suspend2 how much
+   extra memory it should ensure is available for those allocations. If
+   your attempts at suspending end with a message in dmesg indicating that
+   insufficient extra pages were allowed, you need to increase this value.
+
+   - filewriter/target:
+
+   Read this value to get the current setting. Write to it to point Suspend
+   at a new storage location for the filewriter. See above for details of how
+   to set up the filewriter.
+
+   - freezer_test
+
+   This entry can be used to get Suspend2 to just test the freezer without
+   actually doing a suspend cycle. It is useful for diagnosing freezing
+   issues.
+
+   - image_exists:
+
+   Can be used in a script to determine whether a valid image exists at the
+   location currently pointed to by resume2=. Returns up to three lines.
+   The first is whether an image exists (-1 for unsure, otherwise 0 or 1).
+   If an image eixsts, additional lines will return the machine and version.
+   Echoing anything to this entry removes any current image.
+
+   - image_size_limit:
+
+   The maximum size of suspend image written to disk, measured in megabytes
+   (1024*1024).
+
+   - interface_version:
+
+   The value returned by this file can be used by scripts and configuration
+   tools to determine what entries should be looked for. The value is
+   incremented whenever an entry in /sys/power/suspend2 is obsoleted or 
+   added.
+
+   - last_result:
+
+   The result of the last suspend, as defined in
+   include/linux/suspend-debug.h with the values SUSPEND_ABORTED to
+   SUSPEND_KEPT_IMAGE. This is a bitmask.
+
+   - log_everything (CONFIG_PM_DEBUG):
+
+   Setting this option results in all messages printed being logged. Normally,
+   only a subset are logged, so as to not slow the process and not clutter the
+   logs. Useful for debugging. It can be toggled during a cycle by pressing
+   'L'.
+
+   - pause_between_steps (CONFIG_PM_DEBUG):
+
+   This option is used during debugging, to make Suspend2 pause between
+   each step of the process. It is ignored when the nice display is on.
+
+   - powerdown_method:
+
+   Used to select a method by which Suspend2 should powerdown after writing the
+   image. Currently:
+
+   0: Don't use ACPI to power off.
+   3: Attempt to enter Suspend-to-ram.
+   4: Attempt to enter ACPI S4 mode.
+   5: Attempt to power down via ACPI S5 mode.
+
+   Note that these options are highly dependant upon your hardware & software:
+
+   3: When succesful, your machine suspends-to-ram instead of powering off.
+      The advantage of using this mode is that it doesn't matter whether your
+      battery has enough charge to make it through to your next resume. If it
+      lasts, you will simply resume from suspend to ram (and the image on disk
+      will be discarded). If the battery runs out, you will resume from disk
+      instead. The disadvantage is that it takes longer than a normal
+      suspend-to-ram to enter the state, since the suspend-to-disk image needs
+      to be written first.
+   4/5: When successful, your machine will be off and comsume (almost) no power.
+      But it might still react to some external events like opening the lid or
+      trafic on  a network or usb device. For the bios, resume is then the same
+      as warm boot, similar to a situation where you used the command `reboot'
+      to reboot your machine. If your machine has problems on warm boot or if
+      you want to protect your machine with the bios password, this is probably
+      not the right choice. Mode 4 may be necessary on some machines where ACPI
+      wake up methods need to be run to properly reinitialise hardware after a
+      suspend-to-disk cycle.  
+   0: Switch the machine completely off. The only possible wakeup is the power
+      button. For the bios, resume is then the same as a cold boot, in
+      particular you would  have to provide your bios boot password if your
+      machine uses that feature for booting.
+
+   - progressbar_granularity_limit:
+
+   This option can be used to limit the granularity of the progress bar
+   displayed with a bootsplash screen. The value is the maximum number of
+   steps. That is, 10 will make the progress bar jump in 10% increments.
+
+   - reboot:
+
+   This option causes Suspend2 to reboot rather than powering down
+   at the end of saving an image. It can be toggled during a cycle by pressing
+   'R'.
+
+   - resume_commandline:
+
+   This entry can be read after resuming to see the commandline that was used
+   when resuming began. You might use this to set up two bootloader entries
+   that are the same apart from the fact that one includes a extra append=
+   argument "at_work=1". You could then grep resume_commandline in your
+   post-resume scripts and configure networking (for example) differently
+   depending upon whether you're at home or work. resume_commandline can be
+   set to arbitrary text if you wish to remove sensitive contents.
+
+   - swapwriter/swapfilename:
+
+   This entry is used to specify the swapfile or partition that
+   Suspend2 will attempt to swapon/swapoff automatically. Thus, if
+   I normally use /dev/hda1 for swap, and want to use /dev/hda2 for specifically
+   for my suspend image, I would
+  
+   echo /dev/hda2 > /sys/power/suspend2/swapwriter/swapfile
+
+   /dev/hda2 would then be automatically swapon'd and swapoff'd. Note that the
+   swapon and swapoff occur while other processes are frozen (including kswapd)
+   so this swap file will not be used up when attempting to free memory. The
+   parition/file is also given the highest priority, so other swapfiles/partitions
+   will only be used to save the image when this one is filled.
+
+   The value of this file is used by headerlocations along with any currently
+   activated swapfiles/partitions.
+
+   - swapwriter/headerlocations:
+
+   This option tells you the resume2= options to use for swap devices you
+   currently have activated. It is particularly useful when you only want to
+   use a swap file to store your image. See above for further details.
+
+   - toggle_process_nofreeze
+
+   This entry can be used to toggle the NOFREEZE flag on a process, to allow it
+   to run during Suspending. It should be used with extreme caution. There are
+   strict limitations on what a process running during suspend can do. This is
+   really only intended for use by Suspend's helpers (userui in particular).
+
+   - userui_program
+
+   This entry is used to tell Suspend what userspace program to use for
+   providing a user interface while suspending. The program uses a netlink
+   socket to pass messages back and forward to the kernel, allowing all of the
+   functions formerly implemented in the kernel user interface components.
+
+   - user_interface/debug_sections (CONFIG_PM_DEBUG):
+
+   This value, together with the console log level, controls what debugging
+   information is displayed. The console log level determines the level of
+   detail, and this value determines what detail is displayed. This value is
+   a bit vector, and the meaning of the bits can be found in the kernel tree
+   in include/linux/suspend2.h. It can be overridden using the kernel's
+   command line option suspend_dbg.
+
+   - user_interface/default_console_level (CONFIG_PM_DEBUG):
+
+   This determines the value of the console log level at the start of a
+   suspend cycle. If debugging is compiled in, the console log level can be
+   changed during a cycle by pressing the digit keys. Meanings are:
+
+   0: Nice display.
+   1: Nice display plus numerical progress.
+   2: Errors only.
+   3: Low level debugging info.
+   4: Medium level debugging info.
+   5: High level debugging info.
+   6: Verbose debugging info.
+
+   - user_interface/enable_escape:
+
+   Setting this to "1" will enable you abort a suspend by
+   pressing escape, "0" (default) disables this feature. Note that enabling
+   this option means that you cannot initiate a suspend and then walk away
+   from your computer, expecting it to be secure. With feature disabled,
+   you can validly have this expectation once Suspend begins to write the
+   image to disk. (Prior to this point, it is possible that Suspend might
+   about because of failure to freeze all processes or because constraints
+   on its ability to save the image are not met).
+
+   - version:
+  
+   The version of suspend you have compiled into the currently running kernel.
+
+7. How do you get support?
+
+   Glad you asked. Suspend2 is being actively maintained and supported
+   by Nigel (the guy doing most of the kernel coding at the moment), Bernard
+   (who maintains the hibernate script and userspace user interface components)
+   and its users.
+
+   Resources availble include HowTos, FAQs and a Wiki, all available via
+   suspend2.net.  You can find the mailing lists there.
+
+8. I think I've found a bug. What should I do?
+
+   By far and a way, the most common problems people have with suspend2
+   related to drivers not having adequate power management support. In this
+   case, it is not a bug with suspend2, but we can still help you. As we
+   mentioned above, such issues can usually be worked around by building the
+   functionality as modules and unloading them while suspending. Please visit
+   the Wiki for up-to-date lists of known issues and work arounds.
+
+   If this information doesn't help, try running:
+
+   hibernate --bug-report
+
+   ..and sending the output to the users mailing list.
+
+   Good information on how to provide us with useful information from an
+   oops is found in the file REPORTING-BUGS, in the top level directory
+   of the kernel tree. If you get an oops, please especially note the
+   information about running what is printed on the screen through ksymoops.
+   The raw information is useless.
+
+9. When will XXX be supported?
+
+   If there's a feature missing from Suspend2 that you'd like, feel free to
+   ask. We try to be obliging, within reason.
+
+   Patches are welcome. Please send to the list.
+
+10. How does it work?
+
+   Suspend2 does its work in a number of steps.
+
+   a. Freezing system activity.
+
+   The first main stage in suspending is to stop all other activity. This is
+   achieved in stages. Processes are considered in fours groups, which we will
+   describe in reverse order for clarity's sake: Threads with the PF_NOFREEZE
+   flag, kernel threads without this flag, userspace processes with the
+   PF_SYNCTHREAD flag and all other processes. The first set (PF_NOFREEZE) are
+   untouched by the refrigerator code. They are allowed to run during suspending
+   and resuming, and are used to support user interaction, storage access or the
+   like. Other kernel threads (those unneeded while suspending) are frozen last.
+   This leaves us with userspace processes that need to be frozen. When a
+   process enters one of the *_sync system calls, we set a PF_SYNCTHREAD flag on
+   that process for the duration of that call. Processes that have this flag are
+   frozen after processes without it, so that we can seek to ensure that dirty
+   data is synced to disk as quickly as possible in a situation where other
+   processes may be submitting writes at the same time. Freezing the processes
+   that are submitting data stops new I/O from being submitted. Syncthreads can
+   then cleanly finish their work. So the order is:
+
+   - Userspace processes without PF_SYNCTHREAD or PF_NOFREEZE;
+   - Userspace processes with PF_SYNCTHREAD (they won't have NOFREEZE);
+   - Kernel processes without PF_NOFREEZE.
+
+   b. Eating memory.
+
+   For a successful suspend, you need to have enough disk space to store the
+   image and enough memory for the various limitations of Suspend2's
+   algorithm. You can also specify a maximum image size. In order to attain
+   to those constraints, Suspend2 may 'eat' memory. If, after freezing
+   processes, the constraints aren't met, Suspend2 will thaw all the
+   other processes and begin to eat memory until its calculations indicate
+   the constraints are met. It will then freeze processes again and recheck
+   its calculations.
+
+   c. Allocation of storage.
+
+   Next, Suspend2 allocates the storage that will be used to save
+   the image.
+
+   The core of Suspend2 knows nothing about how or where pages are stored. We
+   therefore request the active writer (remember you might have compiled in
+   more than one!) to allocate enough storage for our expect image size. If
+   this request cannot be fulfilled, we eat more memory and try again. If it
+   is fulfiled, we seek to allocate additional storage, just in case our
+   expected compression ratio (if any) isn't achieved. This time, however, we
+   just continue if we can't allocate enough storage.
+
+   If these calls to our writer change the characteristics of the image such
+   that we haven't allocated enough memory, we also loop. (The writer may well
+   need to allocate space for its storage information).
+
+   d. Write the first part of the image.
+
+   Suspend2 stores the image in two sets of pages called 'pagesets'.
+   Pageset 2 contains pages on the active and inactive lists; essentially
+   the page cache. Pageset 1 contains all other pages, including the kernel.
+   We use two pagesets for one important reason: We need to make an atomic copy
+   of the kernel to ensure consistency of the image. Without a second pageset,
+   that would limit us to an image that was at most half the amount of memory
+   available. Using two pagesets allows us to store a full image. Since pageset
+   2 pages won't be needed in saving pageset 1, we first save pageset 2 pages.
+   We can then make our atomic copy of the remaining pages using both pageset 2
+   pages and any other pages that are free. While saving both pagesets, we are
+   careful not to corrupt the image. Among other things, we use lowlevel block
+   I/O routines that don't change the pagecache contents.
+
+   The next step, then, is writing pageset 2.
+
+   e. Suspending drivers and storing processor context.
+
+   Having written pageset2, Suspend2 calls the power management functions to
+   notify drivers of the suspend, and saves the processor state in preparation
+   for the atomic copy of memory we are about to make.
+
+   f. Atomic copy.
+
+   At this stage, everything else but the Suspend2 code is halted. Processes
+   are frozen or idling, drivers are quiesced and have stored (ideally and where
+   necessary) their configuration in memory we are about to atomically copy.
+   In our lowlevel architecture specific code, we have saved the CPU state.
+   We can therefore now do our atomic copy before resuming drivers etc.
+
+   g. Save the atomic copy (pageset 1).
+
+   Suspend can then write the atomic copy of the remaining pages. Since we
+   have copied the pages into other locations, we can continue to use the
+   normal block I/O routines without fear of corruption our image.
+
+   f. Save the suspend header.
+
+   Nearly there! We save our settings and other parameters needed for
+   reloading pageset 1 in a 'suspend header'. We also tell our writer to
+   serialise its data at this stage, so that it can reread the image at resume
+   time. Note that the writer can write this data in any format - in the case
+   of the swapwriter, for example, it splits header pages in 4092 byte blocks,
+   using the last four bytes to link pages of data together. This is completely
+   transparent to the core.
+
+   g. Set the image header.
+
+   Finally, we edit the header at our resume2= location. The signature is
+   changed by the writer to reflect the fact that an image exists, and to point
+   to the start of that data if necessary (swapwriter).
+
+   h. Power down.
+
+   Or reboot if we're debugging and the appropriate option is selected.
+
+   Whew!
+
+   Reloading the image.
+   --------------------
+
+   Reloading the image is essentially the reverse of all the above. We load
+   our copy of pageset 1, being careful to choose locations that aren't going
+   to be overwritten as we copy it back (We start very early in the boot
+   process, so there are no other processes to quiesce here). We then copy
+   pageset 1 back to its original location in memory and restore the process
+   context. We are now running with the original kernel. Next, we reload the
+   pageset 2 pages, free the memory and swap used by Suspend2, restore
+   the pageset header and restart processes. Sounds easy in comparison to
+   suspending, doesn't it!
+
+   There is of course more to Suspend2 than this, but this explanation
+   should be a good start. If there's interest, I'll write further
+   documentation on range pages and the low level I/O.
+
+11. Who wrote Suspend2?
+
+   (Answer based on the writings of Florent Chabaud, credits in files and
+   Nigel's limited knowledge; apologies to anyone missed out!)
+
+   The main developers of Suspend2 have been...
+
+   Gabor Kuti
+   Pavel Machek
+   Florent Chabaud
+   Bernard Blackham
+   Nigel Cunningham
+
+   They have been aided in their efforts by a host of hundreds, if not thousands
+   of testers and people who have submitted bug fixes & suggestions. Of special
+   note are the efforts of Michael Frank, who had his computers repetitively
+   suspend and resume for literally tens of thousands of cycles and developed
+   scripts to stress the system and test Suspend2 far beyond the point
+   most of us (Nigel included!) would consider testing. His efforts have
+   contributed as much to Suspend2 as any of the names above.
diff -urN oldtree/MAINTAINERS newtree/MAINTAINERS
--- oldtree/MAINTAINERS	2006-10-05 15:26:55.000000000 -0400
+++ newtree/MAINTAINERS	2006-10-06 17:28:20.000000000 -0400
@@ -2882,6 +2882,13 @@
 W:	http://sammy.net/sun3/
 S:	Maintained
 
+SUSPEND2
+P:	Nigel Cunningham
+M:	nigel@suspend2.net
+L:	suspend2-devel@suspend2.net
+W:	http://suspend2.net
+S:	Maintained
+
 SVGA HANDLING
 P:	Martin Mares
 M:	mj@ucw.cz
diff -urN oldtree/arch/i386/mm/init.c newtree/arch/i386/mm/init.c
--- oldtree/arch/i386/mm/init.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/arch/i386/mm/init.c	2006-10-06 17:28:20.000000000 -0400
@@ -385,7 +385,7 @@
 #endif
 }
 
-#if defined(CONFIG_SOFTWARE_SUSPEND) || defined(CONFIG_ACPI_SLEEP)
+#if defined(CONFIG_SUSPEND_SHARED) || defined(CONFIG_ACPI_SLEEP)
 /*
  * Swap suspend & friends need this for resume because things like the intel-agp
  * driver might have split up a kernel 4MB mapping.
diff -urN oldtree/arch/i386/power/Makefile newtree/arch/i386/power/Makefile
--- oldtree/arch/i386/power/Makefile	2006-10-05 15:26:55.000000000 -0400
+++ newtree/arch/i386/power/Makefile	2006-10-06 17:28:20.000000000 -0400
@@ -1,2 +1,2 @@
 obj-$(CONFIG_PM)		+= cpu.o
-obj-$(CONFIG_SOFTWARE_SUSPEND)	+= swsusp.o
+obj-$(CONFIG_SUSPEND_SHARED)	+= swsusp.o
diff -urN oldtree/arch/powerpc/kernel/Makefile newtree/arch/powerpc/kernel/Makefile
--- oldtree/arch/powerpc/kernel/Makefile	2006-10-05 15:26:55.000000000 -0400
+++ newtree/arch/powerpc/kernel/Makefile	2006-10-06 17:28:20.000000000 -0400
@@ -36,7 +36,7 @@
 obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o
 obj-$(CONFIG_6xx)		+= idle_6xx.o l2cr_6xx.o cpu_setup_6xx.o
 obj-$(CONFIG_TAU)		+= tau_6xx.o
-obj32-$(CONFIG_SOFTWARE_SUSPEND) += swsusp_32.o
+obj32-$(CONFIG_SUSPEND_SHARED)	+= swsusp_32.o
 obj32-$(CONFIG_MODULES)		+= module_32.o
 obj-$(CONFIG_E500)		+= perfmon_fsl_booke.o
 
diff -urN oldtree/arch/x86_64/kernel/Makefile newtree/arch/x86_64/kernel/Makefile
--- oldtree/arch/x86_64/kernel/Makefile	2006-10-05 15:26:55.000000000 -0400
+++ newtree/arch/x86_64/kernel/Makefile	2006-10-06 17:28:20.000000000 -0400
@@ -26,7 +26,7 @@
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o crash.o
 obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o
 obj-$(CONFIG_PM)		+= suspend.o
-obj-$(CONFIG_SOFTWARE_SUSPEND)	+= suspend_asm.o
+obj-$(CONFIG_SUSPEND_SHARED)	+= suspend_asm.o
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq/
 obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
 obj-$(CONFIG_IOMMU)		+= pci-gart.o aperture.o
diff -urN oldtree/arch/x86_64/kernel/suspend.c newtree/arch/x86_64/kernel/suspend.c
--- oldtree/arch/x86_64/kernel/suspend.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/arch/x86_64/kernel/suspend.c	2006-10-06 17:28:20.000000000 -0400
@@ -140,7 +140,7 @@
 
 }
 
-#ifdef CONFIG_SOFTWARE_SUSPEND
+#ifdef CONFIG_SUSPEND_SHARED
 /* Defined in arch/x86_64/kernel/suspend_asm.S */
 extern int restore_image(void);
 
@@ -209,6 +209,12 @@
 	return 0;
 }
 
+#ifdef CONFIG_SUSPEND2
+void suspend2_beep(int suspending);
+#else
+#define suspend2_beep(num) do { } while(0)
+#endif
+
 int swsusp_arch_resume(void)
 {
 	int error;
@@ -216,7 +222,8 @@
 	/* We have got enough memory and from now on we cannot recover */
 	if ((error = set_up_temporary_mappings()))
 		return error;
+	suspend2_beep(0);
 	restore_image();
 	return 0;
 }
-#endif /* CONFIG_SOFTWARE_SUSPEND */
+#endif /* CONFIG_SUSPEND_SHARED */
diff -urN oldtree/arch/x86_64/kernel/time.c newtree/arch/x86_64/kernel/time.c
--- oldtree/arch/x86_64/kernel/time.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/arch/x86_64/kernel/time.c	2006-10-06 17:28:20.000000000 -0400
@@ -1064,9 +1064,9 @@
 #endif
 	} else
 		vxtime.last_tsc = get_cycles_sync();
-	write_sequnlock_irqrestore(&xtime_lock,flags);
 	jiffies += sleep_length;
 	monotonic_base += sleep_length * (NSEC_PER_SEC/HZ);
+	write_sequnlock_irqrestore(&xtime_lock,flags);
 	touch_softlockup_watchdog();
 	return 0;
 }
diff -urN oldtree/block/ll_rw_blk.c newtree/block/ll_rw_blk.c
--- oldtree/block/ll_rw_blk.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/block/ll_rw_blk.c	2006-10-06 17:28:20.000000000 -0400
@@ -28,6 +28,8 @@
 #include <linux/interrupt.h>
 #include <linux/cpu.h>
 #include <linux/blktrace_api.h>
+#include <linux/freezer.h>
+#include <linux/suspend.h>
 
 /*
  * for max sense size
@@ -3242,6 +3244,10 @@
 			bdevname(bio->bi_bdev,b));
 	}
 
+	BUG_ON( test_suspend_state(SUSPEND_RUNNING) &&	/* Suspend2, that is */
+		test_freezer_state(FREEZING_COMPLETE) &&
+		!(bio->bi_flags & (1 << BIO_SUSPEND_DATA)));
+	
 	generic_make_request(bio);
 }
 
diff -urN oldtree/crypto/Kconfig newtree/crypto/Kconfig
--- oldtree/crypto/Kconfig	2006-10-05 15:26:55.000000000 -0400
+++ newtree/crypto/Kconfig	2006-10-06 17:28:20.000000000 -0400
@@ -403,6 +403,13 @@
 	  
 	  You will most probably want this if using IPSec.
 
+config CRYPTO_LZF
+	tristate "LZF compression algorithm"
+	depends on CRYPTO
+	help
+	  This is the LZF algorithm. It is especially useful for Suspend2,
+	  because it achieves good compression quickly.
+
 config CRYPTO_MICHAEL_MIC
 	tristate "Michael MIC keyed digest algorithm"
 	select CRYPTO_ALGAPI
diff -urN oldtree/crypto/Makefile newtree/crypto/Makefile
--- oldtree/crypto/Makefile	2006-10-05 15:26:55.000000000 -0400
+++ newtree/crypto/Makefile	2006-10-06 17:28:20.000000000 -0400
@@ -40,5 +40,6 @@
 obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o
 obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o
 obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o
+obj-$(CONFIG_CRYPTO_LZF) += lzf.o
 
 obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
diff -urN oldtree/crypto/lzf.c newtree/crypto/lzf.c
--- oldtree/crypto/lzf.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/crypto/lzf.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,335 @@
+/* 
+ * Cryptoapi LZF compression module.
+ *
+ * Copyright (c) 2004-2005 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * based on the deflate.c file:
+ * 
+ * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
+ * 
+ * and upon the LZF compression module donated to the Suspend2 project with
+ * the following copyright:
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ * Copyright (c) 2000-2003 Marc Alexander Lehmann <pcg@goof.com>
+ * 
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ * 
+ *   1.  Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ * 
+ *   2.  Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ * 
+ *   3.  The name of the author may not be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
+ * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
+ * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
+ * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License version 2 (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of the above. If you wish to
+ * allow the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the
+ * BSD license, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL. If
+ * you do not delete the provisions above, a recipient may use your version
+ * of this file under either the BSD or the GPL.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <linux/vmalloc.h>
+#include <asm/string.h>
+
+struct lzf_ctx {
+	void *hbuf;
+	unsigned int bufofs;
+};
+
+/*
+ * size of hashtable is (1 << hlog) * sizeof (char *)
+ * decompression is independent of the hash table size
+ * the difference between 15 and 14 is very small
+ * for small blocks (and 14 is also faster).
+ * For a low-memory configuration, use hlog == 13;
+ * For best compression, use 15 or 16.
+ */
+static const int hlog = 14;
+
+/*
+ * don't play with this unless you benchmark!
+ * decompression is not dependent on the hash function
+ * the hashing function might seem strange, just believe me
+ * it works ;)
+ */
+static inline u16 first(const u8 *p)
+{
+	return ((p[0]) << 8) + p[1];
+}
+
+static inline u16 next(u8 v, const u8 *p)
+{
+	return ((v) << 8) + p[2];
+}
+
+static inline u32 idx(unsigned int h)
+{
+	return (((h ^ (h << 5)) >> (3*8 - hlog)) + h*3) & ((1 << hlog) - 1);
+}
+
+/*
+ * IDX works because it is very similar to a multiplicative hash, e.g.
+ * (h * 57321 >> (3*8 - hlog))
+ * the next one is also quite good, albeit slow ;)
+ * (int)(cos(h & 0xffffff) * 1e6)
+ */
+
+static const int max_lit = (1 <<  5);
+static const int max_off = (1 << 13);
+static const int max_ref = ((1 <<  8) + (1 << 3));
+
+/*
+ * compressed format
+ *
+ * 000LLLLL <L+1>    ; literal
+ * LLLOOOOO oooooooo ; backref L
+ * 111OOOOO LLLLLLLL oooooooo ; backref L+7
+ *
+ */
+
+static void lzf_compress_exit(struct crypto_tfm *tfm)
+{
+	struct lzf_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	if (ctx->hbuf) {
+		vfree(ctx->hbuf);
+		ctx->hbuf = NULL;
+	}
+}
+
+static int lzf_compress_init(struct crypto_tfm *tfm)
+{
+	struct lzf_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	/* Get LZF ready to go */
+	ctx->hbuf = vmalloc_32((1 << hlog) * sizeof(char *));
+	if (!ctx->hbuf) {
+		printk(KERN_WARNING
+		       "Failed to allocate %ld bytes for lzf workspace\n",
+		       (long) ((1 << hlog) * sizeof(char *)));
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static int lzf_compress(struct crypto_tfm *tfm, const u8 *in_data,
+		unsigned int in_len, u8 *out_data, unsigned int *out_len)
+{
+	struct lzf_ctx *ctx = crypto_tfm_ctx(tfm);
+	const u8 **htab = ctx->hbuf;
+	const u8 **hslot;
+	const u8 *ip = in_data;
+	u8 *op = out_data;
+	const u8 *in_end = ip + in_len;
+	u8 *out_end = op + *out_len - 3;
+	const u8 *ref;
+
+	unsigned int hval = first(ip);
+	unsigned long off;
+	int lit = 0;
+
+	memset(htab, 0, sizeof(htab));
+
+	for (;;) {
+		if (ip < in_end - 2) {
+			hval = next(hval, ip);
+			hslot = htab + idx(hval);
+			ref = *hslot;
+			*hslot = ip;
+
+			if ((off = ip - ref - 1) < max_off
+			    && ip + 4 < in_end && ref > in_data
+			    && *(u16 *) ref == *(u16 *) ip && ref[2] == ip[2]
+			    ) {
+				/* match found at *ref++ */
+				unsigned int len = 2;
+				unsigned int maxlen = in_end - ip - len;
+				maxlen = maxlen > max_ref ? max_ref : maxlen;
+
+				do
+					len++;
+				while (len < maxlen && ref[len] == ip[len]);
+
+				if (op + lit + 1 + 3 >= out_end) {
+					*out_len = PAGE_SIZE;
+					return 0;
+				}
+
+				if (lit) {
+					*op++ = lit - 1;
+					lit = -lit;
+					do
+						*op++ = ip[lit];
+					while (++lit);
+				}
+
+				len -= 2;
+				ip++;
+
+				if (len < 7) {
+					*op++ = (off >> 8) + (len << 5);
+				} else {
+					*op++ = (off >> 8) + (7 << 5);
+					*op++ = len - 7;
+				}
+
+				*op++ = off;
+
+				ip += len;
+				hval = first(ip);
+				hval = next(hval, ip);
+				htab[idx(hval)] = ip;
+				ip++;
+				continue;
+			}
+		} else if (ip == in_end)
+			break;
+
+		/* one more literal byte we must copy */
+		lit++;
+		ip++;
+
+		if (lit == max_lit) {
+			if (op + 1 + max_lit >= out_end) {
+				*out_len = PAGE_SIZE;
+				return 0;
+			}
+
+			*op++ = max_lit - 1;
+			memcpy(op, ip - max_lit, max_lit);
+			op += max_lit;
+			lit = 0;
+		}
+	}
+
+	if (lit) {
+		if (op + lit + 1 >= out_end) {
+			*out_len = PAGE_SIZE;
+			return 0;
+		}
+
+		*op++ = lit - 1;
+		lit = -lit;
+		do
+			*op++ = ip[lit];
+		while (++lit);
+	}
+
+	*out_len = op - out_data;
+	return 0;
+}
+
+static int lzf_decompress(struct crypto_tfm *tfm, const u8 *src,
+		unsigned int slen, u8 *dst, unsigned int *dlen)
+{
+	u8 const *ip = src;
+	u8 *op = dst;
+	u8 const *const in_end = ip + slen;
+	u8 *const out_end = op + *dlen;
+
+	do {
+		unsigned int ctrl = *ip++;
+
+		if (ctrl < (1 << 5)) {	/* literal run */
+			ctrl++;
+
+			if (op + ctrl > out_end) {
+				*dlen = PAGE_SIZE;
+				return 0;
+			}
+			memcpy(op, ip, ctrl);
+			op += ctrl;
+			ip += ctrl;
+		} else {	/* back reference */
+
+			unsigned int len = ctrl >> 5;
+
+			u8 *ref = op - ((ctrl & 0x1f) << 8) - 1;
+
+			if (len == 7)
+				len += *ip++;
+
+			ref -= *ip++;
+
+			if (op + len + 2 > out_end) {
+				*dlen = PAGE_SIZE;
+				return 0;
+			}
+
+			if (ref < (u8 *) dst) {
+				*dlen = PAGE_SIZE;
+				return 0;
+			}
+
+			*op++ = *ref++;
+			*op++ = *ref++;
+
+			do
+				*op++ = *ref++;
+			while (--len);
+		}
+	}
+	while (op < out_end && ip < in_end);
+
+	*dlen = op - (u8 *) dst;
+	return 0;
+}
+
+static struct crypto_alg alg = {
+	.cra_name = "lzf",
+	.cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
+	.cra_ctxsize = 0,
+	.cra_module = THIS_MODULE,
+	.cra_list = LIST_HEAD_INIT(alg.cra_list),
+	.cra_init = lzf_compress_init,
+	.cra_exit = lzf_compress_exit,
+	.cra_u = { .compress = {
+	.coa_compress = lzf_compress,
+	.coa_decompress = lzf_decompress } }
+};
+
+static int __init init(void)
+{
+	return crypto_register_alg(&alg);
+}
+
+static void __exit fini(void)
+{
+	crypto_unregister_alg(&alg);
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LZF Compression Algorithm");
+MODULE_AUTHOR("Marc Alexander Lehmann & Nigel Cunningham");
diff -urN oldtree/drivers/base/power/resume.c newtree/drivers/base/power/resume.c
--- oldtree/drivers/base/power/resume.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/drivers/base/power/resume.c	2006-10-06 17:28:20.000000000 -0400
@@ -36,6 +36,7 @@
 	}
 	if (dev->bus && dev->bus->resume) {
 		dev_dbg(dev,"resuming\n");
+		device_beep(BEEP_RESUMING);
 		error = dev->bus->resume(dev);
 	}
 	if (dev->class && dev->class->resume) {
diff -urN oldtree/drivers/base/power/suspend.c newtree/drivers/base/power/suspend.c
--- oldtree/drivers/base/power/suspend.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/drivers/base/power/suspend.c	2006-10-06 17:28:20.000000000 -0400
@@ -74,6 +74,7 @@
 				? ", may wakeup"
 				: ""
 			);
+		device_beep(BEEP_SUSPENDING);
 		error = dev->class->suspend(dev, state);
 		suspend_report_result(dev->class->suspend, error);
 	}
@@ -86,6 +87,7 @@
 				? ", may wakeup"
 				: ""
 			);
+		device_beep(BEEP_SUSPENDING);
 		error = dev->bus->suspend(dev, state);
 		suspend_report_result(dev->bus->suspend, error);
 	}
@@ -111,6 +113,7 @@
 				? ", may wakeup"
 				: ""
 			);
+		device_beep(BEEP_SUSPENDING);
 		error = dev->bus->suspend_late(dev, state);
 		suspend_report_result(dev->bus->suspend_late, error);
 	}
@@ -152,6 +155,7 @@
 
 		printk("Suspending device %s\n", kobject_name(&dev->kobj));
 
+		device_beep(BEEP_SUSPENDING);
 		error = suspend_device(dev, state);
 
 		down(&dpm_list_sem);
@@ -197,6 +201,7 @@
 		struct list_head * entry = dpm_off.prev;
 
 		dev = to_device(entry);
+		device_beep(BEEP_SUSPENDING);
 		error = suspend_device_late(dev, state);
 		if (error)
 			goto Error;
diff -urN oldtree/drivers/base/sys.c newtree/drivers/base/sys.c
--- oldtree/drivers/base/sys.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/drivers/base/sys.c	2006-10-06 17:28:20.000000000 -0400
@@ -21,6 +21,8 @@
 #include <linux/string.h>
 #include <linux/pm.h>
 #include <linux/device.h>
+#include <linux/bootmem.h>
+#include <linux/delay.h>
 #include <asm/semaphore.h>
 
 #include "base.h"
@@ -30,6 +32,7 @@
 #define to_sysdev(k) container_of(k, struct sys_device, kobj)
 #define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr)
 
+int __nosavedata driver_model_beeping = 0;
 
 static ssize_t
 sysdev_show(struct kobject * kobj, struct attribute * attr, char * buffer)
@@ -340,25 +343,83 @@
 	up(&sysdev_drivers_lock);
 }
 
+static void device_do_beep(unsigned int count)
+{
+	/* enable counter 2 */
+	outb_p(inb_p(0x61) | 3, 0x61);
+	/* set command for counter 2, 2 byte write */
+	outb_p(0xB6, 0x43);
+	/* select desired HZ */
+	outb_p(count & 0xff, 0x42);
+	outb((count >> 8) & 0xff, 0x42);
+
+	mdelay(100);
+
+	/* disable counter 2 */
+	outb(inb_p(0x61) & 0xFC, 0x61);
+}
+
+/* 
+ * Beep in groups of three, with different frequencies so the individual
+ * beeps can be distinguished. In addition, frequency rises when suspending
+ * and drops when resuming.
+ *
+ * Yes, I know freq isn't really _the_ frequency.
+ */
+
+#define SUSPEND_FREQ_START 2000
+#define FREQ_CHANGE -500
+#define GROUP_SIZE 3
+#define RESUME_FREQ_START (SUSPEND_FREQ_START + (GROUP_SIZE - 1) * FREQ_CHANGE)
+
+void device_beep(int suspending)
+{
+	static int last_action = 0, this_freq = SUSPEND_FREQ_START;
+	int limit = suspending ? RESUME_FREQ_START : SUSPEND_FREQ_START;
+
+	if (!driver_model_beeping)
+		return;
+
+	if (suspending != last_action)
+		this_freq = suspending ? SUSPEND_FREQ_START : RESUME_FREQ_START;
+	
+	device_do_beep(this_freq);
+
+	if (this_freq == limit) {
+		/* Time for a gap & freq reset */
+		mdelay(300);
+		this_freq = suspending ? SUSPEND_FREQ_START : RESUME_FREQ_START;
+	} else
+		this_freq += (suspending ? FREQ_CHANGE : -FREQ_CHANGE);
+
+	last_action = suspending;
+}
+
 static void __sysdev_resume(struct sys_device *dev)
 {
 	struct sysdev_class *cls = dev->cls;
 	struct sysdev_driver *drv;
 
 	/* First, call the class-specific one */
-	if (cls->resume)
+	if (cls->resume) {
+		device_beep(BEEP_RESUMING);
 		cls->resume(dev);
+	}
 
 	/* Call auxillary drivers next. */
 	list_for_each_entry(drv, &cls->drivers, entry) {
-		if (drv->resume)
+		if (drv->resume) {
+			device_beep(BEEP_RESUMING);
 			drv->resume(dev);
+		}
 	}
 
 	/* Call global drivers. */
 	list_for_each_entry(drv, &sysdev_drivers, entry) {
-		if (drv->resume)
+		if (drv->resume) {
+			device_beep(BEEP_RESUMING);
 			drv->resume(dev);
+		}
 	}
 }
 
@@ -396,6 +457,7 @@
 			/* Call global drivers first. */
 			list_for_each_entry(drv, &sysdev_drivers, entry) {
 				if (drv->suspend) {
+					device_beep(BEEP_SUSPENDING);
 					ret = drv->suspend(sysdev, state);
 					if (ret)
 						goto gbl_driver;
@@ -405,6 +467,7 @@
 			/* Call auxillary drivers next. */
 			list_for_each_entry(drv, &cls->drivers, entry) {
 				if (drv->suspend) {
+					device_beep(BEEP_SUSPENDING);
 					ret = drv->suspend(sysdev, state);
 					if (ret)
 						goto aux_driver;
@@ -413,6 +476,7 @@
 
 			/* Now call the generic one */
 			if (cls->suspend) {
+				device_beep(BEEP_SUSPENDING);
 				ret = cls->suspend(sysdev, state);
 				if (ret)
 					goto cls_driver;
@@ -423,7 +487,8 @@
 	/* resume current sysdev */
 cls_driver:
 	drv = NULL;
-	printk(KERN_ERR "Class suspend failed for %s\n",
+	printk(KERN_ERR "Class suspend (%p) failed for %s\n",
+		cls->suspend,
 		kobject_name(&sysdev->kobj));
 
 aux_driver:
@@ -433,8 +498,10 @@
 	list_for_each_entry(err_drv, &cls->drivers, entry) {
 		if (err_drv == drv)
 			break;
-		if (err_drv->resume)
+		if (err_drv->resume) {
+			device_beep(BEEP_RESUMING);
 			err_drv->resume(sysdev);
+		}
 	}
 	drv = NULL;
 
@@ -445,8 +512,10 @@
 	list_for_each_entry(err_drv, &sysdev_drivers, entry) {
 		if (err_drv == drv)
 			break;
-		if (err_drv->resume)
+		if (err_drv->resume) {
+			device_beep(BEEP_RESUMING);
 			err_drv->resume(sysdev);
+		}
 	}
 	/* resume other sysdevs in current class */
 	list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
@@ -505,5 +574,13 @@
 	return subsystem_register(&system_subsys);
 }
 
+static int device_beep_setup(char *str)
+{
+	driver_model_beeping = !!simple_strtol(str, NULL, 0);
+	return 1;
+}
+
+__setup("device_beep=", device_beep_setup);
+
 EXPORT_SYMBOL_GPL(sysdev_register);
 EXPORT_SYMBOL_GPL(sysdev_unregister);
diff -urN oldtree/include/linux/bio.h newtree/include/linux/bio.h
--- oldtree/include/linux/bio.h	2006-10-05 15:26:55.000000000 -0400
+++ newtree/include/linux/bio.h	2006-10-06 17:28:20.000000000 -0400
@@ -124,6 +124,7 @@
 #define BIO_BOUNCED	5	/* bio is a bounce bio */
 #define BIO_USER_MAPPED 6	/* contains user pages */
 #define BIO_EOPNOTSUPP	7	/* not supported */
+#define BIO_SUSPEND_DATA 8
 #define bio_flagged(bio, flag)	((bio)->bi_flags & (1 << (flag)))
 
 /*
diff -urN oldtree/include/linux/device.h newtree/include/linux/device.h
--- oldtree/include/linux/device.h	2006-10-05 15:26:55.000000000 -0400
+++ newtree/include/linux/device.h	2006-10-06 17:28:20.000000000 -0400
@@ -470,4 +470,9 @@
 	MODULE_ALIAS("char-major-" __stringify(major) "-" __stringify(minor))
 #define MODULE_ALIAS_CHARDEV_MAJOR(major) \
 	MODULE_ALIAS("char-major-" __stringify(major) "-*")
+
+/* Beep debugging support */
+#define BEEP_SUSPENDING 0
+#define BEEP_RESUMING 1
+extern void device_beep(int suspending);
 #endif /* _DEVICE_H_ */
diff -urN oldtree/include/linux/dyn_pageflags.h newtree/include/linux/dyn_pageflags.h
--- oldtree/include/linux/dyn_pageflags.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/dyn_pageflags.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,67 @@
+/*
+ * include/linux/dyn_pageflags.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * It implements support for dynamically allocated bitmaps that are
+ * used for temporary or infrequently used pageflags, in lieu of
+ * bits in the struct page flags entry.
+ */
+
+#ifndef DYN_PAGEFLAGS_H
+#define DYN_PAGEFLAGS_H
+
+#include <linux/mm.h>
+
+typedef unsigned long *** dyn_pageflags_t;
+
+#if BITS_PER_LONG == 32
+#define UL_SHIFT 5
+#else 
+#if BITS_PER_LONG == 64
+#define UL_SHIFT 6
+#else
+#error Bits per long not 32 or 64?
+#endif
+#endif
+
+#define BIT_NUM_MASK (sizeof(unsigned long) * 8 - 1)
+#define PAGE_NUM_MASK (~((1 << (PAGE_SHIFT + 3)) - 1))
+#define UL_NUM_MASK (~(BIT_NUM_MASK | PAGE_NUM_MASK))
+
+/*
+ * PAGENUMBER gives the index of the page within the zone.
+ * PAGEINDEX gives the index of the unsigned long within that page.
+ * PAGEBIT gives the index of the bit within the unsigned long.
+ */
+#define BITS_PER_PAGE (PAGE_SIZE << 3)
+#define PAGENUMBER(zone_offset) ((int) (zone_offset >> (PAGE_SHIFT + 3)))
+#define PAGEINDEX(zone_offset) ((int) ((zone_offset & UL_NUM_MASK) >> UL_SHIFT))
+#define PAGEBIT(zone_offset) ((int) (zone_offset & BIT_NUM_MASK))
+
+#define PAGE_UL_PTR(bitmap, zone_num, zone_pfn) \
+       ((bitmap[zone_num][PAGENUMBER(zone_pfn)])+PAGEINDEX(zone_pfn))
+
+#define BITMAP_FOR_EACH_SET(bitmap, counter) \
+	for (counter = get_next_bit_on(bitmap, max_pfn); counter < max_pfn; \
+		counter = get_next_bit_on(bitmap, counter))
+
+extern void clear_dyn_pageflags(dyn_pageflags_t pagemap);
+extern int allocate_dyn_pageflags(dyn_pageflags_t *pagemap);
+extern void free_dyn_pageflags(dyn_pageflags_t *pagemap);
+extern unsigned long get_next_bit_on(dyn_pageflags_t bitmap, unsigned long counter);
+
+extern int test_dynpageflag(dyn_pageflags_t *bitmap, struct page *page);
+extern void set_dynpageflag(dyn_pageflags_t *bitmap, struct page *page);
+extern void clear_dynpageflag(dyn_pageflags_t *bitmap, struct page *page);
+#endif
+
+/* 
+ * With the above macros defined, you can do...
+ * #define PagePageset1(page) (test_dynpageflag(&pageset1_map, page))
+ * #define SetPagePageset1(page) (set_dynpageflag(&pageset1_map, page))
+ * #define ClearPagePageset1(page) (clear_dynpageflag(&pageset1_map, page))
+ */
+
diff -urN oldtree/include/linux/freezer.h newtree/include/linux/freezer.h
--- oldtree/include/linux/freezer.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/freezer.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,27 @@
+/* Freezer declarations */
+
+#define FREEZER_ON 0
+#define ABORT_FREEZING 1
+#define FREEZING_COMPLETE 2
+
+#define FREEZER_KERNEL_THREADS 0
+#define FREEZER_ALL_THREADS 1
+
+#ifdef CONFIG_PM
+extern unsigned long freezer_state;
+
+#define test_freezer_state(bit) test_bit(bit, &freezer_state)
+#define set_freezer_state(bit) set_bit(bit, &freezer_state)
+#define clear_freezer_state(bit) clear_bit(bit, &freezer_state)
+
+#define freezer_is_on() (test_freezer_state(FREEZER_ON))
+
+#else
+
+#define test_freezer_state(bit) (0)
+#define set_freezer_state(bit) do { } while(0)
+#define clear_freezer_state(bit) do { } while(0)
+
+#define freezer_is_on() (0)
+
+#endif
diff -urN oldtree/include/linux/kernel.h newtree/include/linux/kernel.h
--- oldtree/include/linux/kernel.h	2006-10-05 15:26:55.000000000 -0400
+++ newtree/include/linux/kernel.h	2006-10-06 17:28:20.000000000 -0400
@@ -114,6 +114,8 @@
 	__attribute__ ((format (printf, 2, 0)));
 extern int snprintf(char * buf, size_t size, const char * fmt, ...)
 	__attribute__ ((format (printf, 3, 4)));
+extern int snprintf_used(char *buffer, int buffer_size,
+		const char *fmt, ...);
 extern int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 	__attribute__ ((format (printf, 3, 0)));
 extern int scnprintf(char * buf, size_t size, const char * fmt, ...)
diff -urN oldtree/include/linux/netlink.h newtree/include/linux/netlink.h
--- oldtree/include/linux/netlink.h	2006-10-05 15:26:55.000000000 -0400
+++ newtree/include/linux/netlink.h	2006-10-06 17:28:20.000000000 -0400
@@ -23,6 +23,8 @@
 #define NETLINK_GENERIC		16
 /* leave room for NETLINK_DM (DM Events) */
 #define NETLINK_SCSITRANSPORT	18	/* SCSI Transports */
+#define NETLINK_SUSPEND2_USERUI	19	/* For suspend2's userui */
+#define NETLINK_SUSPEND2_USM	20	/* For suspend2's userui */
 
 #define MAX_LINKS 32		
 
diff -urN oldtree/include/linux/sched.h newtree/include/linux/sched.h
--- oldtree/include/linux/sched.h	2006-10-06 16:28:30.000000000 -0400
+++ newtree/include/linux/sched.h	2006-10-06 17:28:20.000000000 -0400
@@ -1747,7 +1747,7 @@
 
 extern void refrigerator(void);
 extern int freeze_processes(void);
-extern void thaw_processes(void);
+extern void thaw_processes(int which_threads);
 
 static inline int try_to_freeze(void)
 {
@@ -1766,7 +1766,7 @@
 
 static inline void refrigerator(void) {}
 static inline int freeze_processes(void) { BUG(); return 0; }
-static inline void thaw_processes(void) {}
+static inline void thaw_processes(int which_threads) {}
 
 static inline int try_to_freeze(void) { return 0; }
 
diff -urN oldtree/include/linux/suspend.h newtree/include/linux/suspend.h
--- oldtree/include/linux/suspend.h	2006-10-05 15:26:55.000000000 -0400
+++ newtree/include/linux/suspend.h	2006-10-06 17:28:20.000000000 -0400
@@ -8,6 +8,7 @@
 #include <linux/notifier.h>
 #include <linux/init.h>
 #include <linux/pm.h>
+#include <linux/suspend2.h>
 
 /* page backup entry */
 struct pbe {
@@ -24,26 +25,24 @@
 /* kernel/power/swsusp.c */
 extern int software_suspend(void);
 
-#if defined(CONFIG_VT) && defined(CONFIG_VT_CONSOLE)
 extern int pm_prepare_console(void);
 extern void pm_restore_console(void);
-#else
-static inline int pm_prepare_console(void) { return 0; }
-static inline void pm_restore_console(void) {}
-#endif /* defined(CONFIG_VT) && defined(CONFIG_VT_CONSOLE) */
+extern int freeze_processes(void);
+extern void thaw_processes(int which_threads);
+
 #else
 static inline int software_suspend(void)
 {
 	printk("Warning: fake suspend called\n");
 	return -ENOSYS;
 }
+static inline int freeze_processes(void) { return 0; }
+static inline void thaw_processes(int which_threads) { }
 #endif /* CONFIG_PM */
 
 void save_processor_state(void);
 void restore_processor_state(void);
 struct saved_context;
-void __save_processor_state(struct saved_context *ctxt);
-void __restore_processor_state(struct saved_context *ctxt);
 unsigned long get_safe_page(gfp_t gfp_mask);
 
 /*
diff -urN oldtree/include/linux/suspend2.h newtree/include/linux/suspend2.h
--- oldtree/include/linux/suspend2.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/suspend2.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,183 @@
+#ifndef _LINUX_SUSPEND2_H
+#define _LINUX_SUSPEND2_H
+
+#include <linux/dyn_pageflags.h>
+
+/* arch/i386/mm/init.c */
+extern char __nosave_begin, __nosave_end;
+
+#define SECTOR_SIZE 512
+
+/* kernel/power/main.c */
+extern unsigned long suspend_result;
+
+/* kernel/power/process.c */
+extern unsigned long suspend_debug_state;
+
+/* arch/i386/power/suspend2.c */
+extern unsigned long suspend_action;
+extern int suspend_io_time[2][2];
+
+extern dyn_pageflags_t pageset1_map;
+extern dyn_pageflags_t pageset1_copy_map;
+
+#ifdef CONFIG_PM_DEBUG
+#define test_debug_state(bit) (test_bit(bit, &suspend_debug_state))
+#else
+#define test_debug_state(bit) (0)
+#endif
+
+#define test_result_state(bit) (test_bit(bit, &suspend_result))
+
+/* Debug sections  - if debugging compiled in */
+enum {
+	SUSPEND_ANY_SECTION,
+	SUSPEND_EAT_MEMORY,
+	SUSPEND_IO,
+	SUSPEND_HEADER,
+	SUSPEND_WRITER,
+	SUSPEND_MEMORY,
+};
+
+/* debugging levels. */
+#define SUSPEND_STATUS		0
+#define SUSPEND_ERROR		2
+#define SUSPEND_LOW	 	3
+#define SUSPEND_MEDIUM	 	4
+#define SUSPEND_HIGH	  	5
+#define SUSPEND_VERBOSE		6
+
+/* Configuration flags */
+enum {
+	SUSPEND_REBOOT,
+	SUSPEND_PAUSE,
+	SUSPEND_SLOW,
+	SUSPEND_LOGALL,
+	SUSPEND_CAN_CANCEL,
+	SUSPEND_KEEP_IMAGE,
+	SUSPEND_FREEZER_TEST,
+	SUSPEND_SINGLESTEP,
+	SUSPEND_PAUSE_NEAR_PAGESET_END,
+	SUSPEND_TEST_FILTER_SPEED,
+	SUSPEND_TEST_BIO,
+	SUSPEND_NO_PAGESET2,
+	SUSPEND_PM_PREPARE_CONSOLE,
+	SUSPEND_IGNORE_ROOTFS,
+	SUSPEND_REPLACE_SWSUSP,
+	SUSPEND_RETRY_RESUME,
+};
+
+#ifdef CONFIG_SUSPEND2
+#define test_action_state(bit) (test_bit(bit, &suspend_action))
+#define set_action_state(bit) (test_and_set_bit(bit, &suspend_action))
+#define clear_action_state(bit) (test_and_clear_bit(bit, &suspend_action))
+#else
+#define test_action_state(bit) (0)
+#endif
+
+extern void __suspend_message(unsigned long section, unsigned long level, int log_normally,
+		const char *fmt, ...);
+
+#ifdef CONFIG_PM_DEBUG
+#define suspend_message(sn, lev, log, fmt, a...) \
+do { \
+	if (!sn || test_debug_state(sn)) \
+		__suspend_message(sn, lev, log, fmt, ##a); \
+} while(0)
+#else /* CONFIG_PM_DEBUG */
+#define suspend_message(sn, lev, log, fmt, a...) \
+do { \
+	if (lev == SUSPEND_STATUS) \
+		__suspend_message(sn, lev, log, fmt, ##a); \
+} while(0)
+#endif /* CONFIG_PM_DEBUG */
+  
+/* Suspend 2 */
+
+enum {
+	SUSPEND_CAN_SUSPEND,
+	SUSPEND_CAN_RESUME,
+	SUSPEND_RUNNING,
+	SUSPEND_RESUME_DEVICE_OK,
+	SUSPEND_NORESUME_SPECIFIED,
+	SUSPEND_SANITY_CHECK_PROMPT,
+	SUSPEND_PAGESET2_NOT_LOADED,
+	SUSPEND_CONTINUE_REQ,
+	SUSPEND_RESUMED_BEFORE,
+	SUSPEND_RESUME_NOT_DONE,
+	SUSPEND_BOOT_TIME,
+	SUSPEND_NOW_RESUMING,
+	SUSPEND_IGNORE_LOGLEVEL,
+	SUSPEND_TRYING_TO_RESUME,
+	SUSPEND_TRY_RESUME_RD,
+};
+
+/* --------------------------------------------------------------------- */
+#ifdef CONFIG_SUSPEND2
+
+/* Used in init dir files */
+extern unsigned long suspend_state;
+
+extern void suspend2_try_resume(void);
+extern int suspend_early_boot_message 
+	(int can_erase_image, int default_answer, char *warning_reason, ...);
+extern unsigned long suspend_update_status (unsigned long value, unsigned long maximum,
+		const char *fmt, ...);
+extern void suspend_prepare_status (int clearbar, const char *fmt, ...);
+
+#define test_suspend_state(bit) \
+	(test_bit(bit, &suspend_state))
+
+#define clear_suspend_state(bit) \
+	(clear_bit(bit, &suspend_state))
+
+#define set_suspend_state(bit) \
+	(set_bit(bit, &suspend_state))
+
+extern void suspend2_try_suspend(int have_pmsem);
+
+/* --------------------------------------------------------------------- */
+#else
+/* --------------------------------------------------------------------- */
+
+#define suspend_state		(0)
+#define clear_suspend_state(bit)	do { } while (0)
+#define test_suspend_state(bit) 	(0)
+#define set_suspend_state(bit)		do { } while(0)
+
+#define suspend2_try_resume()			do { } while(0)
+static inline int suspend_early_boot_message(int a, int b, char *c, ...)	{ return 0; }
+static inline unsigned long suspend_update_status(unsigned long value, unsigned long maximum,
+		const char *fmt, ...)
+{
+	return maximum;
+}
+#define suspend_prepare_status(a, ...)  do { } while(0)
+
+#endif /* CONFIG_SUSPEND2 */
+
+#define test_and_set_suspend_state(bit) \
+	(test_and_set_bit(bit, &suspend_state))
+
+#define get_suspend_state()  (suspend_state)
+
+#define restore_suspend_state(saved_state) \
+	do { suspend_state = saved_state; } while(0)
+	
+#if defined(CONFIG_SUSPEND2) && defined(CONFIG_ACPI)
+#include <acpi/acpi.h>
+static inline int may_try_suspend2(u32 state)
+{
+	if (state == ACPI_STATE_S4) {
+		suspend2_try_suspend(0);
+		return 1;
+	}
+	return 0;
+}
+#else
+static inline int may_try_suspend2(u32 state)
+{
+	return 0;
+}
+#endif
+#endif /* _LINUX_SUSPEND2_H */
diff -urN oldtree/include/linux/swap.h newtree/include/linux/swap.h
--- oldtree/include/linux/swap.h	2006-10-06 14:45:31.000000000 -0400
+++ newtree/include/linux/swap.h	2006-10-06 17:28:20.000000000 -0400
@@ -370,5 +370,10 @@
 #define disable_swap_token() do { } while(0)
 
 #endif /* CONFIG_SWAP */
+
+/* For Suspend2 - unlink LRU pages while saving separately */
+void unlink_lru_lists(void);
+void relink_lru_lists(void);
+
 #endif /* __KERNEL__*/
 #endif /* _LINUX_SWAP_H */
diff -urN oldtree/init/do_mounts.c newtree/init/do_mounts.c
--- oldtree/init/do_mounts.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/init/do_mounts.c	2006-10-06 17:28:20.000000000 -0400
@@ -26,6 +26,19 @@
 
 dev_t ROOT_DEV;
 
+#ifdef CONFIG_SOFTWARE_SUSPEND
+extern int software_resume(void);
+#else
+#ifdef CONFIG_SUSPEND2
+extern void suspend2_try_resume(void);
+static int software_resume(void)
+{
+	suspend2_try_resume();
+	return 0;
+}
+#endif
+#endif
+
 static int __init load_ramdisk(char *str)
 {
 	rd_doload = simple_strtol(str,NULL,0) & 3;
@@ -139,11 +152,16 @@
 	char s[32];
 	char *p;
 	dev_t res = 0;
-	int part;
+	int part, mount_result;
 
 #ifdef CONFIG_SYSFS
 	int mkdir_err = sys_mkdir("/sys", 0700);
-	if (sys_mount("sysfs", "/sys", "sysfs", 0, NULL) < 0)
+	/* 
+	 * When changing resume2 parameter for Software Suspend, sysfs may
+	 * already be mounted. 
+	 */
+	mount_result = sys_mount("sysfs", "/sys", "sysfs", 0, NULL);
+	if (mount_result < 0 && mount_result != -EBUSY)
 		goto out;
 #endif
 
@@ -195,7 +213,8 @@
 	res = try_name(s, part);
 done:
 #ifdef CONFIG_SYSFS
-	sys_umount("/sys", 0);
+	if (mount_result >= 0)
+		sys_umount("/sys", 0);
 out:
 	if (!mkdir_err)
 		sys_rmdir("/sys");
@@ -434,9 +453,27 @@
 
 	is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
 
+	/* Suspend2:
+	 * By this point, suspend_early_init has been called to initialise our
+	 * sysfs interface. If modules are built in, they have registered (all
+	 * of the above via initcalls).
+	 * 
+	 * We have not yet looked to see if an image exists, however. If we
+	 * have an initrd, it is expected that the user will have set it up
+	 * to echo > /sys/power/suspend2/do_resume and thus initiate any
+	 * resume. If they don't do that, we do it immediately after the initrd
+	 * is finished (major issues if they mount filesystems rw from the
+	 * initrd! - they are warned. If there's no usable initrd, we do our
+	 * check next.
+	 */
 	if (initrd_load())
 		goto out;
 
+#ifdef CONFIG_SUSPEND2
+	if (test_suspend_state(SUSPEND_RESUME_NOT_DONE))
+		software_resume();
+#endif
+	
 	if (is_floppy && rd_doload && rd_load_disk(0))
 		ROOT_DEV = Root_RAM0;
 
diff -urN oldtree/init/do_mounts_initrd.c newtree/init/do_mounts_initrd.c
--- oldtree/init/do_mounts_initrd.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/init/do_mounts_initrd.c	2006-10-06 17:28:20.000000000 -0400
@@ -6,6 +6,7 @@
 #include <linux/romfs_fs.h>
 #include <linux/initrd.h>
 #include <linux/sched.h>
+#include <linux/suspend.h>
 
 #include "do_mounts.h"
 
@@ -57,10 +58,17 @@
 	current->flags |= PF_NOFREEZE;
 	pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
 	if (pid > 0) {
-		while (pid != sys_wait4(-1, NULL, 0, NULL))
+		while (pid != sys_wait4(-1, NULL, 0, NULL)) {
 			yield();
+			try_to_freeze();
+		}
 	}
 
+	if (test_suspend_state(SUSPEND_RESUME_NOT_DONE))
+		printk(KERN_ERR "Suspend2: Initrd lacks echo > /sys/power/suspend2/do_resume.\n");
+	clear_suspend_state(SUSPEND_BOOT_TIME);
+	current->flags &= ~PF_NOFREEZE;
+
 	/* move initrd to rootfs' /old */
 	sys_fchdir(old_fd);
 	sys_mount("/", ".", NULL, MS_MOVE, NULL);
diff -urN oldtree/init/main.c newtree/init/main.c
--- oldtree/init/main.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/init/main.c	2006-10-06 17:28:20.000000000 -0400
@@ -742,7 +742,9 @@
 
 	/*
 	 * check if there is an early userspace init.  If yes, let it do all
-	 * the work
+	 * the work. For suspend2, we assume that it will do the right thing
+	 * with regard to trying to resume at the right place. When that
+	 * happens, the BOOT_TIME flag will be cleared.
 	 */
 
 	if (!ramdisk_execute_command)
diff -urN oldtree/kernel/kmod.c newtree/kernel/kmod.c
--- oldtree/kernel/kmod.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/kmod.c	2006-10-06 17:28:20.000000000 -0400
@@ -34,6 +34,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/resource.h>
+#include <linux/freezer.h>
 #include <asm/uaccess.h>
 
 extern int max_threads;
@@ -280,6 +281,11 @@
 	if (path[0] == '\0')
 		return 0;
 
+	if (freezer_is_on()) {
+		printk(KERN_WARNING "Freezer is on. Refusing to start %s.\n", path);
+		return -EBUSY;
+	}
+
 	queue_work(khelper_wq, &work);
 	wait_for_completion(&done);
 	return sub_info.retval;
diff -urN oldtree/kernel/power/Kconfig newtree/kernel/power/Kconfig
--- oldtree/kernel/power/Kconfig	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/power/Kconfig	2006-10-06 17:28:20.000000000 -0400
@@ -130,3 +130,85 @@
 	bool
 	depends on HOTPLUG_CPU && X86 && PM
 	default y
+
+config SUSPEND2_CRYPTO
+	bool
+	depends on SUSPEND2 && CRYPTO
+	default y
+
+menuconfig SUSPEND2
+	bool "Suspend2"
+	depends on PM
+	select DYN_PAGEFLAGS
+	select HOTPLUG_CPU if SMP
+	---help---
+	  Suspend2 is the 'new and improved' suspend support.
+	  
+	  See the Suspend2 home page (suspend2.net)
+	  for FAQs, HOWTOs and other documentation.
+
+	comment "Image Storage (you need at least one writer)"
+		depends on SUSPEND2
+	
+	config SUSPEND2_FILEWRITER
+		bool "File Writer"
+		depends on SUSPEND2
+		---help---
+		  This option enables support for storing an image in a
+		  simple file. This should be possible, but we're still
+		  testing it.
+
+	config SUSPEND2_SWAPWRITER
+		bool "Swap Writer"
+		depends on SUSPEND2
+		select SWAP
+		---help---
+		  This option enables support for storing an image in your
+		  swap space.
+
+	comment "General Options"
+		depends on SUSPEND2
+
+	config SUSPEND2_DEFAULT_RESUME2
+		string "Default resume device name"
+		depends on SUSPEND2
+		---help---
+		  You normally need to add a resume2= parameter to your lilo.conf or
+		  equivalent. With this option properly set, the kernel has a value
+		  to default. No damage will be done if the value is invalid.
+
+	config SUSPEND2_KEEP_IMAGE
+		bool "Allow Keep Image Mode"
+		depends on SUSPEND2
+		---help---
+		  This option allows you to keep and image and reuse it. It is intended
+		  __ONLY__ for use with systems where all filesystems are mounted read-
+		  only (kiosks, for example). To use it, compile this option in and boot
+		  normally. Set the KEEP_IMAGE flag in /proc/suspend2 and suspend.
+		  When you resume, the image will not be removed. You will be unable to turn
+		  off swap partitions (assuming you are using the swap writer), but future
+		  suspends simply do a power-down. The image can be updated using the
+		  kernel command line parameter suspend_act= to turn off the keep image
+		  bit. Keep image mode is a little less user friendly on purpose - it
+		  should not be used without thought!
+
+	config SUSPEND2_REPLACE_SWSUSP
+		bool "Replace swsusp by default"
+		default y
+		depends on SUSPEND2
+		---help---
+		  Suspend2 can replace swsusp. This option makes that the default state,
+		  requiring you to echo 0 > /sys/power/suspend2/replace_swsusp if you want
+		  to use the vanilla kernel functionality. Note that your initrd/ramfs will
+		  need to do this before trying to resume, too.
+		  With overriding swsusp enabled, Suspend2 will use both the resume= and
+		  noresume commandline options _and_ the resume2= and noresume2 ones (for
+		  compatibility). resume= takes precedence over resume2=. Echoing disk 
+		  to /sys/power/state will start a Suspend2 cycle. If resume= doesn't
+		  specify a writer and both the swapwriter and filewriter are compiled in,
+		  the swapwriter will be used by default.
+
+config SUSPEND_SHARED
+	bool
+	depends on SUSPEND2 || SOFTWARE_SUSPEND
+	default y
diff -urN oldtree/kernel/power/Makefile newtree/kernel/power/Makefile
--- oldtree/kernel/power/Makefile	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/power/Makefile	2006-10-06 17:28:20.000000000 -0400
@@ -5,6 +5,26 @@
 
 obj-y				:= main.o process.o console.o
 obj-$(CONFIG_PM_LEGACY)		+= pm.o
-obj-$(CONFIG_SOFTWARE_SUSPEND)	+= swsusp.o disk.o snapshot.o swap.o user.o
+obj-$(CONFIG_SUSPEND_SHARED)	+= snapshot.o
+
+# Order is important for compression and encryption - we
+# compress before encrypting.
+
+suspend_core-objs := io.o pagedir.o prepare_image.o \
+		extent.o suspend.o modules.o \
+		pageflags.o ui.o sysfs.o \
+		power_off.o atomic_copy.o
+
+#ifdef CONFIG_NET
+suspend_core-objs += storage.o netlink.o
+#endif
+
+obj-$(CONFIG_SUSPEND2)			+= suspend_core.o
+obj-$(CONFIG_SUSPEND2_CRYPTO)		+= compression.o encryption.o
+
+obj-$(CONFIG_SUSPEND2_SWAPWRITER)	+= suspend_block_io.o suspend_swap.o
+obj-$(CONFIG_SUSPEND2_FILEWRITER)	+= suspend_block_io.o suspend_file.o
+
+obj-$(CONFIG_SOFTWARE_SUSPEND)	+= swsusp.o disk.o swap.o user.o
 
 obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
diff -urN oldtree/kernel/power/atomic_copy.c newtree/kernel/power/atomic_copy.c
--- oldtree/kernel/power/atomic_copy.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/atomic_copy.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,543 @@
+/*
+ * kernel/power/atomic_copy.c
+ *
+ * Copyright 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * Distributed under GPLv2.
+ *
+ * Routines for doing the atomic save/restore.
+ */
+
+#include <linux/suspend.h>
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+#include <linux/vt_kern.h>
+#include <linux/cpu.h>
+#include <asm/setup.h>
+#include "suspend.h"
+#include "storage.h"
+#include "power_off.h"
+#include "ui.h"
+#include "power.h"
+#include "io.h"
+#include "prepare_image.h"
+#include "pageflags.h"
+#include "extent.h"
+
+static unsigned long state1 __nosavedata = 0;
+static unsigned long state2 __nosavedata = 0;
+static int state3 __nosavedata = 0;
+static int io_speed_save[2][2] __nosavedata;
+__nosavedata char suspend_resume_commandline[COMMAND_LINE_SIZE];
+
+#define SUSPEND_FREQ_START 2000
+#define FREQ_CHANGE -100
+#define GROUP_SIZE 10
+#define RESUME_FREQ_START (SUSPEND_FREQ_START + (GROUP_SIZE - 1) * FREQ_CHANGE)
+
+static int __nosavedata suspend_beeping = 0;
+static int __nosavedata this_freq = SUSPEND_FREQ_START;
+
+extern void suspend_power_down(void);
+extern int swsusp_resume(void);
+extern int suspend2_in_suspend __nosavedata;
+int extra_pd1_pages_used;
+
+#ifdef CONFIG_HIGHMEM
+static dyn_pageflags_t __nosavedata origmap;
+static dyn_pageflags_t __nosavedata copymap;
+static unsigned long __nosavedata origoffset;
+static unsigned long __nosavedata copyoffset;
+static __nosavedata int o_zone_num, c_zone_num;
+
+struct zone_data {
+	unsigned long start_pfn;
+	unsigned long end_pfn;
+	int is_highmem;
+};
+
+static __nosavedata struct zone_data *zone_nosave;
+static __nosavedata unsigned long boot_max_pfn;
+
+/**
+ * suspend_init_nosave_zone_table: Set up nosave copy of zone table data.
+ *
+ * Zone information might be overwritten during the copy back, so we copy
+ * the fields we need to a non-conflicting page and use it.
+ **/
+static void suspend_init_nosave_zone_table(void)
+{
+	struct zone *zone;
+	
+	zone_nosave = (struct zone_data *) suspend_get_nonconflicting_page();
+
+	BUG_ON(!zone_nosave);
+
+	/* It should all fit in one page. */
+	BUG_ON(ZONEID_MASK > (PAGE_SIZE / sizeof(struct zone_data)));
+
+	memset((char *) zone_nosave, 0, PAGE_SIZE);
+
+	for_each_zone(zone) {
+		if (populated_zone(zone)) {
+			int zone_num = page_zone_id(pfn_to_page(zone->zone_start_pfn));
+			zone_nosave[zone_num].start_pfn = zone->zone_start_pfn;
+			zone_nosave[zone_num].end_pfn = zone->zone_start_pfn +
+				zone->spanned_pages - 1;
+			zone_nosave[zone_num].is_highmem = is_highmem(zone);
+		}
+	}
+
+	boot_max_pfn = max_pfn;
+}
+
+/**
+ * __suspend_get_next_bit_on: Atomic-copy safe location of next bit on in a bitmap.
+ * 
+ * @bitmap:	The bitmap we are traversing.
+ * @zone_num:	Which zone we are in.
+ * @counter:	The current pfn.
+ *
+ * A version of __get_next_bit_on that can be used when doing the atomic
+ * copy. While doing it, we can't rely on the zone information being in
+ * a constant location.
+ **/
+static unsigned long __suspend_get_next_bit_on(dyn_pageflags_t bitmap,
+		int *zone_num, unsigned long counter)
+{
+	unsigned long *ul_ptr = NULL;
+	int reset_ul_ptr = 1, pagebit;
+	BUG_ON(*zone_num == (1 << ZONEID_SHIFT));
+
+	if (counter == boot_max_pfn) {
+		*zone_num = 0;
+		while (!zone_nosave[*zone_num].end_pfn)
+			(*zone_num)++;
+		counter = zone_nosave[*zone_num].start_pfn - 1;
+	}
+
+	do {
+		counter++;
+		if (counter > zone_nosave[*zone_num].end_pfn) {
+			(*zone_num)++;
+			while (*zone_num < (1 << ZONEID_SHIFT) &&
+					!zone_nosave[*zone_num].end_pfn)
+				(*zone_num)++;
+			
+			if (*zone_num == (1 << ZONEID_SHIFT))
+				return boot_max_pfn;
+			counter = zone_nosave[*zone_num].start_pfn;
+			reset_ul_ptr = 1;
+		} else
+			if (!(counter & BIT_NUM_MASK))
+				reset_ul_ptr = 1;
+
+		pagebit = PAGEBIT(counter);
+
+		if (reset_ul_ptr) {
+			reset_ul_ptr = 0;
+			ul_ptr = PAGE_UL_PTR(bitmap, *zone_num,
+				(counter - zone_nosave[*zone_num].start_pfn));
+		}
+		
+		if (!(*ul_ptr & ~((1 << pagebit) - 1))) {
+			counter += BITS_PER_LONG - pagebit - 1;
+			continue;
+		}
+	} while(!test_bit(pagebit, ul_ptr));
+
+	return counter;
+}
+
+/*
+ * copyback_high: Restore highmem pages.
+ *
+ * Iterate through the source and destination bitmaps, restoring
+ * highmem pages that were atomically copied.
+ */
+void copyback_high(void)
+{
+	unsigned long *origpage;
+	unsigned long *copypage;
+
+	origoffset = __suspend_get_next_bit_on(origmap, &o_zone_num, boot_max_pfn);
+	copyoffset = __suspend_get_next_bit_on(copymap, &c_zone_num, boot_max_pfn);
+
+	while (o_zone_num < (1 << ZONEID_SHIFT)) {
+		if (zone_nosave[o_zone_num].is_highmem) {
+			origpage = (unsigned long *) kmap_atomic(pfn_to_page(origoffset), KM_USER1);
+			copypage = (unsigned long *) __va(copyoffset << PAGE_SHIFT);
+
+			memcpy(origpage, copypage, PAGE_SIZE);
+
+			kunmap_atomic(origpage, KM_USER1);
+		}
+		
+		origoffset = __suspend_get_next_bit_on(origmap, &o_zone_num, origoffset);
+		copyoffset = __suspend_get_next_bit_on(copymap, &c_zone_num, copyoffset);
+	}
+}
+#else
+void copyback_high(void) { }
+#endif
+
+static void suspend_do_beep(unsigned int count)
+{
+	/* enable counter 2 */
+	outb_p(inb_p(0x61) | 3, 0x61);
+	/* set command for counter 2, 2 byte write */
+	outb_p(0xB6, 0x43);
+	/* select desired HZ */
+	outb_p(count & 0xff, 0x42);
+	outb((count >> 8) & 0xff, 0x42);
+
+	mdelay(100);
+
+	/* disable counter 2 */
+	outb(inb_p(0x61) & 0xFC, 0x61);
+}
+
+/* 
+ * Beep in groups of three, with different frequencies so the individual
+ * beeps can be distinguished. In addition, frequency rises when suspending
+ * and drops when resuming.
+ *
+ * Yes, I know freq isn't really _the_ frequency.
+ */
+
+void suspend2_beep(int suspending)
+{
+	static int last_action = 0;
+	int limit = suspending ? RESUME_FREQ_START : SUSPEND_FREQ_START;
+
+	if (!suspend_beeping)
+		return;
+
+	if (suspending != last_action)
+		this_freq = suspending ? SUSPEND_FREQ_START : RESUME_FREQ_START;
+	
+	suspend_do_beep(this_freq);
+
+	if (this_freq == limit) {
+		/* Time for a gap & freq reset */
+		mdelay(300);
+		this_freq = suspending ? SUSPEND_FREQ_START : RESUME_FREQ_START;
+	} else
+		this_freq += (suspending ? FREQ_CHANGE : -FREQ_CHANGE);
+
+	last_action = suspending;
+}
+
+/*
+ * prepare_suspend2_pbe_list
+ *
+ * Prepare pageset2 pages for doing the atomic copy. If necessary,
+ * we allocate extra pages.
+ *
+ */
+
+void prepare_suspend2_pbe_list(void)
+{
+	unsigned long orig_pfn, copy_pfn;
+	int i = 1;
+	struct pbe *this_pbe = NULL, *last_pbe = NULL;
+
+	orig_pfn = copy_pfn = max_pfn;
+
+	restore_pblist = NULL;
+
+	do {
+		if (!this_pbe ||
+		    ((((unsigned long) this_pbe) & (PAGE_SIZE - 1)) 
+		     + 2 * sizeof(struct pbe)) > PAGE_SIZE) {
+			/* Get the next page for pbes */
+			this_pbe = (struct pbe *) suspend_get_nonconflicting_page();
+			BUG_ON(!this_pbe);
+			BUG_ON(PagePageset1(virt_to_page(this_pbe)));
+		} else
+			this_pbe++;
+
+		do {
+			orig_pfn = get_next_bit_on(pageset1_map, orig_pfn);
+			if (orig_pfn == max_pfn)
+				return;
+			copy_pfn = get_next_bit_on(pageset1_copy_map, copy_pfn);
+		} while (PageHighMem(pfn_to_page(orig_pfn)));
+		
+		if (!last_pbe)
+			restore_pblist = this_pbe;
+		else
+			last_pbe->next = this_pbe;
+
+		last_pbe = this_pbe;
+		this_pbe->orig_address = (unsigned long) page_address(pfn_to_page(orig_pfn));
+		this_pbe->address = (unsigned long) page_address(pfn_to_page(copy_pfn));
+		this_pbe->next = NULL; /* get_nonconflicting_page doesn't get zeroed pages */
+
+		i++;
+
+	} while (1);
+}
+
+/*
+ * copyback_post: Post atomic-restore actions.
+ *
+ * After doing the atomic restore, we have a few more things to do:
+ * 1) We want to retain some values across the restore, so we now copy
+ * these from the nosave variables to the normal ones.
+ * 2) Set the status flags.
+ * 3) Resume devices.
+ * 4) Get userui to redraw.
+ * 5) Reread the page cache.
+ */
+
+void copyback_post(void)
+{
+	int loop;
+
+	suspend2_beep(0);
+
+	suspend_action = state1;
+	suspend_debug_state = state2;
+	console_loglevel = state3;
+
+	for (loop = 0; loop < 4; loop++)
+		suspend_io_time[loop/2][loop%2] =
+			io_speed_save[loop/2][loop%2];
+
+	set_suspend_state(SUSPEND_NOW_RESUMING);
+	set_suspend_state(SUSPEND_PAGESET2_NOT_LOADED);
+
+	if (pm_ops && pm_ops->finish && suspend_powerdown_method > 3)
+		pm_ops->finish(suspend_powerdown_method);
+
+	if (suspend_activate_storage(1))
+		panic("Failed to reactivate our storage.");
+
+	userui_redraw();
+
+	suspend_cond_pause(1, "About to reload secondary pagedir.");
+
+	if (read_pageset2(0))
+		panic("Unable to successfully reread the page cache.");
+
+	clear_suspend_state(SUSPEND_PAGESET2_NOT_LOADED);
+	
+	suspend_prepare_status(DONT_CLEAR_BAR, "Cleaning up...");
+}
+
+/*
+ * suspend_post_context_save: Steps after saving the cpu context.
+ *
+ * Steps taken after saving the CPU state to make the actual
+ * atomic copy.
+ *
+ * Called from swsusp_save in snapshot.c.
+ */
+
+int suspend_post_context_save(void)
+{
+	int old_ps1_size = pagedir1.pageset_size;
+	int old_ps2_size = pagedir2.pageset_size;
+	
+	BUG_ON(!irqs_disabled());
+
+	suspend2_beep(1);
+
+	suspend_recalculate_image_contents(1);
+
+	extra_pd1_pages_used = pagedir1.pageset_size - old_ps1_size;
+
+	if ((pagedir1.pageset_size - old_ps1_size) > extra_pd1_pages_allowance) {
+		abort_suspend("Pageset1 has grown by %d pages. "
+			"extra_pages_allowance is currently only %d.\n",
+			pagedir1.pageset_size - old_ps1_size,
+			extra_pd1_pages_allowance);
+		return -1;
+	}
+
+	BUG_ON(old_ps2_size != pagedir2.pageset_size);
+
+	BUG_ON(!irqs_disabled());
+
+	if (!test_action_state(SUSPEND_TEST_FILTER_SPEED) &&
+	    !test_action_state(SUSPEND_TEST_BIO))
+		suspend_copy_pageset1();
+
+
+	suspend2_beep(1);
+
+	return 0;
+}
+
+/* suspend_copy_pageset1: Do the atomic copy of pageset1.
+ *
+ * Make the atomic copy of pageset1. We can't use copy_page (as we once did)
+ * because we can't be sure what side effects it has. On my old Duron, with
+ * 3DNOW, kernel_fpu_begin increments preempt count, making our preempt
+ * count at resume time 4 instead of 3.
+ * 
+ * We don't want to call kmap_atomic unconditionally because it has the side
+ * effect of incrementing the preempt count, which will leave it one too high
+ * post resume (the page containing the preempt count will be copied after
+ * its incremented. This is essentially the same problem.
+ */
+
+void suspend_copy_pageset1(void)
+{
+	int i;
+	unsigned long source_index, dest_index;
+
+	source_index = get_next_bit_on(pageset1_map, max_pfn);
+	dest_index = get_next_bit_on(pageset1_copy_map, max_pfn);
+
+	for (i = 0; i < pagedir1.pageset_size; i++) {
+		unsigned long *origvirt, *copyvirt;
+		struct page *origpage;
+		int loop = (PAGE_SIZE / sizeof(unsigned long)) - 1;
+
+		origpage = pfn_to_page(source_index);
+		
+	       	if (PageHighMem(origpage))
+			origvirt = kmap_atomic(origpage, KM_USER0);
+		else
+			origvirt = page_address(origpage);
+
+		copyvirt = (unsigned long *) page_address(pfn_to_page(dest_index));
+
+		while (loop >= 0) {
+			*(copyvirt + loop) = *(origvirt + loop);
+			loop--;
+		}
+		
+		if (PageHighMem(origpage))
+			kunmap_atomic(origvirt, KM_USER0);
+		
+		source_index = get_next_bit_on(pageset1_map, source_index);
+		dest_index = get_next_bit_on(pageset1_copy_map, dest_index);
+	}
+}
+
+int suspend2_suspend(void)
+{
+	int error;
+
+	suspend2_beep(1);
+
+	if (test_action_state(SUSPEND_PM_PREPARE_CONSOLE))
+		pm_prepare_console();
+
+	if ((error = arch_prepare_suspend()))
+		return error;
+	local_irq_disable();
+	/* At this point, device_suspend() has been called, but *not*
+	 * device_power_down(). We *must* device_power_down() now.
+	 * Otherwise, drivers for some devices (e.g. interrupt controllers)
+	 * become desynchronized with the actual state of the hardware
+	 * at resume time, and evil weirdness ensues.
+	 */
+	if ((error = device_power_down(PMSG_FREEZE))) {
+		set_result_state(SUSPEND_DEVICE_REFUSED);
+		set_result_state(SUSPEND_ABORTED);
+		printk(KERN_ERR "Some devices failed to power down, aborting suspend\n");
+		goto enable_irqs;
+	}
+
+	suspend2_beep(1);
+
+	save_processor_state();
+	if ((error = swsusp_arch_suspend()))
+		printk(KERN_ERR "Error %d suspending\n", error);
+	/* Restore control flow appears here */
+	restore_processor_state();
+
+	suspend2_beep(1);
+
+	if (!suspend2_in_suspend)
+		copyback_high();
+	device_power_up();
+enable_irqs:
+	local_irq_enable();
+	if (test_action_state(SUSPEND_PM_PREPARE_CONSOLE))
+		pm_restore_console();
+
+	suspend2_beep(1);
+
+	return error;
+}
+
+/*
+ * suspend_atomic_restore
+ *
+ * Get ready to do the atomic restore. This part gets us into the same
+ * state we are in prior to do calling do_suspend2_lowlevel while
+ * suspending: hotunplugging secondary cpus and freeze processes,
+ * before starting the thread that will do the restore.
+ */
+int suspend_atomic_restore(void)
+{
+	int error, loop;
+
+	suspend2_beep(0);
+
+	suspend_prepare_status(DONT_CLEAR_BAR,	"Atomic restore preparation");
+	prepare_suspend2_pbe_list();
+
+	suspend2_beep(0);
+
+	if (test_action_state(SUSPEND_PM_PREPARE_CONSOLE))
+		pm_prepare_console();
+
+	disable_nonboot_cpus();
+
+	if ((error = device_suspend(PMSG_FREEZE))) {
+		printk("Some devices failed to suspend\n");
+		if (test_action_state(SUSPEND_PM_PREPARE_CONSOLE))
+			pm_restore_console();
+		BUG();
+	}
+
+	suspend2_beep(0);
+
+#ifdef CONFIG_HIGHMEM
+	origmap = pageset1_map;
+	copymap = pageset1_copy_map;
+	suspend_init_nosave_zone_table();
+#endif
+
+	state1 = suspend_action;
+	state2 = suspend_debug_state;
+	state3 = console_loglevel;
+	
+	for (loop = 0; loop < 4; loop++)
+		io_speed_save[loop/2][loop%2] =
+			suspend_io_time[loop/2][loop%2];
+	memcpy(suspend_resume_commandline, saved_command_line, COMMAND_LINE_SIZE);
+
+	mb();
+
+	local_irq_disable();
+	if (device_power_down(PMSG_FREEZE)) {
+		printk(KERN_ERR "Some devices failed to power down. Very bad.\n");
+		BUG();
+	}
+
+	/* We'll ignore saved state, but this gets preempt count (etc) right */
+	save_processor_state();
+	suspend2_beep(0);
+	error = swsusp_arch_resume();
+	/* Code below is only ever reached in case of failure. Otherwise
+	 * execution continues at place where swsusp_arch_suspend was called.
+         */
+	BUG();
+	return 1;
+}
+
+static int suspend_beep_setup(char *str)
+{
+	suspend_beeping = !!simple_strtol(str, NULL, 0);
+	return 1;
+}
+
+__setup("suspend_beep=", suspend_beep_setup);
+
diff -urN oldtree/kernel/power/block_io.h newtree/kernel/power/block_io.h
--- oldtree/kernel/power/block_io.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/block_io.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,55 @@
+/*
+ * kernel/power/block_io.h
+ *
+ * Copyright 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * Distributed under GPLv2.
+ *
+ * This file contains declarations for functions exported from
+ * block_io.c, which contains low level io functions.
+ */
+
+#include <linux/buffer_head.h>
+#include "extent.h"
+
+struct suspend_bdev_info {
+	struct block_device *bdev;
+	dev_t dev_t;
+	int bmap_shift;
+	int blocks_per_page;
+};
+
+/* 
+ * Our exported interface so the swapwriter and filewriter don't
+ * need these functions duplicated.
+ */
+struct suspend_bio_ops {
+	int (*bdev_page_io) (int rw, struct block_device *bdev, long pos,
+			struct page *page);
+	void (*check_io_stats) (void);
+	void (*reset_io_stats) (void);
+	void (*finish_all_io) (void);
+	int (*prepare_readahead) (int index);
+	void (*cleanup_readahead) (int index);
+	struct page ** readahead_pages;
+	int (*readahead_ready) (int readahead_index);
+	int (*forward_one_page) (void);
+	void (*set_extra_page_forward) (void);
+	void (*set_devinfo) (struct suspend_bdev_info *info);
+	int (*read_chunk) (struct page *buffer_page, int sync);
+	int (*write_chunk) (struct page *buffer_page);
+	int (*rw_header_chunk) (int rw, struct suspend_module_ops *owner,
+			char *buffer, int buffer_size);
+	int (*write_header_chunk_finish) (void);
+	int (*rw_init) (int rw, int stream_number);
+	int (*rw_cleanup) (int rw);
+};
+
+extern struct suspend_bio_ops suspend_bio_ops;
+
+extern char *suspend_writer_buffer;
+extern int suspend_writer_buffer_posn;
+extern int suspend_read_fd;
+extern struct extent_iterate_saved_state suspend_writer_posn_save[3];
+extern struct extent_iterate_state suspend_writer_posn;
+extern int suspend_header_bytes_used;
diff -urN oldtree/kernel/power/compression.c newtree/kernel/power/compression.c
--- oldtree/kernel/power/compression.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/compression.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,557 @@
+/*
+ * kernel/power/compression.c
+ *
+ * Copyright (C) 2003-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * This file contains data compression routines for suspend,
+ * using cryptoapi.
+ *
+ */
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/vmalloc.h>
+#include <linux/crypto.h>
+
+#include "suspend.h"
+#include "modules.h"
+#include "sysfs.h"
+#include "io.h"
+
+#define S2C_WRITE 0
+#define S2C_READ 1
+
+static int suspend_expected_compression = 0;
+
+static struct suspend_module_ops suspend_compression_ops;
+static struct suspend_module_ops *next_driver;
+
+static char suspend_compressor_name[32] = "lzf";
+static struct crypto_tfm *suspend_compressor_transform;
+
+static u8 *local_buffer = NULL;
+static u8 *page_buffer = NULL;
+static unsigned int bufofs;
+
+static int position = 0;
+       
+/* ---- Local buffer management ---- */
+
+/* 
+ * suspend_compress_allocate_local_buffer
+ *
+ * Allocates a page of memory for buffering output.
+ * Int: Zero if successful, -ENONEM otherwise.
+ */
+static int suspend_compress_allocate_local_buffer(void)
+{
+	if (!local_buffer) {
+		local_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	
+		if (!local_buffer) {
+			printk(KERN_ERR
+				"Failed to allocate the local buffer for "
+				"suspend2 compression driver.\n");
+			return -ENOMEM;
+		}
+	}
+
+	if (!page_buffer) {
+		page_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	
+		if (!page_buffer) {
+			printk(KERN_ERR
+				"Failed to allocate the page buffer for "
+				"suspend2 compression driver.\n");
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+/* 
+ * suspend_compress_free_local_buffer
+ *
+ * Frees memory allocated for buffering output.
+ */
+static inline void suspend_compress_free_local_buffer(void)
+{
+	if (local_buffer)
+		free_page((unsigned long) local_buffer);
+
+	local_buffer = NULL;
+
+	if (page_buffer)
+		free_page((unsigned long) page_buffer);
+
+	page_buffer = NULL;
+}
+
+/* 
+ * suspend_compress_cleanup
+ *
+ * Frees memory allocated for our labours.
+ */
+static void suspend_compress_cleanup(void)
+{
+	if (suspend_compressor_transform) {
+		crypto_free_tfm(suspend_compressor_transform);
+		suspend_compressor_transform = NULL;
+	}
+}
+
+/* 
+ * suspend_crypto_prepare
+ *
+ * Prepare to do some work by allocating buffers and transforms.
+ * Returns: Int: Zero. Even if we can't set up compression, we still
+ * seek to suspend.
+ */
+static int suspend_compress_crypto_prepare(void)
+{
+	if (!*suspend_compressor_name) {
+		printk("Suspend2: Compression enabled but no compressor name set.\n");
+		suspend_compression_ops.enabled = 0;
+		return 0;
+	}
+
+	if (!(suspend_compressor_transform = crypto_alloc_tfm(suspend_compressor_name, 0))) {
+		printk("Suspend2: Failed to initialise the %s compression transform.\n",
+				suspend_compressor_name);
+		suspend_compression_ops.enabled = 0;
+		return 0;
+	}
+
+	return 0;
+}
+
+/* 
+ * suspend_compress_write_cleanup(): Write unflushed data and free workspace.
+ * 
+ * Returns: Result of writing last page.
+ */
+static int suspend_compress_rw_cleanup(int rw)
+{
+	int ret = 0;
+	
+	if (rw == WRITE && suspend_compressor_transform)
+		ret = next_driver->write_chunk(virt_to_page(local_buffer));
+
+	suspend_compress_cleanup();
+	suspend_compress_free_local_buffer();
+
+	return ret;
+}
+
+/* 
+ * suspend_compress_rw_init()
+ * @stream_number:	Ignored.
+ *
+ * Allocate buffers and prepare to compress data.
+ * Returns: Zero on success, -ENOMEM if unable to vmalloc.
+ */
+static int suspend_compress_rw_init(int rw, int stream_number)
+{
+	int result;
+	
+	next_driver = suspend_get_next_filter(&suspend_compression_ops);
+
+	if (!next_driver) {
+		printk("Compression Driver: Argh! Nothing follows me in"
+				" the pipeline!");
+		return -ECHILD;
+	}
+
+	if ((result = suspend_compress_crypto_prepare() ||
+	     !suspend_compression_ops.enabled))
+		return result;
+	
+	if ((result = suspend_compress_allocate_local_buffer()))
+		return result;
+
+	if (rw == READ)
+		bufofs = PAGE_SIZE;
+	else {
+		/* Only reset the stats if starting to write an image */
+		if (stream_number == 2)
+			bytes_in = bytes_out = 0;
+	
+		bufofs = 0;
+	}
+
+	position = 0;
+
+	return 0;
+}
+
+/* 
+ * suspend_compress_write()
+ * @u8*:		Output buffer to be written.
+ * @unsigned int:	Length of buffer.
+ *
+ * Helper function for write_chunk. Write the compressed data.
+ * Return: Int.	Result to be passed back to caller.
+ */
+static int suspend_compress_write (u8 *buffer, unsigned int len)
+{
+	int ret;
+
+	bytes_out += len;
+
+	while (len + bufofs > PAGE_SIZE) {
+		unsigned int chunk = PAGE_SIZE - bufofs;
+		memcpy (local_buffer + bufofs, buffer, chunk);
+		buffer += chunk;
+		len -= chunk;
+		bufofs = 0;
+		if ((ret = next_driver->write_chunk(virt_to_page(local_buffer))) < 0)
+			return ret;
+	}
+	memcpy (local_buffer + bufofs, buffer, len);
+	bufofs += len;
+	return 0;
+}
+
+/* 
+ * suspend_compress_write_chunk()
+ *
+ * Compress a page of data, buffering output and passing on filled
+ * pages to the next module in the pipeline.
+ * 
+ * Buffer_page:	Pointer to a buffer of size PAGE_SIZE, containing
+ * data to be compressed.
+ *
+ * Returns:	0 on success. Otherwise the error is that returned by later
+ * 		modules, -ECHILD if we have a broken pipeline or -EIO if
+ * 		zlib errs.
+ */
+static int suspend_compress_write_chunk(struct page *buffer_page)
+{
+	int ret; 
+	unsigned int len;
+	u16 len_written;
+	char *buffer_start;
+	
+	if (!suspend_compressor_transform)
+		return next_driver->write_chunk(buffer_page);
+
+	buffer_start = kmap(buffer_page);
+
+	bytes_in += PAGE_SIZE;
+
+	len = PAGE_SIZE;
+
+	ret = crypto_comp_compress(suspend_compressor_transform,
+			buffer_start, PAGE_SIZE,
+			page_buffer, &len);
+	
+	if (ret) {
+		printk("Compression failed.\n");
+		goto failure;
+	}
+	
+	len_written = (u16) len;
+		
+	if ((ret = suspend_compress_write((u8 *)&len_written, 2)) >= 0) {
+		if ((ret = suspend_compress_write((u8 *) &position, sizeof(position))))
+			return -EIO;
+		if (len < PAGE_SIZE) { /* some compression */
+			position += len;
+			ret = suspend_compress_write(page_buffer, len);
+		} else {
+			ret = suspend_compress_write(buffer_start, PAGE_SIZE);
+			position += PAGE_SIZE;
+		}
+	}
+	position += 2 + sizeof(int);
+
+
+failure:
+	kunmap(buffer_page);
+	return ret;
+}
+
+/* 
+ * suspend_compress_read()
+ * @buffer: u8 *. Address of the buffer.
+ * @len: unsigned int. Length.
+ *
+ * Description:	Read data into compression buffer.
+ * Returns:	int:		Result of reading the image chunk.
+ */
+static int suspend_compress_read (u8 *buffer, unsigned int len)
+{
+	int ret;
+
+	while (len + bufofs > PAGE_SIZE) {
+		unsigned int chunk = PAGE_SIZE - bufofs;
+		memcpy(buffer, local_buffer + bufofs, chunk);
+		buffer += chunk;
+		len -= chunk;
+		bufofs = 0;
+		if ((ret = next_driver->read_chunk(
+				virt_to_page(local_buffer), SUSPEND_SYNC)) < 0) {
+			return ret;
+		}
+	}
+	memcpy (buffer, local_buffer + bufofs, len);
+	bufofs += len;
+	return 0;
+}
+
+/* 
+ * suspend_compress_read_chunk()
+ * @buffer_page: struct page *. Pointer to a buffer of size PAGE_SIZE.
+ * @sync:	int. Whether the previous module (or core) wants its data synchronously.
+ *
+ * Retrieve data from later modules and decompress it until the input buffer
+ * is filled.
+ * Zero if successful. Error condition from me or from downstream on failure.
+ */
+static int suspend_compress_read_chunk(struct page *buffer_page, int sync)
+{
+	int ret, position_saved; 
+	unsigned int len;
+	u16 len_written;
+	char *buffer_start;
+
+	if (!suspend_compressor_transform)
+		return next_driver->read_chunk(buffer_page, SUSPEND_ASYNC);
+
+	/* 
+	 * All our reads must be synchronous - we can't decompress
+	 * data that hasn't been read yet.
+	 */
+
+	buffer_start = kmap(buffer_page);
+
+	if ((ret = suspend_compress_read ((u8 *)&len_written, 2)) >= 0) {
+		len = (unsigned int) len_written;
+		ret = suspend_compress_read((u8 *) &position_saved, sizeof(position_saved));
+		if (ret)
+			return ret;
+
+		if (position != position_saved) {
+			printk("Position saved (%d) != position I'm at now (%d).\n",
+					position_saved, position);
+			BUG_ON(1);
+		}
+		if (len >= PAGE_SIZE) { /* uncompressed */
+			ret = suspend_compress_read(buffer_start, PAGE_SIZE);
+			if (ret)
+				return ret;
+
+			position += PAGE_SIZE;
+		} else { /* compressed */
+			if ((ret = suspend_compress_read(page_buffer, len)) >= 0) {
+				int outlen = PAGE_SIZE;
+				/* Important note.
+				 *
+				 * For Deflate, decompression return values may represent
+				 * errors. Deflate complains when everything is alright, so
+				 * we ignore the errors unless the number of output bytes is
+				 * not PAGE_SIZE.
+				 */
+				crypto_comp_decompress(suspend_compressor_transform, 
+						page_buffer, len,
+						buffer_start, &outlen);
+				if (outlen != PAGE_SIZE) {
+					printk("Decompression yielded %d bytes instead of %ld.\n", outlen, PAGE_SIZE);
+					ret = -EIO;
+				} else
+					ret = 0;
+			}
+			position += len;
+		}
+		position += 2 + sizeof(int);
+	} else
+		printk("Compress_read returned %d.", ret);
+	kunmap(buffer_page);
+	return ret;
+}
+
+/* 
+ * suspend_compress_print_debug_stats
+ * @buffer: Pointer to a buffer into which the debug info will be printed.
+ * @size: Size of the buffer.
+ *
+ * Print information to be recorded for debugging purposes into a buffer.
+ * Returns: Number of characters written to the buffer.
+ */
+
+static int suspend_compress_print_debug_stats(char *buffer, int size)
+{
+	int pages_in = bytes_in >> PAGE_SHIFT, 
+		pages_out = bytes_out >> PAGE_SHIFT;
+	int len;
+	
+	/* Output the compression ratio achieved. */
+	if (*suspend_compressor_name)
+		len = snprintf_used(buffer, size, "- Compressor is '%s'.\n",
+				suspend_compressor_name);
+	else
+		len = snprintf_used(buffer, size, "- Compressor is not set.\n");
+
+	if (pages_in)
+		len+= snprintf_used(buffer+len, size - len,
+		  "  Compressed %ld bytes into %ld (%d percent compression).\n",
+		  bytes_in, bytes_out, (pages_in - pages_out) * 100 / pages_in);
+	return len;
+}
+
+/* 
+ * suspend_compress_compression_memory_needed
+ *
+ * Tell the caller how much memory we need to operate during suspend/resume.
+ * Returns: Unsigned long. Maximum number of bytes of memory required for
+ * operation.
+ */
+static unsigned long suspend_compress_memory_needed(void)
+{
+	return 2 * PAGE_SIZE;
+}
+
+static unsigned long suspend_compress_storage_needed(void)
+{
+	return 4 * sizeof(unsigned long) + strlen(suspend_compressor_name) + 1;
+}
+
+/* 
+ * suspend_compress_save_config_info
+ * @buffer: Pointer to a buffer of size PAGE_SIZE.
+ *
+ * Save informaton needed when reloading the image at resume time.
+ * Returns: Number of bytes used for saving our data.
+ */
+static int suspend_compress_save_config_info(char *buffer)
+{
+	int namelen = strlen(suspend_compressor_name) + 1;
+	int total_len;
+	
+	*((unsigned long *) buffer) = bytes_in;
+	*((unsigned long *) (buffer + 1 * sizeof(unsigned long))) = bytes_out;
+	*((unsigned long *) (buffer + 2 * sizeof(unsigned long))) =
+		suspend_expected_compression;
+	*((unsigned long *) (buffer + 3 * sizeof(unsigned long))) = namelen;
+	strncpy(buffer + 4 * sizeof(unsigned long), suspend_compressor_name, 
+								namelen);
+	total_len = 4 * sizeof(unsigned long) + namelen;
+	return total_len;
+}
+
+/* suspend_compress_load_config_info
+ * @buffer: Pointer to the start of the data.
+ * @size: Number of bytes that were saved.
+ *
+ * Description:	Reload information needed for decompressing the image at
+ * resume time.
+ */
+static void suspend_compress_load_config_info(char *buffer, int size)
+{
+	int namelen;
+	
+	bytes_in = *((unsigned long *) buffer);
+	bytes_out = *((unsigned long *) (buffer + 1 * sizeof(unsigned long)));
+	suspend_expected_compression = *((unsigned long *) (buffer + 2 *
+				sizeof(unsigned long)));
+	namelen = *((unsigned long *) (buffer + 3 * sizeof(unsigned long)));
+	strncpy(suspend_compressor_name, buffer + 4 * sizeof(unsigned long),
+			namelen);
+	return;
+}
+
+/* 
+ * suspend_expected_compression_ratio
+ * 
+ * Description:	Returns the expected ratio between data passed into this module
+ * 		and the amount of data output when writing.
+ * Returns:	100 if the module is disabled. Otherwise the value set by the
+ * 		user via our sysfs entry.
+ */
+
+int suspend_expected_compression_ratio(void)
+{
+	if (!suspend_compression_ops.enabled)
+		return 100;
+	else
+		return 100 - suspend_expected_compression;
+}
+
+/*
+ * data for our sysfs entries.
+ */
+static struct suspend_sysfs_data sysfs_params[] = {
+	{
+		SUSPEND2_ATTR("expected_compression", SYSFS_RW),
+		SYSFS_INT(&suspend_expected_compression, 0, 99)
+	},
+
+	{
+		SUSPEND2_ATTR("enabled", SYSFS_RW),
+		SYSFS_INT(&suspend_compression_ops.enabled, 0, 1)
+	},
+
+	{
+		SUSPEND2_ATTR("algorithm", SYSFS_RW),
+		SYSFS_STRING(suspend_compressor_name, 31, SYSFS_SM_NOT_NEEDED)
+	}
+};
+
+/*
+ * Ops structure.
+ */
+static struct suspend_module_ops suspend_compression_ops = {
+	.type			= FILTER_MODULE,
+	.name			= "Suspend2 Compressor",
+	.module			= THIS_MODULE,
+	.memory_needed 		= suspend_compress_memory_needed,
+	.print_debug_info	= suspend_compress_print_debug_stats,
+	.save_config_info	= suspend_compress_save_config_info,
+	.load_config_info	= suspend_compress_load_config_info,
+	.storage_needed		= suspend_compress_storage_needed,
+	
+	.rw_init		= suspend_compress_rw_init,
+	.rw_cleanup		= suspend_compress_rw_cleanup,
+
+	.write_chunk		= suspend_compress_write_chunk,
+	.read_chunk		= suspend_compress_read_chunk,
+};
+
+/* ---- Registration ---- */
+
+static __init int suspend_compress_load(void)
+{
+	int result;
+	int i, numfiles = sizeof(sysfs_params) / sizeof(struct suspend_sysfs_data);
+
+	printk("Suspend2 Compression Driver loading.\n");
+	if (!(result = suspend_register_module(&suspend_compression_ops))) {
+		struct kobject *kobj = make_suspend2_sysdir("compression");
+		for (i=0; i< numfiles; i++)
+			suspend_register_sysfs_file(kobj, &sysfs_params[i]);
+	} else
+		printk("Suspend2 Compression Driver unable to register!\n");
+	return result;
+}
+
+#ifdef MODULE
+static __exit void suspend_compress_unload(void)
+{
+	printk("Suspend2 Compression Driver unloading.\n");
+	for (i=0; i< numfiles; i++)
+		suspend_unregister_sysfs_file(&sysfs_params[i]);
+	suspend_unregister_module(&suspend_compression_ops);
+}
+
+module_init(suspend_compress_load);
+module_exit(suspend_compress_unload);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nigel Cunningham");
+MODULE_DESCRIPTION("Compression Support for Suspend2");
+#else
+late_initcall(suspend_compress_load);
+#endif
diff -urN oldtree/kernel/power/disk.c newtree/kernel/power/disk.c
--- oldtree/kernel/power/disk.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/power/disk.c	2006-10-06 17:28:20.000000000 -0400
@@ -10,6 +10,7 @@
  */
 
 #include <linux/suspend.h>
+#include <linux/freezer.h>
 #include <linux/syscalls.h>
 #include <linux/reboot.h>
 #include <linux/string.h>
@@ -22,7 +23,7 @@
 #include <linux/cpu.h>
 
 #include "power.h"
-
+#include "suspend.h"
 
 static int noresume = 0;
 char resume_file[256] = CONFIG_PM_STD_PARTITION;
@@ -95,7 +96,7 @@
 	if (!(error = swsusp_shrink_memory()))
 		return 0;
 thaw:
-	thaw_processes();
+	thaw_processes(FREEZER_ALL_THREADS);
 enable_cpus:
 	enable_nonboot_cpus();
 	pm_restore_console();
@@ -105,7 +106,7 @@
 static void unprepare_processes(void)
 {
 	platform_finish();
-	thaw_processes();
+	thaw_processes(FREEZER_ALL_THREADS);
 	enable_nonboot_cpus();
 	pm_restore_console();
 }
@@ -123,6 +124,16 @@
 {
 	int error;
 
+#ifdef CONFIG_SUSPEND2
+	if (test_action_state(SUSPEND_REPLACE_SWSUSP)) {
+		if (suspend_start_anything(1))
+			return -EBUSY;
+		suspend_main(1);
+		suspend_finish_anything(1);
+		return 0;
+	}
+#endif
+
 	error = prepare_processes();
 	if (error)
 		return error;
@@ -186,10 +197,20 @@
  *
  */
 
-static int software_resume(void)
+int software_resume(void)
 {
 	int error;
 
+#ifdef CONFIG_SUSPEND2
+	/* 
+	 * We can't know (until an image header - if any - is loaded), whether
+	 * we did override swsusp. We therefore ensure that both are tried.
+	 */
+	if (test_action_state(SUSPEND_REPLACE_SWSUSP))
+		printk("Replacing swsusp.\n");
+		suspend2_try_resume();
+#endif
+
 	down(&pm_sem);
 	if (!swsusp_resume_device) {
 		if (!strlen(resume_file)) {
@@ -257,9 +278,6 @@
 	return 0;
 }
 
-late_initcall(software_resume);
-
-
 static const char * const pm_disk_modes[] = {
 	[PM_DISK_FIRMWARE]	= "firmware",
 	[PM_DISK_PLATFORM]	= "platform",
@@ -440,6 +458,7 @@
 static int __init noresume_setup(char *str)
 {
 	noresume = 1;
+	set_suspend_state(SUSPEND_NORESUME_SPECIFIED);
 	return 1;
 }
 
diff -urN oldtree/kernel/power/encryption.c newtree/kernel/power/encryption.c
--- oldtree/kernel/power/encryption.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/encryption.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,494 @@
+/*
+ * kernel/power/encryption.c
+ *
+ * Copyright (C) 2003-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * This file contains data encryption routines for suspend,
+ * using cryptoapi transforms.
+ *
+ * ToDo:
+ * - Apply min/max_keysize the cipher changes.
+ * - Test.
+ */
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/vmalloc.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+
+#include "suspend.h"
+#include "modules.h"
+#include "sysfs.h"
+#include "io.h"
+
+#define S2C_WRITE 0
+#define S2C_READ 1
+
+static struct suspend_module_ops suspend_encryption_ops;
+static struct suspend_module_ops *next_driver;
+
+static char suspend_encryptor_name[32];
+static struct crypto_blkcipher *suspend_encryptor_transform;
+static struct blkcipher_desc suspend_encryptor_desc;
+static char suspend_encryptor_key[256];
+static int suspend_key_len;
+static char suspend_encryptor_iv[256];
+static int suspend_encryptor_save_key_and_iv;
+
+static u8 *page_buffer = NULL;
+static unsigned int bufofs;
+
+static struct scatterlist suspend_crypt_sg[PAGE_SIZE/8];
+       
+/* ---- Local buffer management ---- */
+
+/* allocate_local_buffer
+ *
+ * Description:	Allocates a page of memory for buffering output.
+ * Returns:	Int: Zero if successful, -ENONEM otherwise.
+ */
+static int allocate_local_buffer(void)
+{
+	if (!page_buffer) {
+		int i, remainder;
+		
+		page_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	
+		if (!page_buffer) {
+			printk(KERN_ERR
+				"Failed to allocate the page buffer for "
+				"suspend2 encryption driver.\n");
+			return -ENOMEM;
+		}
+
+		for (i=0; i < (PAGE_SIZE / suspend_key_len); i++) {
+			suspend_crypt_sg[i].page = virt_to_page(page_buffer);
+			suspend_crypt_sg[i].offset = suspend_key_len * i;
+			suspend_crypt_sg[i].length = suspend_key_len;
+		}
+		
+		remainder = PAGE_SIZE % suspend_key_len;
+
+		if (remainder) {
+			suspend_crypt_sg[i].page = virt_to_page(page_buffer);
+			suspend_crypt_sg[i].offset = suspend_key_len * i;
+			suspend_crypt_sg[i].length = remainder;
+		}
+	}
+
+	return 0;
+}
+
+/* free_local_buffer
+ *
+ * Description:	Frees memory allocated for buffering output.
+ */
+static void free_local_buffer(void)
+{
+	if (page_buffer)
+		free_page((unsigned long) page_buffer);
+
+	page_buffer = NULL;
+}
+
+/* suspend_encrypt_rw_cleanup
+ *
+ * Description:	Frees memory allocated for our labours.
+ */
+static int suspend_encrypt_rw_cleanup(int rw)
+{
+	if (suspend_encryptor_transform) {
+		crypto_free_blkcipher(suspend_encryptor_transform);
+		suspend_encryptor_transform = NULL;
+	}
+
+	free_local_buffer();
+
+	return 0;
+}
+
+/* suspend_crypto_prepare
+ *
+ * Description:	Prepare to do some work by allocating buffers and transforms.
+ * Returns:	Int: Zero if successful, 1 otherwise.
+ */
+static int suspend_encrypt_rw_prepare(int rw)
+{
+	int iv_len;
+
+	if (!*suspend_encryptor_name) {
+		printk("Suspend2: Encryptor enabled but no name set.\n");
+		suspend_encryption_ops.enabled = 0;
+		return 1;
+	}
+
+	suspend_encryptor_desc.tfm = suspend_encryptor_transform;
+	suspend_encryptor_desc.flags = 0;
+
+	suspend_encryptor_transform = crypto_alloc_blkcipher(
+		suspend_encryptor_name, 0, 0);
+	if (!suspend_encryptor_transform) {
+		printk("Suspend2: Failed to initialise the encryption "
+				"transform (%s).\n",
+				suspend_encryptor_name);
+		suspend_encryption_ops.enabled = 0;
+		return 1;
+	}
+
+	if (rw == READ)
+		bufofs = PAGE_SIZE;
+	else
+		bufofs = 0;
+
+	crypto_blkcipher_clear_flags(suspend_encryptor_transform, 0);
+
+	suspend_key_len = strlen(suspend_encryptor_key);
+
+	if (crypto_blkcipher_setkey(suspend_encryptor_transform, suspend_encryptor_key, 
+				suspend_key_len)) {
+		printk("%d is an invalid key length for cipher %s.\n",
+					suspend_key_len,
+					suspend_encryptor_name);
+		return 1;
+	}
+	
+	iv_len = crypto_blkcipher_ivsize(suspend_encryptor_transform);
+
+	if (iv_len)
+		crypto_blkcipher_set_iv(suspend_encryptor_transform,
+			suspend_encryptor_iv,
+			iv_len);
+		
+	return 0;
+}
+
+/* ---- Exported functions ---- */
+
+/* suspend_encrypt_write_chunk()
+ *
+ * Description:	Encrypt a page of data, buffering output and passing on
+ * 		filled pages to the next module in the pipeline.
+ * Arguments:	Buffer_page:	Pointer to a buffer of size PAGE_SIZE, 
+ * 				containing data to be encrypted.
+ * Returns:	0 on success. Otherwise the error is that returned by later
+ * 		modules, -ECHILD if we have a broken pipeline or -EIO if
+ * 		zlib errs.
+ */
+static int suspend_encrypt_write_chunk(struct page *buffer_page)
+{
+	int ret; 
+	unsigned int len;
+	u16 len_written;
+	char *buffer_start;
+	
+	if (!suspend_encryptor_transform)
+		return next_driver->write_chunk(buffer_page);
+
+	buffer_start = kmap(buffer_page);
+	memcpy(page_buffer, buffer_start, PAGE_SIZE);
+	kunmap(buffer_page);
+	
+	bytes_in += PAGE_SIZE;
+
+	len = PAGE_SIZE;
+
+	ret = crypto_blkcipher_encrypt(&suspend_encryptor_desc,
+			suspend_crypt_sg, suspend_crypt_sg, PAGE_SIZE);
+	
+	if (ret) {
+		printk("Encryption failed.\n");
+		return -EIO;
+	}
+	
+	len_written = (u16) len;
+
+	ret = next_driver->write_chunk(virt_to_page(page_buffer));
+
+	return ret;
+}
+
+/* rw_init()
+ *
+ * Description:	Prepare to read a new stream of data.
+ * Arguments:	int: Section of image about to be read.
+ * Returns:	int: Zero on success, error number otherwise.
+ */
+static int suspend_encrypt_rw_init(int rw, int stream_number)
+{
+	int result;
+
+	next_driver = suspend_get_next_filter(&suspend_encryption_ops);
+
+	if (!next_driver) {
+		printk("Encryption Driver: Argh! I'm at the end of the pipeline!");
+		return -ECHILD;
+	}
+	
+	if ((result = suspend_encrypt_rw_prepare(rw))) {
+		set_result_state(SUSPEND_ENCRYPTION_SETUP_FAILED);
+		suspend_encrypt_rw_cleanup(rw);
+		return result;
+	}
+	
+	if ((result = allocate_local_buffer()))
+		return result;
+
+	if (rw == WRITE && stream_number == 2)
+		bytes_in = bytes_out = 0;
+	
+	bufofs = (rw == READ) ? PAGE_SIZE : 0;
+
+	return 0;
+}
+
+/* suspend_encrypt_read_chunk()
+ *
+ * Description:	Retrieve data from later modules and deencrypt it until the
+ * 		input buffer is filled.
+ * Arguments:	Buffer_start: 	Pointer to a buffer of size PAGE_SIZE.
+ * 		Sync:		Whether the previous module (or core) wants its
+ * 				data synchronously.
+ * Returns:	Zero if successful. Error condition from me or from downstream
+ * 		on failure.
+ */
+static int suspend_encrypt_read_chunk(struct page *buffer_page, int sync)
+{
+	int ret; 
+	char *buffer_start;
+
+	if (!suspend_encryptor_transform)
+		return next_driver->read_chunk(buffer_page, sync);
+
+	/* 
+	 * All our reads must be synchronous - we can't deencrypt
+	 * data that hasn't been read yet.
+	 */
+
+	if ((ret = next_driver->read_chunk(
+			virt_to_page(page_buffer), SUSPEND_SYNC)) < 0) {
+		printk("Failed to read an encrypted block.\n");
+		return ret;
+	}
+
+	ret = crypto_blkcipher_decrypt(&suspend_encryptor_desc,
+			suspend_crypt_sg, suspend_crypt_sg, PAGE_SIZE);
+
+	if (ret)
+		printk("Decrypt function returned %d.\n", ret);
+
+	buffer_start = kmap(buffer_page);
+	memcpy(buffer_start, page_buffer, PAGE_SIZE);
+	kunmap(buffer_page);
+	return ret;
+}
+
+/* suspend_encrypt_print_debug_stats
+ *
+ * Description:	Print information to be recorded for debugging purposes into a
+ * 		buffer.
+ * Arguments:	buffer: Pointer to a buffer into which the debug info will be
+ * 			printed.
+ * 		size:	Size of the buffer.
+ * Returns:	Number of characters written to the buffer.
+ */
+static int suspend_encrypt_print_debug_stats(char *buffer, int size)
+{
+	int len;
+	
+	if (*suspend_encryptor_name)
+		len = snprintf_used(buffer, size, "- Encryptor is '%s'.\n",
+				suspend_encryptor_name);
+	else
+		len = snprintf_used(buffer, size, "- Encryptor is not set.\n");
+	return len;
+}
+
+/* encryption_memory_needed
+ *
+ * Description:	Tell the caller how much memory we need to operate during
+ * 		suspend/resume.
+ * Returns:	Unsigned long. Maximum number of bytes of memory required for
+ * 		operation.
+ */
+static unsigned long suspend_encrypt_memory_needed(void)
+{
+	return PAGE_SIZE;
+}
+
+static unsigned long suspend_encrypt_storage_needed(void)
+{
+	return 3 + strlen(suspend_encryptor_name) +
+		(suspend_encryptor_save_key_and_iv ?
+		 (4 + strlen(suspend_encryptor_key) +
+		  strlen(suspend_encryptor_iv)) : 0);
+}
+	
+/* suspend_encrypt_save_config_info
+ *
+ * Description:	Save informaton needed when reloading the image at resume time.
+ * Arguments:	Buffer:		Pointer to a buffer of size PAGE_SIZE.
+ * Returns:	Number of bytes used for saving our data.
+ */
+static int suspend_encrypt_save_config_info(char *buffer)
+{
+	int buf_offset, str_size;
+
+	str_size = strlen(suspend_encryptor_name);
+	*buffer = (char) str_size;
+	strncpy(buffer + 1, suspend_encryptor_name, str_size + 1);
+	buf_offset = str_size + 2;
+
+	*(buffer + buf_offset) = (char) suspend_encryptor_save_key_and_iv;
+	buf_offset++;
+
+	if (suspend_encryptor_save_key_and_iv) {
+		
+		str_size = strlen(suspend_encryptor_key);
+		*(buffer + buf_offset) = (char) str_size;
+		strncpy(buffer + buf_offset + 1, suspend_encryptor_key, str_size + 1);
+
+		buf_offset+= str_size + 2;
+
+		str_size = strlen(suspend_encryptor_iv);
+		*(buffer + buf_offset) = (char) str_size;
+		strncpy(buffer + buf_offset + 1, suspend_encryptor_iv, str_size + 1);
+
+		buf_offset += str_size + 2;
+	}
+
+	return buf_offset;
+}
+
+/* suspend_encrypt_load_config_info
+ *
+ * Description:	Reload information needed for deencrypting the image at 
+ * 		resume time.
+ * Arguments:	Buffer:		Pointer to the start of the data.
+ *		Size:		Number of bytes that were saved.
+ */
+static void suspend_encrypt_load_config_info(char *buffer, int size)
+{
+	int buf_offset, str_size;
+
+	str_size = (int) *buffer;
+	strncpy(suspend_encryptor_name, buffer + 1, str_size + 1);
+	buf_offset = str_size + 2;
+	
+	suspend_encryptor_save_key_and_iv = (int) *(buffer + buf_offset);
+	buf_offset++;
+
+	if (suspend_encryptor_save_key_and_iv) {
+		str_size = (int) *(buffer + buf_offset);
+		strncpy(suspend_encryptor_key, buffer + buf_offset + 1, str_size + 1);
+
+		buf_offset+= str_size + 2;
+
+		str_size = (int) *(buffer + buf_offset);
+		strncpy(suspend_encryptor_iv, buffer + buf_offset + 1, str_size + 1);
+
+		buf_offset += str_size + 2;
+	} else {
+		*suspend_encryptor_key = 0;
+		*suspend_encryptor_iv = 0;
+	}
+	
+	if (buf_offset != size) {
+		printk("Suspend Encryptor config info size mismatch (%d != %d): settings ignored.\n",
+				buf_offset, size);
+		*suspend_encryptor_key = 0;
+		*suspend_encryptor_iv = 0;
+	}
+	return;
+}
+
+/*
+ * data for our sysfs entries.
+ */
+static struct suspend_sysfs_data sysfs_params[] = {
+	{
+		SUSPEND2_ATTR("algorithm", SYSFS_RW),
+		SYSFS_STRING(suspend_encryptor_name, 31, SYSFS_SM_NOT_NEEDED)
+	},
+
+	{
+		SUSPEND2_ATTR("save_key_and_iv", SYSFS_RW),
+		SYSFS_INT(&suspend_encryptor_save_key_and_iv, 0, 1)
+	},
+
+	{
+		SUSPEND2_ATTR("key", SYSFS_RW),
+		SYSFS_STRING(suspend_encryptor_key, 255, SYSFS_SM_NOT_NEEDED)
+	},
+
+	{
+		SUSPEND2_ATTR("iv", SYSFS_RW),
+		SYSFS_STRING(suspend_encryptor_iv, 255, SYSFS_SM_NOT_NEEDED)
+	},
+
+	{
+		SUSPEND2_ATTR("enabled", SYSFS_RW),
+		SYSFS_INT(&suspend_encryption_ops.enabled, 0, 1)
+	},
+	
+};
+
+/*
+ * Ops structure.
+ */
+
+static struct suspend_module_ops suspend_encryption_ops = {
+	.type			= FILTER_MODULE,
+	.name			= "Encryptor",
+	.module			= THIS_MODULE,
+	.memory_needed 		= suspend_encrypt_memory_needed,
+	.print_debug_info	= suspend_encrypt_print_debug_stats,
+	.save_config_info	= suspend_encrypt_save_config_info,
+	.load_config_info	= suspend_encrypt_load_config_info,
+	.storage_needed		= suspend_encrypt_storage_needed,
+	
+	.rw_init		= suspend_encrypt_rw_init,
+	.rw_cleanup		= suspend_encrypt_rw_cleanup,
+
+	.write_chunk		= suspend_encrypt_write_chunk,
+	.read_chunk		= suspend_encrypt_read_chunk,
+};
+
+/* ---- Registration ---- */
+
+static __init int suspend_encrypt_load(void)
+{
+	int result;
+	int i, numfiles = sizeof(sysfs_params) / sizeof(struct suspend_sysfs_data);
+
+	printk("Suspend2 Encryption Driver loading.\n");
+	if (!(result = suspend_register_module(&suspend_encryption_ops))) {
+		struct kobject *kobj = make_suspend2_sysdir("encryption");
+		for (i=0; i< numfiles; i++)
+			suspend_register_sysfs_file(kobj, &sysfs_params[i]);
+		suspend_encryption_ops.enabled = 0;
+	} else
+		printk("Suspend2 Encryption Driver unable to register!\n");
+	return result;
+}
+
+#ifdef MODULE
+static __exit void suspend_compress_unload(void)
+{
+	printk("Suspend2 Encryption Driver unloading.\n");
+	for (i=0; i< numfiles; i++)
+		suspend_unregister_sysfs_file(&sysfs_params[i]);
+	suspend_unregister_module(&suspend_encryption_ops);
+}
+
+module_init(suspend_encrypt_load);
+module_exit(suspend_encrypt_unload);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nigel Cunningham");
+MODULE_DESCRIPTION("Encryption Support for Suspend2");
+#else
+late_initcall(suspend_encrypt_load);
+#endif
diff -urN oldtree/kernel/power/extent.c newtree/kernel/power/extent.c
--- oldtree/kernel/power/extent.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/extent.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,309 @@
+/* 
+ * kernel/power/extent.c
+ * 
+ * (C) 2003-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * Distributed under GPLv2.
+ * 
+ * These functions encapsulate the manipulation of storage metadata. For
+ * pageflags, we use dynamically allocated bitmaps.
+ */
+
+#include <linux/module.h>
+#include <linux/suspend.h>
+#include "modules.h"
+#include "extent.h"
+#include "ui.h"
+
+int suspend_extents_allocated = 0;
+
+/* suspend_get_extent
+ *
+ * Returns a free extent. May fail, returning NULL instead.
+ */
+static struct extent *suspend_get_extent(void)
+{
+	struct extent *result;
+	
+	if (!(result = kmalloc(sizeof(struct extent), GFP_ATOMIC)))
+		return NULL;
+
+	suspend_extents_allocated++;
+	result->minimum = result->maximum = 0;
+	result->next = NULL;
+
+	return result;
+}
+
+/* suspend_put_extent.
+ *
+ * Frees an extent. Assumes unlinking is done by the caller.
+ */
+void suspend_put_extent(struct extent *extent)
+{
+	BUG_ON(!extent);
+
+	kfree(extent);
+	suspend_extents_allocated--;
+}
+
+/* suspend_put_extent_chain.
+ *
+ * Frees a whole chain of extents.
+ */
+void suspend_put_extent_chain(struct extent_chain *chain)
+{
+	struct extent *this;
+
+	this = chain->first;
+
+	while(this) {
+		struct extent *next = this->next;
+		kfree(this);
+		chain->frees++;
+		suspend_extents_allocated --;
+		this = next;
+	}
+	
+	BUG_ON(chain->frees != chain->allocs);
+	chain->first = chain->last = chain->last_touched = NULL;
+	chain->size = chain->allocs = chain->frees = 0;
+}
+
+/* 
+ * suspend_add_to_extent_chain
+ *
+ * Add an extent to an existing chain.
+ */
+int suspend_add_to_extent_chain(struct extent_chain *chain, 
+		unsigned long minimum, unsigned long maximum)
+{
+	struct extent *new_extent = NULL, *start_at;
+
+	/* Find the right place in the chain */
+	start_at = (chain->last_touched && 
+		    (chain->last_touched->minimum < minimum)) ?
+		chain->last_touched : NULL;
+
+	if (!start_at && chain->first && chain->first->minimum < minimum)
+		start_at = chain->first;
+
+	while (start_at && start_at->next && start_at->next->minimum < minimum)
+		start_at = start_at->next;
+
+	if (start_at && start_at->maximum == (minimum - 1)) {
+		start_at->maximum = maximum;
+
+		/* Merge with the following one? */
+		if (start_at->next &&
+		    start_at->maximum + 1 == start_at->next->minimum) {
+			struct extent *to_free = start_at->next;
+			start_at->maximum = start_at->next->maximum;
+			start_at->next = start_at->next->next;
+			chain->frees++;
+			suspend_put_extent(to_free);
+		}
+
+		chain->last_touched = start_at;
+		chain->size+= (maximum - minimum + 1);
+
+		return 0;
+	}
+
+	new_extent = suspend_get_extent();
+	if (!new_extent) {
+		printk("Error unable to append a new extent to the chain.\n");
+		return 2;
+	}
+
+	chain->allocs++;
+	chain->size+= (maximum - minimum + 1);
+	new_extent->minimum = minimum;
+	new_extent->maximum = maximum;
+	new_extent->next = NULL;
+
+	chain->last_touched = new_extent;
+
+	if (start_at) {
+		struct extent *next = start_at->next;
+		start_at->next = new_extent;
+		new_extent->next = next;
+		if (!next)
+			chain->last = new_extent;
+	} else {
+		if (chain->first) {
+			new_extent->next = chain->first;
+			chain->first = new_extent;
+		} else
+			chain->last = chain->first = new_extent;
+	}
+
+	return 0;
+}
+
+/* suspend_serialise_extent_chain
+ *
+ * Write a chain in the image.
+ */
+int suspend_serialise_extent_chain(struct suspend_module_ops *owner,
+		struct extent_chain *chain)
+{
+	struct extent *this;
+	int ret, i = 0;
+	
+	if ((ret = suspend_active_writer->rw_header_chunk(WRITE, owner,
+		(char *) chain,
+		3 * sizeof(int))))
+		return ret;
+
+	this = chain->first;
+	while (this) {
+		if ((ret = suspend_active_writer->rw_header_chunk(WRITE, owner,
+				(char *) this,
+				2 * sizeof(unsigned long))))
+			return ret;
+		this = this->next;
+		i++;
+	}
+
+	if (i != (chain->allocs - chain->frees)) {
+		printk(KERN_EMERG "Saved %d extents but chain metadata says there should be %d-%d.\n",
+				i, chain->allocs, chain->frees);
+		BUG();
+	}
+
+	return ret;
+}
+
+/* suspend_load_extent_chain
+ *
+ * Read back a chain saved in the image.
+ */
+int suspend_load_extent_chain(struct extent_chain *chain)
+{
+	struct extent *this, *last = NULL;
+	int i, ret;
+
+	if (!(ret = suspend_active_writer->rw_header_chunk(READ, NULL,
+		(char *) chain,
+		3 * sizeof(int))))
+		return ret;
+
+	for (i = 0; i < (chain->allocs - chain->frees); i++) {
+		this = kmalloc(sizeof(struct extent), GFP_ATOMIC);
+		BUG_ON(!this); /* Shouldn't run out of memory trying this! */
+		this->next = NULL;
+		if (!(ret = suspend_active_writer->rw_header_chunk(READ, NULL,
+				(char *) this, 2 * sizeof(unsigned long))))
+			return ret;
+		if (last)
+			last->next = this;
+		else
+			chain->first = this;
+		last = this;
+	}
+	chain->last = last;
+	return ret;
+}
+
+/* suspend_extent_state_next
+ *
+ * Given a state, progress to the next valid entry. We may begin in an
+ * invalid state, as we do when invoked after extent_state_goto_start below.
+ *
+ * When using compression and expected_compression > 0, we allocate fewer
+ * swap entries, so we can validly run out of data to return.
+ */
+unsigned long suspend_extent_state_next(struct extent_iterate_state *state)
+{
+	if (state->current_chain > state->num_chains)
+		return 0;
+
+	if (state->current_extent) {
+		if (state->current_offset == state->current_extent->maximum) {
+			if (state->current_extent->next) {
+				state->current_extent = state->current_extent->next;
+				state->current_offset = state->current_extent->minimum;
+			} else {
+				state->current_extent = NULL;
+				state->current_offset = 0;
+			}
+		} else
+			state->current_offset++;
+	}
+
+	while(!state->current_extent) {
+		int chain_num = ++(state->current_chain);
+
+		if (chain_num > state->num_chains)
+			return 0;
+
+		state->current_extent = (state->chains + chain_num)->first;
+
+		if (!state->current_extent)
+			continue;
+
+		state->current_offset = state->current_extent->minimum;
+	}
+
+	return state->current_offset;
+}
+
+/* suspend_extent_state_goto_start
+ *
+ * Find the first valid value in a group of chains.
+ */
+void suspend_extent_state_goto_start(struct extent_iterate_state *state)
+{
+	state->current_chain = -1;
+	state->current_extent = NULL;
+	state->current_offset = 0;
+}
+
+/* suspend_extent_start_save
+ *
+ * Given a state and a struct extent_state_store, save the crreutn
+ * position in a format that can be used with relocated chains (at
+ * resume time).
+ */
+void suspend_extent_state_save(struct extent_iterate_state *state,
+		struct extent_iterate_saved_state *saved_state)
+{
+	struct extent *extent;
+
+	saved_state->chain_num = state->current_chain;
+	saved_state->extent_num = 0;
+	saved_state->offset = state->current_offset;
+
+	if (saved_state->chain_num == -1)
+		return;
+	
+	extent = (state->chains + state->current_chain)->first;
+
+	while (extent != state->current_extent) {
+		saved_state->extent_num++;
+		extent = extent->next;
+	}
+}
+
+/* suspend_extent_start_restore
+ *
+ * Restore the position saved by extent_state_save.
+ */
+void suspend_extent_state_restore(struct extent_iterate_state *state,
+		struct extent_iterate_saved_state *saved_state)
+{
+	int posn = saved_state->extent_num;
+
+	if (saved_state->chain_num == -1) {
+		suspend_extent_state_goto_start(state);
+		return;
+	}
+
+	state->current_chain = saved_state->chain_num;
+	state->current_extent = (state->chains + state->current_chain)->first;
+	state->current_offset = saved_state->offset;
+
+	while (posn--)
+		state->current_extent = state->current_extent->next;
+}
diff -urN oldtree/kernel/power/extent.h newtree/kernel/power/extent.h
--- oldtree/kernel/power/extent.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/extent.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,82 @@
+/*
+ * kernel/power/extent.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * It contains declarations related to extents. Extents are
+ * suspend's method of storing some of the metadata for the image.
+ * See extent.c for more info.
+ *
+ */
+
+#include "modules.h"
+
+#ifndef EXTENT_H
+#define EXTENT_H
+struct extent_chain {
+	int size; /* size of the extent ie sum (max-min+1) */
+	int allocs, frees;
+	char *name;
+	struct extent *first, *last, *last_touched;
+};
+
+/*
+ * We rely on extents not fitting evenly into a page.
+ * The last four bytes are used to store the number
+ * of the page, to make saving & reloading pages simpler.
+ */
+struct extent {
+	unsigned long minimum, maximum;
+	struct extent *next;
+};
+
+struct extent_iterate_state {
+	struct extent_chain *chains;
+	int num_chains;
+	int current_chain;
+	struct extent *current_extent;
+	unsigned long current_offset;
+};
+
+struct extent_iterate_saved_state {
+	int chain_num;
+	int extent_num;
+	unsigned long offset;
+};
+
+#define suspend_extent_state_eof(state) ((state)->num_chains < (state)->current_chain)
+
+#define suspend_extent_for_each(extent_chain, extentpointer, value) \
+if ((extent_chain)->first) \
+	for ((extentpointer) = (extent_chain)->first, (value) = \
+			(extentpointer)->minimum; \
+	     ((extentpointer) && ((extentpointer)->next || (value) <= \
+				 (extentpointer)->maximum)); \
+	     (((value) == (extentpointer)->maximum) ? \
+		((extentpointer) = (extentpointer)->next, (value) = \
+		 ((extentpointer) ? (extentpointer)->minimum : 0)) : \
+			(value)++))
+
+extern int suspend_extents_allocated;
+void suspend_put_extent_chain(struct extent_chain *chain);
+int suspend_add_to_extent_chain(struct extent_chain *chain, 
+		unsigned long minimum, unsigned long maximum);
+int suspend_serialise_extent_chain(struct suspend_module_ops *owner,
+		struct extent_chain *chain);
+int suspend_load_extent_chain(struct extent_chain *chain);
+
+/* swap_entry_to_extent_val & extent_val_to_swap_entry: 
+ * We are putting offset in the low bits so consecutive swap entries
+ * make consecutive extent values */
+#define swap_entry_to_extent_val(swp_entry) (swp_entry.val)
+#define extent_val_to_swap_entry(val) (swp_entry_t) { (val) }
+
+void suspend_extent_state_save(struct extent_iterate_state *state,
+		struct extent_iterate_saved_state *saved_state);
+void suspend_extent_state_restore(struct extent_iterate_state *state,
+		struct extent_iterate_saved_state *saved_state);
+void suspend_extent_state_goto_start(struct extent_iterate_state *state);
+unsigned long suspend_extent_state_next(struct extent_iterate_state *state);
+#endif
diff -urN oldtree/kernel/power/io.c newtree/kernel/power/io.c
--- oldtree/kernel/power/io.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/io.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,1014 @@
+/*
+ * kernel/power/io.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-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * It contains high level IO routines for suspending.
+ *
+ */
+
+#include <linux/suspend.h>
+#include <linux/version.h>
+#include <linux/utsname.h>
+#include <linux/mount.h>
+#include <linux/suspend2.h>
+#include <linux/highmem.h>
+#include <asm/tlbflush.h>
+
+#include "suspend.h"
+#include "modules.h"
+#include "pageflags.h"
+#include "io.h"
+#include "ui.h"
+#include "storage.h"
+#include "prepare_image.h"
+#include "extent.h"
+
+/* suspend_attempt_to_parse_resume_device
+ *
+ * Can we suspend, using the current resume2= parameter?
+ */
+int suspend_attempt_to_parse_resume_device(void)
+{
+	struct list_head *writer;
+	struct suspend_module_ops *this_writer;
+	int result, returning = 0;
+
+	if (suspend_activate_storage(0))
+		return 0;
+
+	suspend_active_writer = NULL;
+	clear_suspend_state(SUSPEND_RESUME_DEVICE_OK);
+	clear_suspend_state(SUSPEND_CAN_RESUME);
+	clear_result_state(SUSPEND_ABORTED);
+
+	if (!suspend_num_writers) {
+		printk(name_suspend "No writers have been registered. Suspending will be disabled.\n");
+		goto cleanup;
+	}
+	
+	if (!resume2_file[0]) {
+		printk(name_suspend "Resume2 parameter is empty. Suspending will be disabled.\n");
+		goto cleanup;
+	}
+
+	list_for_each(writer, &suspend_writers) {
+		this_writer = list_entry(writer, struct suspend_module_ops, type_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->enabled)
+			continue;
+
+		result = this_writer->parse_sig_location(
+				resume2_file, (suspend_num_writers == 1));
+
+		switch (result) {
+			case -EINVAL:
+				/* 
+				 * For this writer, but not a valid 
+				 * configuration. Error already printed.
+				 */
+
+				goto cleanup;
+
+			case 0:
+				/*
+				 * For this writer and valid.
+				 */
+
+				suspend_active_writer = this_writer;
+
+				set_suspend_state(SUSPEND_RESUME_DEVICE_OK);
+				set_suspend_state(SUSPEND_CAN_RESUME);
+				printk(name_suspend "Resuming enabled.\n");
+
+				returning = 1;
+				goto cleanup;
+		}
+	}
+	printk(name_suspend "No matching enabled writer found. Resuming disabled.\n");
+cleanup:
+	suspend_deactivate_storage(0);
+	return returning;
+}
+
+void attempt_to_parse_resume_device2(void)
+{
+	suspend_prepare_usm();
+	suspend_attempt_to_parse_resume_device();
+	suspend_cleanup_usm();
+}
+
+/* noresume_reset_modules
+ *
+ * Description:	When we read the start of an image, modules (and especially the
+ * 		active writer) might need to reset data structures if we decide
+ * 		to invalidate the image rather than resuming from it.
+ */
+
+static void noresume_reset_modules(void)
+{
+	struct suspend_module_ops *this_filter;
+	
+	list_for_each_entry(this_filter, &suspend_filters, type_list) {
+		if (this_filter->noresume_reset)
+			this_filter->noresume_reset();
+	}
+
+	if (suspend_active_writer && suspend_active_writer->noresume_reset)
+		suspend_active_writer->noresume_reset();
+}
+
+/* fill_suspend_header()
+ * 
+ * Description:	Fill the suspend header structure.
+ * Arguments:	struct suspend_header: Header data structure to be filled.
+ */
+
+static void fill_suspend_header(struct suspend_header *sh)
+{
+	int i;
+	
+	memset((char *)sh, 0, sizeof(*sh));
+
+	sh->version_code = LINUX_VERSION_CODE;
+	sh->num_physpages = num_physpages;
+	sh->orig_mem_free = suspend_orig_mem_free;
+	memcpy(&sh->uts, init_utsname(), sizeof(struct new_utsname));
+	sh->page_size = PAGE_SIZE;
+	sh->pagedir = pagedir1;
+	sh->pageset_2_size = pagedir2.pageset_size;
+	sh->param0 = suspend_result;
+	sh->param1 = suspend_action;
+	sh->param2 = suspend_debug_state;
+	sh->param3 = console_loglevel;
+	sh->root_fs = current->fs->rootmnt->mnt_sb->s_dev;
+	for (i = 0; i < 4; i++)
+		sh->io_time[i/2][i%2] =
+		       suspend_io_time[i/2][i%2];
+}
+
+/*
+ * rw_init_modules
+ *
+ * Iterate over modules, preparing the ones that will be used to read or write
+ * data.
+ */
+static int rw_init_modules(int rw, int which)
+{
+	struct suspend_module_ops *this_module;
+	/* Initialise page transformers */
+	list_for_each_entry(this_module, &suspend_filters, type_list) {
+		if (!this_module->enabled)
+			continue;
+		if (this_module->rw_init &&
+		     	this_module->rw_init(rw, which)) {
+			abort_suspend("Failed to initialise the %s filter.",
+				this_module->name);
+				return 1;
+		}
+	}
+
+	/* Initialise writer */
+	if (suspend_active_writer->rw_init(rw, which)) {
+		abort_suspend("Failed to initialise the writer."); 
+		if (!rw)
+			suspend_active_writer->invalidate_image();
+		return 1;
+	}
+
+	/* Initialise other modules */
+	list_for_each_entry(this_module, &suspend_modules, module_list) {
+		if (!this_module->enabled)
+			continue;
+		if ((this_module->type == FILTER_MODULE) ||
+		    (this_module->type == WRITER_MODULE))
+			continue;
+		if (this_module->rw_init && this_module->rw_init(rw, which)) {
+				set_result_state(SUSPEND_ABORTED);
+				return 1;
+			}
+	}
+
+	return 0;
+}
+
+/*
+ * rw_cleanup_modules
+ *
+ * Cleanup components after reading or writing a set of pages.
+ * Only the writer may fail.
+ */
+static int rw_cleanup_modules(int rw)
+{
+	struct suspend_module_ops *this_module;
+	int result = 0;
+
+	/* Cleanup other modules */
+	list_for_each_entry(this_module, &suspend_modules, module_list) {
+		if (!this_module->enabled)
+			continue;
+		if ((this_module->type == FILTER_MODULE) ||
+		    (this_module->type == WRITER_MODULE))
+			continue;
+		if (this_module->rw_cleanup)
+			result |= this_module->rw_cleanup(rw);
+	}
+
+	/* Flush data and cleanup */
+	list_for_each_entry(this_module, &suspend_filters, type_list) {
+		if (!this_module->enabled)
+			continue;
+		if (this_module->rw_cleanup)
+			result |= this_module->rw_cleanup(rw);
+	}
+
+	result |= suspend_active_writer->rw_cleanup(rw);
+
+	return result;
+}
+
+/*
+ * do_rw_loop
+ *
+ * The main I/O loop for reading or writing pages.
+ */
+static int do_rw_loop(int write, int finish_at, dyn_pageflags_t *pageflags,
+		int base, int barmax)
+{
+	int pfn, pc, step = 1, nextupdate = 0, i;
+	int result;
+	struct suspend_module_ops *first_filter = suspend_get_next_filter(NULL);
+	char *buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+
+	pfn = get_next_bit_on(*pageflags, max_pfn);
+
+	pc = finish_at / 5;
+
+	/* Read the pages */
+	for (i=0; i< finish_at; i++) {
+		struct page *page = pfn_to_page(pfn);
+
+		/* Status */
+		if ((i+base) >= nextupdate)
+			nextupdate = suspend_update_status(i+base, barmax,
+				" %d/%d MB ", MB(base+i+1), MB(barmax));
+
+		if ((i + 1) == pc) {
+			printk("%d%%...", 20 * step);
+			step++;
+			pc = finish_at * step / 5;
+		}
+		
+		if (write)
+			result = first_filter->write_chunk(page);
+		else
+			result = first_filter->read_chunk(page, SUSPEND_ASYNC);
+
+		if (result) {
+			if (write) {
+				printk("Write chunk returned %d.\n", result);
+				abort_suspend("Failed to write a chunk of the "
+					"image.");
+				free_page((unsigned long) buffer);
+				return result;
+			} else
+				panic("Failed to read chunk %d/%d of the image. (%d)",
+					i, finish_at, result);
+		}
+
+		/* Interactivity*/
+		suspend_cond_pause(0, NULL);
+
+		if (test_result_state(SUSPEND_ABORTED) && write) {
+			free_page((unsigned long) buffer);
+			return 1;
+		}
+
+		/* Prepare next */
+		pfn = get_next_bit_on(*pageflags, pfn);
+	}
+
+	printk("done.\n");
+
+	suspend_update_status(base + finish_at, barmax, " %d/%d MB ",
+			MB(base + finish_at), MB(barmax));
+	free_page((unsigned long) buffer);
+	return 0;
+}
+
+/* write_pageset()
+ *
+ * Description:	Write a pageset to disk.
+ * Arguments:	pagedir:	Pointer to the pagedir to be saved.
+ * 		whichtowrite:	Controls what debugging output is printed.
+ * Returns:	Zero on success or -1 on failure.
+ */
+
+int write_pageset(struct pagedir *pagedir, int whichtowrite)
+{
+	int finish_at, base = 0, start_time, end_time;
+	int barmax = pagedir1.pageset_size + pagedir2.pageset_size;
+	long error = 0;
+	dyn_pageflags_t *pageflags;
+
+	/* 
+	 * Even if there is nothing to read or write, the writer
+	 * may need the init/cleanup for it's housekeeping.  (eg:
+	 * Pageset1 may start where pageset2 ends when writing).
+	 */
+	finish_at = pagedir->pageset_size;
+
+	if (whichtowrite == 1) {
+		suspend_prepare_status(DONT_CLEAR_BAR,
+				"Writing kernel & process data...");
+		base = pagedir2.pageset_size;
+		if (test_action_state(SUSPEND_TEST_FILTER_SPEED) ||
+		    test_action_state(SUSPEND_TEST_BIO))
+			pageflags = &pageset1_map;
+		else
+			pageflags = &pageset1_copy_map;
+	} else {
+		suspend_prepare_status(CLEAR_BAR, "Writing caches...");
+		pageflags = &pageset2_map;
+		bytes_in = bytes_out = 0;
+	}	
+	
+	start_time = jiffies;
+
+	if (!rw_init_modules(1, whichtowrite))
+		error = do_rw_loop(1, finish_at, pageflags, base, barmax);
+
+	if (rw_cleanup_modules(WRITE)) {
+		abort_suspend("Failed to cleanup after writing.");
+		error = 1;
+	}
+
+	/* Statistics */
+	end_time = jiffies;
+	
+	if ((end_time - start_time) && (!test_result_state(SUSPEND_ABORTED))) {
+		suspend_io_time[0][0] += finish_at,
+		suspend_io_time[0][1] += (end_time - start_time);
+	}
+
+	return error;
+}
+
+/* read_pageset()
+ *
+ * Description:	Read a pageset from disk.
+ * Arguments:	pagedir:	Pointer to the pagedir to be saved.
+ * 		whichtowrite:	Controls what debugging output is printed.
+ * 		overwrittenpagesonly: Whether to read the whole pageset or
+ * 		only part.
+ * Returns:	Zero on success or -1 on failure.
+ */
+
+static int read_pageset(struct pagedir *pagedir, int whichtoread,
+		int overwrittenpagesonly)
+{
+	int result = 0, base = 0, start_time, end_time;
+	int finish_at = pagedir->pageset_size;
+	int barmax = pagedir1.pageset_size + pagedir2.pageset_size;
+	dyn_pageflags_t *pageflags;
+
+	if (whichtoread == 1) {
+		suspend_prepare_status(CLEAR_BAR,
+				"Reading kernel & process data...");
+		pageflags = &pageset1_copy_map;
+	} else {
+		suspend_prepare_status(DONT_CLEAR_BAR, "Reading caches...");
+		if (overwrittenpagesonly)
+			barmax = finish_at = min(pagedir1.pageset_size, 
+						 pagedir2.pageset_size);
+		else {
+			base = pagedir1.pageset_size;
+		}
+		pageflags = &pageset2_map;
+	}	
+	
+	start_time = jiffies;
+
+	if (rw_init_modules(0, whichtoread)) {
+		suspend_active_writer->invalidate_image();
+		result = 1;
+	} else
+		result = do_rw_loop(0, finish_at, pageflags, base, barmax);
+
+	if (rw_cleanup_modules(READ)) {
+		abort_suspend("Failed to cleanup after reading.");
+		result = 1;
+	}
+
+	/* Statistics */
+	end_time=jiffies;
+
+	if ((end_time - start_time) && (!test_result_state(SUSPEND_ABORTED))) {
+		suspend_io_time[1][0] += finish_at,
+		suspend_io_time[1][1] += (end_time - start_time);
+	}
+
+	return result;
+}
+
+/* write_module_configs()
+ *
+ * Description:	Store the configuration for each module in the image header.
+ * Returns:	Int: Zero on success, Error value otherwise.
+ */
+static int write_module_configs(void)
+{
+	struct suspend_module_ops *this_module;
+	char *buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	int len, index = 1;
+	struct suspend_module_header suspend_module_header;
+
+	if (!buffer) {
+		printk("Failed to allocate a buffer for saving "
+				"module configuration info.\n");
+		return -ENOMEM;
+	}
+		
+	/* 
+	 * We have to know which data goes with which module, so we at
+	 * least write a length of zero for a module. Note that we are
+	 * also assuming every module's config data takes <= PAGE_SIZE.
+	 */
+
+	/* For each module (in registration order) */
+	list_for_each_entry(this_module, &suspend_modules, module_list) {
+		if (!this_module->enabled || !this_module->storage_needed ||
+		    (this_module->type == WRITER_MODULE &&
+		     suspend_active_writer != this_module))
+			continue;
+
+		/* Get the data from the module */
+		len = 0;
+		if (this_module->save_config_info)
+			len = this_module->save_config_info(buffer);
+
+		/* Save the details of the module */
+		suspend_module_header.enabled = this_module->enabled;
+		suspend_module_header.type = this_module->type;
+		suspend_module_header.index = index++;
+		strncpy(suspend_module_header.name, this_module->name, 
+					sizeof(suspend_module_header.name));
+		suspend_active_writer->rw_header_chunk(WRITE,
+				this_module,
+				(char *) &suspend_module_header,
+				sizeof(suspend_module_header));
+
+		/* Save the size of the data and any data returned */
+		suspend_active_writer->rw_header_chunk(WRITE,
+				this_module,
+				(char *) &len, sizeof(int));
+		if (len)
+			suspend_active_writer->rw_header_chunk(
+				WRITE, this_module, buffer, len);
+	}
+
+	/* Write a blank header to terminate the list */
+	suspend_module_header.name[0] = '\0';
+	suspend_active_writer->rw_header_chunk(WRITE, 
+			NULL,
+			(char *) &suspend_module_header,
+			sizeof(suspend_module_header));
+
+	free_page((unsigned long) buffer);
+	return 0;
+}
+
+/* read_module_configs()
+ *
+ * Description:	Reload module configurations from the image header.
+ * Returns:	Int. Zero on success, error value otherwise.
+ */
+
+static int read_module_configs(void)
+{
+	struct suspend_module_ops *this_module;
+	char *buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	int len, result = 0;
+	struct suspend_module_header suspend_module_header;
+
+	if (!buffer) {
+		printk("Failed to allocate a buffer for reloading module "
+				"configuration info.\n");
+		return -ENOMEM;
+	}
+		
+	/* All modules are initially disabled. That way, if we have a module
+	 * loaded now that wasn't loaded when we suspended, it won't be used
+	 * in trying to read the data.
+	 */
+	list_for_each_entry(this_module, &suspend_modules, module_list)
+		this_module->enabled = 0;
+	
+	/* Get the first module header */
+	result = suspend_active_writer->rw_header_chunk(READ, NULL,
+			(char *) &suspend_module_header, sizeof(suspend_module_header));
+	if (!result) {
+		printk("Failed to read the next module header.\n");
+		free_page((unsigned long) buffer);
+		return -EINVAL;
+	}
+
+	/* For each module (in registration order) */
+	while (suspend_module_header.name[0]) {
+
+		/* Find the module */
+		this_module = suspend_find_module_given_name(suspend_module_header.name);
+
+		if (!this_module) {
+			/* 
+			 * Is it used? Only need to worry about filters. The active
+			 * writer must be loaded!
+			 */
+			if (suspend_module_header.enabled) {
+				suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ,
+					"It looks like we need module %s for "
+					"reading the image but it hasn't been "
+					"registered.\n",
+					suspend_module_header.name);
+				if (!(test_suspend_state(SUSPEND_CONTINUE_REQ))) {
+					suspend_active_writer->invalidate_image();
+					free_page((unsigned long) buffer);
+					return -EINVAL;
+				}
+			} else
+				printk("Module %s configuration data found, but the module "
+					"hasn't registered. Looks like it was disabled, so "
+					"we're ignoring it's data.",
+					suspend_module_header.name);
+		}
+		
+		/* Get the length of the data (if any) */
+		result = suspend_active_writer->rw_header_chunk(READ, NULL,
+				(char *) &len, sizeof(int));
+		if (!result) {
+			printk("Failed to read the length of the module %s's"
+					" configuration data.\n",
+					suspend_module_header.name);
+			free_page((unsigned long) buffer);
+			return -EINVAL;
+		}
+
+		/* Read any data and pass to the module (if we found one) */
+		if (len) {
+			suspend_active_writer->rw_header_chunk(READ, NULL,
+					buffer, len);
+			if (this_module) {
+				if (!this_module->save_config_info) {
+					printk("Huh? Module %s appears to have "
+						"a save_config_info, but not a "
+						"load_config_info function!\n",
+						this_module->name);
+				} else
+					this_module->load_config_info(buffer, len);
+			}
+		}
+
+		if (this_module) {
+			/* Now move this module to the tail of its lists. This
+			 * will put it in order. Any new modules will end up at
+			 * the top of the lists. They should have been set to
+			 * disabled when loaded (people will normally not edit
+			 * an initrd to load a new module and then suspend
+			 * without using it!).
+			 */
+
+			suspend_move_module_tail(this_module);
+
+			/* 
+			 * We apply the disabled state; modules don't need to
+			 * save whether they were disabled and if they do, we
+			 * override them anyway.
+			 */
+			this_module->enabled = suspend_module_header.enabled;
+		}
+
+		/* Get the next module header */
+		result = suspend_active_writer->rw_header_chunk(READ, NULL,
+				(char *) &suspend_module_header,
+				sizeof(suspend_module_header));
+
+		if (!result) {
+			printk("Failed to read the next module header.\n");
+			free_page((unsigned long) buffer);
+			return -EINVAL;
+		}
+
+	}
+
+	free_page((unsigned long) buffer);
+	return 0;
+}
+
+/* write_image_header()
+ *
+ * Description:	Write the image header after write the image proper.
+ * Returns:	Int. Zero on success or -1 on failure.
+ */
+
+int write_image_header(void)
+{
+	int ret;
+	int total = pagedir1.pageset_size + pagedir2.pageset_size+2;
+	char *header_buffer = NULL;
+
+	/* Now prepare to write the header */
+	if ((ret = suspend_active_writer->write_header_init())) {
+		abort_suspend("Active writer's write_header_init"
+				" function failed.");
+		goto write_image_header_abort;
+	}
+
+	/* Get a buffer */
+	header_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	if (!header_buffer) {
+		abort_suspend("Out of memory when trying to get page "
+				"for header!");
+		goto write_image_header_abort;
+	}
+
+	/* Write suspend header */
+	fill_suspend_header((struct suspend_header *) header_buffer);
+	suspend_active_writer->rw_header_chunk(WRITE, NULL,
+			header_buffer, sizeof(struct suspend_header));
+
+	free_page((unsigned long) header_buffer);
+
+	/* Write module configurations */
+	if ((ret = write_module_configs())) {
+		abort_suspend("Failed to write module configs.");
+		goto write_image_header_abort;
+	}
+
+	save_dyn_pageflags(pageset1_map);
+
+	/* Flush data and let writer cleanup */
+	if (suspend_active_writer->write_header_cleanup()) {
+		abort_suspend("Failed to cleanup writing header.");
+		goto write_image_header_abort_no_cleanup;
+	}
+
+	if (test_result_state(SUSPEND_ABORTED))
+		goto write_image_header_abort_no_cleanup;
+
+	suspend_message(SUSPEND_IO, SUSPEND_VERBOSE, 1, "|\n");
+	suspend_update_status(total, total, NULL);
+
+	return 0;
+
+write_image_header_abort:
+	suspend_active_writer->write_header_cleanup();
+write_image_header_abort_no_cleanup:
+	return -1;
+}
+
+/* sanity_check()
+ *
+ * Description:	Perform a few checks, seeking to ensure that the kernel being
+ * 		booted matches the one suspended. They need to match so we can
+ * 		be _sure_ things will work. It is not absolutely impossible for
+ * 		resuming from a different kernel to work, just not assured.
+ * Arguments:	Struct suspend_header. The header which was saved at suspend
+ * 		time.
+ */
+static char *sanity_check(struct suspend_header *sh)
+{
+	if (sh->version_code != LINUX_VERSION_CODE)
+		return "Incorrect kernel version.";
+	
+	if (sh->num_physpages != num_physpages)
+		return "Incorrect memory size.";
+
+	if (strncmp(sh->uts.sysname, init_utsname()->sysname, 65))
+		return "Incorrect system type.";
+
+	if (strncmp(sh->uts.release, init_utsname()->release, 65))
+		return "Incorrect release.";
+
+	if (strncmp(sh->uts.version, init_utsname()->version, 65))
+		return "Right kernel version but wrong build number.";
+
+	if (strncmp(sh->uts.machine, init_utsname()->machine, 65))
+		return "Incorrect machine type.";
+
+	if (sh->page_size != PAGE_SIZE)
+		return "Incorrect PAGE_SIZE.";
+
+	if (!test_action_state(SUSPEND_IGNORE_ROOTFS)) {
+		const struct super_block *sb;
+		list_for_each_entry(sb, &super_blocks, s_list) {
+			if ((!(sb->s_flags & MS_RDONLY)) &&
+			    (sb->s_type->fs_flags & FS_REQUIRES_DEV))
+				return "Device backed fs has been mounted "
+					"rw prior to resume."; 
+		}
+	}
+
+	return 0;
+}
+
+/* __read_pageset1
+ *
+ * Description:	Test for the existence of an image and attempt to load it.
+ * Returns:	Int. Zero if image found and pageset1 successfully loaded.
+ * 		Error if no image found or loaded.
+ */
+static int __read_pageset1(void)
+{			
+	int i, result = 0;
+	char *header_buffer = (char *) get_zeroed_page(GFP_ATOMIC),
+	     *sanity_error = NULL;
+	struct suspend_header *suspend_header;
+
+	if (!header_buffer) {
+		printk("Unable to allocate a page for reading the signature.\n");
+		return -ENOMEM;
+	}
+	
+	/* Check for an image */
+	if (!(result = suspend_active_writer->image_exists())) {
+		result = -ENODATA;
+		noresume_reset_modules();
+		printk(name_suspend "No image found.\n");
+		goto out;
+	}
+
+	/* Check for noresume command line option */
+	if (test_suspend_state(SUSPEND_NORESUME_SPECIFIED)) {
+		suspend_active_writer->invalidate_image();
+		result = -EINVAL;
+		noresume_reset_modules();
+		printk(name_suspend "Noresume: Invalidated image.\n");
+		goto out;
+	}
+
+	/* Check whether we've resumed before */
+	if (test_suspend_state(SUSPEND_RESUMED_BEFORE)) {
+		int resumed_before_default = 0;
+		if (test_suspend_state(SUSPEND_RETRY_RESUME))
+			resumed_before_default = SUSPEND_CONTINUE_REQ;
+		suspend_early_boot_message(1, resumed_before_default, NULL);
+		clear_suspend_state(SUSPEND_RETRY_RESUME);
+		if (!(test_suspend_state(SUSPEND_CONTINUE_REQ))) {
+			suspend_active_writer->invalidate_image();
+			result = -EINVAL;
+			noresume_reset_modules();
+			printk(name_suspend "Tried to resume before: Invalidated image.\n");
+			goto out;
+		}
+	}
+
+	clear_suspend_state(SUSPEND_CONTINUE_REQ);
+
+	/* 
+	 * Prepare the active writer for reading the image header. The
+	 * activate writer might read its own configuration.
+	 * 
+	 * NB: This call may never return because there might be a signature
+	 * for a different image such that we warn the user and they choose
+	 * to reboot. (If the device ids look erroneous (2.4 vs 2.6) or the
+	 * location of the image might be unavailable if it was stored on a
+	 * network connection.
+	 */
+
+	if ((result = suspend_active_writer->read_header_init())) {
+		noresume_reset_modules();
+		printk(name_suspend "Failed to initialise, reading the image header.\n");
+		goto out;
+	}
+	
+	/* Read suspend header */
+	if ((result = suspend_active_writer->rw_header_chunk(READ, NULL,
+			header_buffer, sizeof(struct suspend_header))) < 0) {
+		noresume_reset_modules();
+		printk(name_suspend "Failed to read the image signature.\n");
+		goto out;
+	}
+	
+	suspend_header = (struct suspend_header *) header_buffer;
+
+	/*
+	 * NB: This call may also result in a reboot rather than returning.
+	 */
+
+	if ((sanity_error = sanity_check(suspend_header)) &&
+	    suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ, sanity_error)) {
+		suspend_active_writer->invalidate_image();
+		result = -EINVAL;
+		noresume_reset_modules();
+		printk(name_suspend "Sanity check failed.\n");
+		goto out;
+	}
+
+	/*
+	 * We have an image and it looks like it will load okay.
+	 */
+
+	/* Get metadata from header. Don't override commandline parameters.
+	 *
+	 * We don't need to save the image size limit because it's not used
+	 * during resume and will be restored with the image anyway.
+	 */
+	
+	suspend_orig_mem_free = suspend_header->orig_mem_free;
+	memcpy((char *) &pagedir1,
+		(char *) &suspend_header->pagedir, sizeof(pagedir1));
+	suspend_result = suspend_header->param0;
+	suspend_action = suspend_header->param1;
+	suspend_debug_state = suspend_header->param2;
+	console_loglevel = suspend_header->param3;
+	clear_suspend_state(SUSPEND_IGNORE_LOGLEVEL);
+	pagedir2.pageset_size = suspend_header->pageset_2_size;
+	for (i = 0; i < 4; i++)
+		suspend_io_time[i/2][i%2] =
+			suspend_header->io_time[i/2][i%2];
+
+	/* Read module configurations */
+	if ((result = read_module_configs())) {
+		noresume_reset_modules();
+		pagedir1.pageset_size =
+			pagedir2.pageset_size = 0;
+		printk(name_suspend "Failed to read Suspend module configurations.\n");
+		goto out;
+	}
+
+	suspend_prepare_console();
+
+	suspend_cond_pause(1, "About to read original pageset1 locations.");
+
+	/*
+	 * Read original pageset1 locations. These are the addresses we can't
+	 * use for the data to be restored.
+	 */
+
+	allocate_dyn_pageflags(&pageset1_map);
+	load_dyn_pageflags(pageset1_map);
+
+	set_suspend_state(SUSPEND_NOW_RESUMING);
+
+	/* Relocate it so that it's not overwritten while we're using it to
+	 * copy the original contents back */
+	relocate_dyn_pageflags(&pageset1_map);
+	
+	allocate_dyn_pageflags(&pageset1_copy_map);
+	relocate_dyn_pageflags(&pageset1_copy_map);
+
+	/* Clean up after reading the header */
+	if ((result = suspend_active_writer->read_header_cleanup())) {
+		noresume_reset_modules();
+		printk(name_suspend "Failed to cleanup after reading the image header.\n");
+		goto out_reset_console;
+	}
+
+	suspend_cond_pause(1, "About to read pagedir.");
+
+	/* 
+	 * Get the addresses of pages into which we will load the kernel to
+	 * be copied back
+	 */
+	if (suspend_get_pageset1_load_addresses()) {
+		result = -ENOMEM;
+		noresume_reset_modules();
+		printk(name_suspend "Failed to get load addresses for pageset1.\n");
+		goto out_reset_console;
+	}
+
+	/* Read the original kernel back */
+	suspend_cond_pause(1, "About to read pageset 1.");
+
+	if (read_pageset(&pagedir1, 1, 0)) {
+		suspend_prepare_status(CLEAR_BAR, "Failed to read pageset 1.");
+		result = -EPERM;
+		noresume_reset_modules();
+		printk(name_suspend "Failed to get load pageset1.\n");
+		goto out_reset_console;
+	}
+
+	suspend_cond_pause(1, "About to restore original kernel.");
+	result = 0;
+
+	if (!test_action_state(SUSPEND_KEEP_IMAGE) &&
+	    suspend_active_writer->mark_resume_attempted)
+		suspend_active_writer->mark_resume_attempted();
+
+out:
+	free_page((unsigned long) header_buffer);
+	return result;
+
+out_reset_console:
+	free_dyn_pageflags(&pageset1_map);
+	free_dyn_pageflags(&pageset1_copy_map);
+	suspend_cleanup_console();
+	goto out;
+}
+
+/* read_pageset1()
+ *
+ * Description:	Attempt to read the header and pageset1 of a suspend image.
+ * 		Handle the outcome, complaining where appropriate.
+ */
+
+int read_pageset1(void)
+{
+	int error;
+
+	error = __read_pageset1();
+
+	switch (error) {
+		case 0:
+		case -ENODATA:
+		case -EINVAL:	/* non fatal error */
+			return error;
+		case -EIO:
+			printk(KERN_CRIT name_suspend "I/O error\n");
+			break;
+		case -ENOENT:
+			printk(KERN_CRIT name_suspend "No such file or directory\n");
+			break;
+		case -EPERM:
+			printk(KERN_CRIT name_suspend "Sanity check error\n");
+			break;
+		default:
+			printk(KERN_CRIT name_suspend "Error %d resuming\n",
+					error);
+			break;
+	}
+	abort_suspend("Error %d in read_pageset1",error);
+	return error;
+}
+
+/*
+ * get_have_image_data()
+ */
+char *get_have_image_data(void)
+{
+	char *output_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	struct suspend_header *suspend_header;
+
+	if (!output_buffer) {
+		printk("Output buffer null.\n");
+		return NULL;
+	}
+
+	/* Check for an image */
+	if (!suspend_active_writer->image_exists() ||
+	    suspend_active_writer->read_header_init() ||
+	    suspend_active_writer->rw_header_chunk(READ, NULL,
+			output_buffer, sizeof(struct suspend_header)) !=
+	    		sizeof(struct suspend_header)) {
+		sprintf(output_buffer, "0\n");
+		goto out;
+	}
+
+	suspend_header = (struct suspend_header *) output_buffer;
+
+	sprintf(output_buffer, "1\n%s\n%s\n",
+			suspend_header->uts.machine,
+			suspend_header->uts.version);
+
+	/* Check whether we've resumed before */
+	if (test_suspend_state(SUSPEND_RESUMED_BEFORE))
+		strcat(output_buffer, "Resumed before.\n");
+
+out:
+	noresume_reset_modules();
+	return output_buffer;
+}
+
+/* read_pageset2()
+ *
+ * Description:	Read in part or all of pageset2 of an image, depending upon
+ * 		whether we are suspending and have only overwritten a portion
+ * 		with pageset1 pages, or are resuming and need to read them 
+ * 		all.
+ * Arguments:	Int. Boolean. Read only pages which would have been
+ * 		overwritten by pageset1?
+ * Returns:	Int. Zero if no error, otherwise the error value.
+ */
+int read_pageset2(int overwrittenpagesonly)
+{
+	int result = 0;
+
+	if (!pagedir2.pageset_size)
+		return 0;
+
+	result = read_pageset(&pagedir2, 2, overwrittenpagesonly);
+
+	suspend_update_status(100, 100, NULL);
+	suspend_cond_pause(1, "Pagedir 2 read.");
+
+	return result;
+}
diff -urN oldtree/kernel/power/io.h newtree/kernel/power/io.h
--- oldtree/kernel/power/io.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/io.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,46 @@
+/*
+ * kernel/power/io.h
+ *
+ * Copyright (C) 2005-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * It contains high level IO routines for suspending.
+ *
+ */
+
+#include <linux/utsname.h>
+#include "pagedir.h"
+
+/* Non-module data saved in our image header */
+struct suspend_header {
+	u32 version_code;
+	unsigned long num_physpages;
+	unsigned long orig_mem_free;
+	struct new_utsname uts;
+	int num_cpus;
+	int page_size;
+	int pageset_2_size;
+	int param0;
+	int param1;
+	int param2;
+	int param3;
+	int progress0;
+	int progress1;
+	int progress2;
+	int progress3;
+	int io_time[2][2];
+	struct pagedir pagedir;
+	dev_t root_fs;
+};
+
+extern int write_pageset(struct pagedir *pagedir, int whichtowrite);
+extern int write_image_header(void);
+extern int read_pageset1(void);
+extern int read_pageset2(int overwrittenpagesonly);
+
+extern int suspend_attempt_to_parse_resume_device(void);
+extern void attempt_to_parse_resume_device2(void);
+extern dev_t name_to_dev_t(char *line);
+extern __nosavedata unsigned long bytes_in, bytes_out;
+extern char *get_have_image_data(void);
diff -urN oldtree/kernel/power/main.c newtree/kernel/power/main.c
--- oldtree/kernel/power/main.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/power/main.c	2006-10-06 17:28:20.000000000 -0400
@@ -9,6 +9,7 @@
  */
 
 #include <linux/suspend.h>
+#include <linux/freezer.h>
 #include <linux/kobject.h>
 #include <linux/string.h>
 #include <linux/delay.h>
@@ -95,7 +96,7 @@
 	if (pm_ops->finish)
 		pm_ops->finish(state);
  Thaw:
-	thaw_processes();
+	thaw_processes(FREEZER_ALL_THREADS);
  Enable_cpu:
 	enable_nonboot_cpus();
 	pm_restore_console();
@@ -134,7 +135,7 @@
 {
 	device_resume();
 	resume_console();
-	thaw_processes();
+	thaw_processes(FREEZER_ALL_THREADS);
 	enable_nonboot_cpus();
 	if (pm_ops && pm_ops->finish)
 		pm_ops->finish(state);
@@ -147,7 +148,7 @@
 static const char * const pm_states[PM_SUSPEND_MAX] = {
 	[PM_SUSPEND_STANDBY]	= "standby",
 	[PM_SUSPEND_MEM]	= "mem",
-#ifdef CONFIG_SOFTWARE_SUSPEND
+#if defined(CONFIG_SOFTWARE_SUSPEND) || defined(CONFIG_SUSPEND2)
 	[PM_SUSPEND_DISK]	= "disk",
 #endif
 };
diff -urN oldtree/kernel/power/modules.c newtree/kernel/power/modules.c
--- oldtree/kernel/power/modules.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/modules.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,333 @@
+/*
+ * kernel/power/modules.c
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ */
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include "suspend.h"
+#include "modules.h"
+#include "ui.h"
+
+struct list_head suspend_filters, suspend_writers, suspend_modules;
+struct suspend_module_ops *suspend_active_writer;
+static int suspend_num_filters;
+int suspend_num_writers, suspend_num_modules;
+       
+/*
+ * suspend_header_storage_for_modules
+ *
+ * Returns the amount of space needed to store configuration
+ * data needed by the modules prior to copying back the original
+ * kernel. We can exclude data for pageset2 because it will be
+ * available anyway once the kernel is copied back.
+ */
+unsigned long suspend_header_storage_for_modules(void)
+{
+	struct suspend_module_ops *this_module;
+	unsigned long bytes = 0;
+	
+	list_for_each_entry(this_module, &suspend_modules, module_list) {
+		if (!this_module->enabled ||
+		    (this_module->type == WRITER_MODULE &&
+		     suspend_active_writer != this_module))
+			continue;
+		if (this_module->storage_needed) {
+			int this = this_module->storage_needed() +
+				sizeof(struct suspend_module_header) +
+				sizeof(int);
+			this_module->header_requested = this;
+			bytes += this;
+		}
+	}
+
+	/* One more for the empty terminator */
+	return bytes + sizeof(struct suspend_module_header);
+}
+
+/*
+ * suspend_memory_for_modules
+ *
+ * Returns the amount of memory requested by modules for
+ * doing their work during the cycle.
+ */
+
+unsigned long suspend_memory_for_modules(void)
+{
+	unsigned long bytes = 0;
+	struct suspend_module_ops *this_module;
+
+	list_for_each_entry(this_module, &suspend_modules, module_list) {
+		if (!this_module->enabled)
+			continue;
+		if (this_module->memory_needed)
+			bytes += this_module->memory_needed();
+	}
+
+	return ((bytes + PAGE_SIZE - 1) >> PAGE_SHIFT);
+}
+
+/* suspend_find_module_given_name
+ * Functionality :	Return a module (if found), given a pointer
+ * 			to its name
+ */
+
+struct suspend_module_ops *suspend_find_module_given_name(char *name)
+{
+	struct suspend_module_ops *this_module, *found_module = NULL;
+	
+	list_for_each_entry(this_module, &suspend_modules, module_list) {
+		if (!strcmp(name, this_module->name)) {
+			found_module = this_module;
+			break;
+		}			
+	}
+
+	return found_module;
+}
+
+/*
+ * suspend_print_module_debug_info
+ * Functionality   : Get debugging info from modules into a buffer.
+ */
+int suspend_print_module_debug_info(char *buffer, int buffer_size)
+{
+	struct suspend_module_ops *this_module;
+	int len = 0;
+
+	list_for_each_entry(this_module, &suspend_modules, module_list) {
+		if (!this_module->enabled)
+			continue;
+		if (this_module->print_debug_info) {
+			int result;
+			result = this_module->print_debug_info(buffer + len, 
+					buffer_size - len);
+			len += result;
+		}
+	}
+
+	/* Ensure null terminated */
+	buffer[buffer_size] = 0;
+
+	return len;
+}
+
+/*
+ * suspend_register_module
+ *
+ * Register a module.
+ */
+int suspend_register_module(struct suspend_module_ops *module)
+{
+	module->enabled = 1;
+	
+	if (suspend_find_module_given_name(module->name)) {
+		printk(name_suspend "Trying to load module %s,"
+				" which is already registered.\n",
+				module->name);
+		return -EBUSY;
+	}
+
+	switch (module->type) {
+		case FILTER_MODULE:
+			list_add_tail(&module->type_list,
+					&suspend_filters);
+			suspend_num_filters++;
+			break;
+
+		case WRITER_MODULE:
+			list_add_tail(&module->type_list,
+					&suspend_writers);
+			suspend_num_writers++;
+			break;
+
+		case MISC_MODULE:
+			break;
+
+		default:
+			printk("Hmmm. Module '%s' has an invalid type."
+				" It has been ignored.\n", module->name);
+			return -EINVAL;
+	}
+	list_add_tail(&module->module_list, &suspend_modules);
+	suspend_num_modules++;
+
+	return 0;	
+}
+
+/*
+ * suspend_unregister_module
+ *
+ * Remove a module.
+ */
+void suspend_unregister_module(struct suspend_module_ops *module)
+{
+	switch (module->type) {
+		case FILTER_MODULE:
+			list_del(&module->type_list);
+			suspend_num_filters--;
+			break;
+
+		case WRITER_MODULE:
+			list_del(&module->type_list);
+			suspend_num_writers--;
+			if (suspend_active_writer == module) {
+				suspend_active_writer = NULL;
+				clear_suspend_state(SUSPEND_CAN_RESUME);
+				clear_suspend_state(SUSPEND_CAN_SUSPEND);
+			}
+			break;
+		
+		case MISC_MODULE:
+			break;
+
+		default:
+			printk("Hmmm. Module '%s' has an invalid type."
+				" It has been ignored.\n", module->name);
+			return;
+	}
+	list_del(&module->module_list);
+	suspend_num_modules--;
+}
+
+/*
+ * suspend_move_module_tail
+ *
+ * Rearrange modules when reloading the config.
+ */
+void suspend_move_module_tail(struct suspend_module_ops *module)
+{
+	switch (module->type) {
+		case FILTER_MODULE:
+			if (suspend_num_filters > 1)
+				list_move_tail(&module->type_list,
+						&suspend_filters);
+			break;
+
+		case WRITER_MODULE:
+			if (suspend_num_writers > 1)
+				list_move_tail(&module->type_list,
+						&suspend_writers);
+			break;
+		
+		case MISC_MODULE:
+			break;
+		default:
+			printk("Hmmm. Module '%s' has an invalid type."
+				" It has been ignored.\n", module->name);
+			return;
+	}
+	if ((suspend_num_filters + suspend_num_writers) > 1)
+		list_move_tail(&module->module_list, &suspend_modules);
+}
+
+/*
+ * suspend_initialise_modules
+ *
+ * Get ready to do some work!
+ */
+int suspend_initialise_modules(int starting_cycle)
+{
+	struct suspend_module_ops *this_module;
+	int result;
+	
+	list_for_each_entry(this_module, &suspend_modules, module_list) {
+		this_module->header_requested = 0;
+		this_module->header_used = 0;
+		if (!this_module->enabled)
+			continue;
+		if (this_module->initialise) {
+			suspend_message(SUSPEND_MEMORY, SUSPEND_MEDIUM, 1,
+				"Initialising module %s.\n",
+				this_module->name);
+			if ((result = this_module->initialise(starting_cycle))) {
+				printk("%s didn't initialise okay.\n",
+						this_module->name);
+				return result;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* 
+ * suspend_cleanup_modules
+ *
+ * Tell modules the work is done.
+ */
+void suspend_cleanup_modules(int finishing_cycle)
+{
+	struct suspend_module_ops *this_module;
+	
+	list_for_each_entry(this_module, &suspend_modules, module_list) {
+		if (!this_module->enabled)
+			continue;
+		if (this_module->cleanup) {
+			suspend_message(SUSPEND_MEMORY, SUSPEND_MEDIUM, 1,
+				"Cleaning up module %s.\n",
+				this_module->name);
+			this_module->cleanup(finishing_cycle);
+		}
+	}
+}
+
+/*
+ * suspend_get_next_filter
+ *
+ * Get the next filter in the pipeline.
+ */
+struct suspend_module_ops *suspend_get_next_filter(struct suspend_module_ops *filter_sought)
+{
+	struct suspend_module_ops *last_filter = NULL, *this_filter = NULL;
+
+	list_for_each_entry(this_filter, &suspend_filters, type_list) {
+		if (!this_filter->enabled)
+			continue;
+		if ((last_filter == filter_sought) || (!filter_sought))
+			return this_filter;
+		last_filter = this_filter;
+	}
+
+	return suspend_active_writer;
+}
+
+/* suspend_get_modules
+ * 
+ * Take a reference to modules so they can't go away under us.
+ */
+
+int suspend_get_modules(void)
+{
+	struct suspend_module_ops *this_module;
+	
+	list_for_each_entry(this_module, &suspend_modules, module_list) {
+		if (!try_module_get(this_module->module)) {
+			/* Failed! Reverse gets and return error */
+			struct suspend_module_ops *this_module2;
+			list_for_each_entry(this_module2, &suspend_modules, module_list) {
+				if (this_module == this_module2)
+					return -EINVAL;
+				module_put(this_module2->module);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* suspend_put_modules
+ *
+ * Release our references to modules we used.
+ */
+
+void suspend_put_modules(void)
+{
+	struct suspend_module_ops *this_module;
+	
+	list_for_each_entry(this_module, &suspend_modules, module_list)
+		module_put(this_module->module);
+}
+
diff -urN oldtree/kernel/power/modules.h newtree/kernel/power/modules.h
--- oldtree/kernel/power/modules.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/modules.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,160 @@
+/*
+ * kernel/power/modules.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * It contains declarations for modules. Modules are additions to
+ * suspend2 that provide facilities such as image compression or
+ * encryption, backends for storage of the image and user interfaces.
+ *
+ */
+
+#ifndef SUSPEND_MODULES_H
+#define SUSPEND_MODULES_H
+
+/* This is the maximum size we store in the image header for a module name */
+#define SUSPEND_MAX_MODULE_NAME_LENGTH 30
+
+/* Per-module metadata */
+struct suspend_module_header {
+	char name[SUSPEND_MAX_MODULE_NAME_LENGTH];
+	int enabled;
+	int type;
+	int index;
+	int data_length;
+	unsigned long signature;
+};
+
+extern int suspend_num_modules, suspend_num_writers;
+
+enum {
+	FILTER_MODULE,
+	WRITER_MODULE,
+	MISC_MODULE /* Block writer, eg. */
+};
+
+enum {
+	SUSPEND_ASYNC,
+	SUSPEND_SYNC
+};
+
+struct suspend_module_ops {
+	/* Functions common to all modules */
+	int type;
+	char *name;
+	struct module *module;
+	int enabled;
+	struct list_head module_list;
+
+	/* List of filters or writers */
+	struct list_head list, type_list;
+
+	/*
+	 * Requirements for memory and storage in
+	 * the image header..
+	 */
+	unsigned long (*memory_needed) (void);
+	unsigned long (*storage_needed) (void);
+
+	unsigned long header_requested, header_used;
+	
+	/* 
+	 * Debug info
+	 */
+	int (*print_debug_info) (char *buffer, int size);
+	int (*save_config_info) (char *buffer);
+	void (*load_config_info) (char *buffer, int len);
+
+	/* 
+	 * Initialise & cleanup - general routines called
+	 * at the start and end of a cycle.
+	 */
+	int (*initialise) (int starting_cycle);
+	void (*cleanup) (int finishing_cycle);
+
+	/* 
+	 * Calls for allocating storage (writers only).
+	 *
+	 * Header space is allocated separately. Note that allocation
+	 * of space for the header might result in allocated space 
+	 * being stolen from the main pool if there is no unallocated
+	 * space. We have to be able to allocate enough space for
+	 * the header. We can eat memory to ensure there is enough
+	 * for the main pool.
+	 */
+
+	int (*storage_available) (void);
+	int (*allocate_header_space) (int space_requested);
+	int (*allocate_storage) (int space_requested);
+	int (*storage_allocated) (void);
+	int (*release_storage) (void);
+	
+	/*
+	 * Routines used in image I/O.
+	 */
+	int (*rw_init) (int rw, int stream_number);
+	int (*rw_cleanup) (int rw);
+	int (*write_chunk) (struct page *buffer_page);
+	int (*read_chunk) (struct page *buffer_page, int sync);
+
+	/* Reset module if image exists but reading aborted */
+	void (*noresume_reset) (void);
+
+	/* Read and write the metadata */	
+	int (*write_header_init) (void);
+	int (*write_header_cleanup) (void);
+
+	int (*read_header_init) (void);
+	int (*read_header_cleanup) (void);
+
+	int (*rw_header_chunk) (int rw, struct suspend_module_ops *owner,
+			char *buffer_start, int buffer_size);
+	
+	/* Attempt to parse an image location */
+	int (*parse_sig_location) (char *buffer, int only_writer);
+
+	/* Determine whether image exists that we can restore */
+	int (*image_exists) (void);
+	
+	/* Mark the image as having tried to resume */
+	void (*mark_resume_attempted) (void);
+
+	/* Destroy image if one exists */
+	int (*invalidate_image) (void);
+	
+};
+
+extern struct suspend_module_ops *suspend_active_writer;
+extern struct list_head suspend_filters, suspend_writers, suspend_modules;
+
+extern void suspend_prepare_console_modules(void);
+extern void suspend_cleanup_console_modules(void);
+
+extern struct suspend_module_ops *suspend_find_module_given_name(char *name),
+	*suspend_get_next_filter(struct suspend_module_ops *);
+
+extern int suspend_register_module(struct suspend_module_ops *module);
+extern void suspend_move_module_tail(struct suspend_module_ops *module);
+
+extern unsigned long suspend_header_storage_for_modules(void);
+extern unsigned long suspend_memory_for_modules(void);
+
+extern int suspend_print_module_debug_info(char *buffer, int buffer_size);
+extern int suspend_register_module(struct suspend_module_ops *module);
+extern void suspend_unregister_module(struct suspend_module_ops *module);
+
+extern int suspend_initialise_modules(int starting_cycle);
+extern void suspend_cleanup_modules(int finishing_cycle);
+
+int suspend_get_modules(void);
+void suspend_put_modules(void);
+
+static inline void suspend_initialise_module_lists(void) {
+	INIT_LIST_HEAD(&suspend_filters);
+	INIT_LIST_HEAD(&suspend_writers);
+	INIT_LIST_HEAD(&suspend_modules);
+}
+
+#endif
diff -urN oldtree/kernel/power/netlink.c newtree/kernel/power/netlink.c
--- oldtree/kernel/power/netlink.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/netlink.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,375 @@
+/*
+ * kernel/power/netlink.c
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Functions for communicating with a userspace helper via netlink.
+ */
+
+
+#include <linux/suspend.h>
+#include "netlink.h"
+
+#ifdef CONFIG_NET
+struct user_helper_data *uhd_list = NULL;
+       
+/* 
+ * Refill our pool of SKBs for use in emergencies (eg, when eating memory and none
+ * can be allocated).
+ */
+static void suspend_fill_skb_pool(struct user_helper_data *uhd)
+{
+	while (uhd->pool_level < uhd->pool_limit) {
+		struct sk_buff *new_skb =
+			alloc_skb(NLMSG_SPACE(uhd->skb_size), GFP_ATOMIC);
+
+		if (!new_skb)
+			break;
+
+		new_skb->next = uhd->emerg_skbs;
+		uhd->emerg_skbs = new_skb;
+		uhd->pool_level++;
+	}
+}
+
+/* 
+ * Try to allocate a single skb. If we can't get one, try to use one from
+ * our pool.
+ */
+static struct sk_buff *suspend_get_skb(struct user_helper_data *uhd)
+{
+	struct sk_buff *skb =
+		alloc_skb(NLMSG_SPACE(uhd->skb_size), GFP_ATOMIC);
+
+	if (skb)
+		return skb;
+
+	skb = uhd->emerg_skbs;
+	if (skb) {
+		uhd->pool_level--;
+		uhd->emerg_skbs = skb->next;
+		skb->next = NULL;
+	}
+
+	return skb;
+}
+
+static void put_skb(struct user_helper_data *uhd, struct sk_buff *skb)
+{
+	if (uhd->pool_level < uhd->pool_limit) {
+		skb->next = uhd->emerg_skbs;
+		uhd->emerg_skbs = skb;
+	} else
+		kfree_skb(skb);
+}
+
+
+static void suspend_notify_userspace(void* data)
+{
+	struct task_struct *t;
+	struct user_helper_data *uhd = (struct user_helper_data *) data;
+
+	BUG_ON(!uhd);
+
+	read_lock(&tasklist_lock);
+	if ((t = find_task_by_pid(uhd->pid)))
+		wake_up_process(t);
+	read_unlock(&tasklist_lock);
+}
+
+DECLARE_WORK(suspend_notify_userspace_work, suspend_notify_userspace, NULL);
+
+void suspend_send_netlink_message(struct user_helper_data *uhd,
+		int type, void* params, size_t len)
+{
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	void *dest;
+
+	if (uhd->pid == -1)
+		return;
+
+	skb = suspend_get_skb(uhd);
+	if (!skb) {
+		printk("suspend_netlink: Can't allocate skb!\n");
+		return;
+	}
+
+	/* NLMSG_PUT contains a hidden goto nlmsg_failure */
+	nlh = NLMSG_PUT(skb, 0, uhd->sock_seq, type, len);
+	uhd->sock_seq++;
+
+	dest = NLMSG_DATA(nlh);
+	if (params && len > 0)
+		memcpy(dest, params, len);
+
+	netlink_unicast(uhd->nl, skb, uhd->pid, 0);
+
+	/* We may be in an interrupt context so defer waking up userspace */
+	suspend_notify_userspace_work.data = uhd;
+	schedule_work(&suspend_notify_userspace_work);
+
+	return;
+
+nlmsg_failure:
+	if (skb)
+		put_skb(uhd, skb);
+}
+
+#ifdef CONFIG_PM_DEBUG
+static int is_debugging = 1;
+#else
+static int is_debugging = 0;
+#endif
+
+static void send_whether_debugging(struct user_helper_data *uhd)
+{
+	suspend_send_netlink_message(uhd, NETLINK_MSG_IS_DEBUGGING,
+			&is_debugging, sizeof(int));
+}
+
+/*
+ * Set the PF_NOFREEZE flag on the given process to ensure it can run whilst we
+ * are suspending.
+ */
+static int nl_set_nofreeze(struct user_helper_data *uhd, int pid)
+{
+	struct task_struct *t;
+
+	read_lock(&tasklist_lock);
+	if ((t = find_task_by_pid(pid)) == NULL) {
+		read_unlock(&tasklist_lock);
+		printk("Strange. Can't find the userspace task %d.\n", pid);
+		return -EINVAL;
+	}
+
+	t->flags |= PF_NOFREEZE;
+
+	read_unlock(&tasklist_lock);
+	uhd->pid = pid;
+
+	suspend_send_netlink_message(uhd, NETLINK_MSG_NOFREEZE_ACK, NULL, 0);
+
+	return 0;
+}
+
+/*
+ * Called when the userspace process has informed us that it's ready to roll.
+ */
+static int nl_ready(struct user_helper_data *uhd, int version)
+{
+	if (version != uhd->interface_version) {
+		printk("%s userspace process using invalid interface version."
+				" Trying to continue without it.\n",
+				uhd->name);
+		if (uhd->not_ready)
+			uhd->not_ready();
+		return 1;
+	}
+
+	complete(&uhd->wait_for_process);
+
+	return 0;
+}
+
+static int suspend_nl_gen_rcv_msg(struct user_helper_data *uhd,
+		struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+	int type;
+	int *data;
+	int err;
+
+	/* Let the more specific handler go first. It returns
+	 * 1 for valid messages that it doesn't know. */
+	if ((err = uhd->rcv_msg(skb, nlh)) != 1)
+		return err;
+	
+	type = nlh->nlmsg_type;
+
+	/* Only allow one task to receive NOFREEZE privileges */
+	if (type == NETLINK_MSG_NOFREEZE_ME && uhd->pid != -1) {
+		printk("Received extra nofreeze me requests.\n");
+		return -EBUSY;
+	}
+
+	data = (int*)NLMSG_DATA(nlh);
+
+	switch (type) {
+		case NETLINK_MSG_NOFREEZE_ME:
+			if ((err = nl_set_nofreeze(uhd, nlh->nlmsg_pid)) != 0)
+				return err;
+			break;
+		case NETLINK_MSG_GET_DEBUGGING:
+			send_whether_debugging(uhd);
+			break;
+		case NETLINK_MSG_READY:
+			if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int))) {
+				printk("Invalid ready mesage.\n");
+				return -EINVAL;
+			}
+			if ((err = nl_ready(uhd, *data)) != 0)
+				return err;
+			break;
+	}
+
+	return 0;
+}
+
+static void suspend_user_rcv_skb(struct user_helper_data *uhd,
+				  struct sk_buff *skb)
+{
+	int err;
+	struct nlmsghdr *nlh;
+
+	while (skb->len >= NLMSG_SPACE(0)) {
+		u32 rlen;
+
+		nlh = (struct nlmsghdr *) skb->data;
+		if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
+			return;
+
+		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+		if (rlen > skb->len)
+			rlen = skb->len;
+
+		if ((err = suspend_nl_gen_rcv_msg(uhd, skb, nlh)) != 0)
+			netlink_ack(skb, nlh, err);
+		else if (nlh->nlmsg_flags & NLM_F_ACK)
+			netlink_ack(skb, nlh, 0);
+		skb_pull(skb, rlen);
+	}
+}
+
+static void suspend_netlink_input(struct sock *sk, int len)
+{
+	struct user_helper_data *uhd = uhd_list;
+
+	while (uhd && uhd->netlink_id != sk->sk_protocol)
+		uhd= uhd->next;
+
+	BUG_ON(!uhd);
+
+	do {
+		struct sk_buff *skb;
+		while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+			suspend_user_rcv_skb(uhd, skb);
+			put_skb(uhd, skb);
+		}
+	} while (uhd->nl && uhd->nl->sk_receive_queue.qlen);
+}
+
+static int netlink_prepare(struct user_helper_data *uhd)
+{
+	uhd->next = uhd_list;
+	uhd_list = uhd;
+
+	uhd->sock_seq = 0x42c0ffee;
+	uhd->nl = netlink_kernel_create(uhd->netlink_id, 0,
+			suspend_netlink_input, THIS_MODULE);
+	if (!uhd->nl) {
+		printk("Failed to allocate netlink socket for %s.\n",
+				uhd->name);
+		return -ENOMEM;
+	}
+
+	suspend_fill_skb_pool(uhd);
+
+	return 0;
+}
+
+void suspend_netlink_close(struct user_helper_data *uhd)
+{
+	if (uhd->nl) {
+		sock_release(uhd->nl->sk_socket);
+		uhd->nl = NULL;
+	}
+
+	while (uhd->emerg_skbs) {
+		struct sk_buff *next = uhd->emerg_skbs->next;
+		kfree_skb(uhd->emerg_skbs);
+		uhd->emerg_skbs = next;
+	}
+}
+
+int suspend2_launch_userspace_program(char *command, int channel_no)
+{
+	int retval;
+	static char *envp[] = {
+			"HOME=/",
+			"TERM=linux",
+			"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+			NULL };
+	static char *argv[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+	char *channel = kmalloc(6, GFP_KERNEL);
+	int arg = 0, size;
+	char test_read[255];
+	char *orig_posn = command;
+
+	if (!strlen(orig_posn))
+		return 1;
+
+	/* Up to 7 args supported */
+	while (arg < 7) {
+		sscanf(orig_posn, "%s", test_read);
+		size = strlen(test_read);
+		if (!(size))
+			break;
+		argv[arg] = kmalloc(size + 1, GFP_ATOMIC);
+		strcpy(argv[arg], test_read);
+		orig_posn += size + 1;
+		*test_read = 0;
+		arg++;
+	}
+	
+	if (channel_no) {
+		sprintf(channel, "-c%d", channel_no);
+		argv[arg] = channel;
+	} else
+		arg--;
+
+	retval = call_usermodehelper(argv[0], argv, envp, 0);
+
+	if (retval)
+		printk("Failed to launch userspace program '%s': Error %d\n",
+				command, retval);
+
+	{
+		int i;
+		for (i = 0; i < arg; i++)
+			if (argv[i] && argv[i] != channel)
+				kfree(argv[i]);
+	}
+
+	kfree(channel);
+
+	return retval;
+}
+
+int suspend_netlink_setup(struct user_helper_data *uhd)
+{
+	if (netlink_prepare(uhd) < 0) {
+		printk("Netlink prepare failed.\n");
+		return 1;
+	}
+
+	if (suspend2_launch_userspace_program(uhd->program, uhd->netlink_id) < 0) {
+		printk("Launch userspace program failed.\n");
+		suspend_netlink_close(uhd);
+		return 1;
+	}
+
+	/* Wait 2 seconds for the userspace process to make contact */
+	wait_for_completion_timeout(&uhd->wait_for_process, 2*HZ);
+
+	if (uhd->pid == -1) {
+		printk("%s: Failed to contact userspace process.\n",
+				uhd->name);
+		suspend_netlink_close(uhd);
+		return 1;
+	}
+
+	return 0;
+}
+#endif
diff -urN oldtree/kernel/power/netlink.h newtree/kernel/power/netlink.h
--- oldtree/kernel/power/netlink.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/netlink.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,59 @@
+/*
+ * kernel/power/netlink.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Declarations for functions for communicating with a userspace helper
+ * via netlink.
+ */
+
+#include <linux/netlink.h>
+#include <net/sock.h>
+
+#define NETLINK_MSG_BASE 0x10
+
+#define NETLINK_MSG_READY 0x10
+#define	NETLINK_MSG_NOFREEZE_ME 0x16
+#define NETLINK_MSG_GET_DEBUGGING 0x19
+#define NETLINK_MSG_CLEANUP 0x24
+#define NETLINK_MSG_NOFREEZE_ACK 0x27
+#define NETLINK_MSG_IS_DEBUGGING 0x28
+
+struct user_helper_data {
+	int (*rcv_msg) (struct sk_buff *skb, struct nlmsghdr *nlh);
+	void (* not_ready) (void);
+	struct sock *nl;
+	u32 sock_seq;
+	pid_t pid;
+	char *comm;
+	char program[256];
+	int pool_level;
+	int pool_limit;
+	struct sk_buff *emerg_skbs;
+	int skb_size;
+	int netlink_id;	
+	char *name;
+	struct user_helper_data *next;
+	struct completion wait_for_process;
+	int interface_version;
+	int must_init;
+};
+
+
+#ifdef CONFIG_NET
+int suspend_netlink_setup(struct user_helper_data *uhd);
+void suspend_netlink_close(struct user_helper_data *uhd);
+void suspend_send_netlink_message(struct user_helper_data *uhd,
+		int type, void* params, size_t len);
+#else
+static inline int suspend_netlink_setup(struct user_helper_data *uhd)
+{
+	return 0;
+}
+
+static inline void suspend_netlink_close(struct user_helper_data *uhd) { };
+static inline void suspend_send_netlink_message(struct user_helper_data *uhd,
+		int type, void* params, size_t len) { };
+#endif
diff -urN oldtree/kernel/power/pagedir.c newtree/kernel/power/pagedir.c
--- oldtree/kernel/power/pagedir.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/pagedir.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,358 @@
+/*
+ * kernel/power/pagedir.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-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Routines for handling pagesets.
+ * Note that pbes aren't actually stored as such. They're stored as
+ * bitmaps and extents.
+ */
+
+#include <linux/suspend.h>
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+#include <linux/hardirq.h>
+#include <linux/sched.h>
+#include <asm/tlbflush.h>
+
+#include "pageflags.h"
+#include "ui.h"
+#include "pagedir.h"
+
+int extra_pagedir_pages_allocated;
+
+struct extras {
+	struct page *page;
+	int order;
+	struct extras *next;
+} *extras_list;
+
+/* suspend_free_extra_pagedir_memory
+ *
+ * Description:	Free previously allocated extra pagedir memory.
+ */
+void suspend_free_extra_pagedir_memory(void)
+{
+	/* Free allocated pages */
+	while (extras_list) {
+		struct extras *this = extras_list;
+		int i;
+
+		extras_list = this->next;
+
+		for (i = 0; i < (1 << this->order); i++)
+			ClearPageNosave(this->page + i);
+
+		__free_pages(this->page, this->order);
+		kfree(this);
+	}
+
+	extra_pagedir_pages_allocated = 0;
+}
+
+/* suspend_allocate_extra_pagedir_memory
+ *
+ * Description:	Allocate memory for making the atomic copy of pagedir1 in the
+ * 		case where it is bigger than pagedir2.
+ * Arguments:	struct pagedir *: 	The pagedir for which we should 
+ * 					allocate memory.
+ * 		int:			Size of pageset 1.
+ * 		int:			Size of pageset 2.
+ * Result:	int. Zero on success. One if unable to allocate enough memory.
+ */
+int suspend_allocate_extra_pagedir_memory(struct pagedir *p, int pageset_size,
+		int alloc_from)
+{
+	int num_to_alloc = pageset_size - alloc_from - extra_pagedir_pages_allocated;
+	int j, order, num_added = 0;
+
+	if (num_to_alloc < 1)
+		num_to_alloc = 0;
+
+	if (num_to_alloc) {
+		order = fls(num_to_alloc);
+		if (order >= MAX_ORDER)
+			order = MAX_ORDER - 1;
+
+		while (num_added < num_to_alloc) {
+			struct page *newpage;
+			unsigned long virt;
+			struct extras *extras_entry;
+			
+			while ((1 << order) > (num_to_alloc - num_added))
+				order--;
+
+			virt = __get_free_pages(GFP_ATOMIC | __GFP_NOWARN, order);
+			while ((!virt) && (order > 0)) {
+				order--;
+				virt = __get_free_pages(GFP_ATOMIC | __GFP_NOWARN, order);
+			}
+
+			if (!virt) {
+				p->pageset_size += num_added;
+				extra_pagedir_pages_allocated += num_added;
+				return 1;
+			}
+
+			newpage = virt_to_page(virt);
+
+			extras_entry = (struct extras *) kmalloc(sizeof(struct extras), GFP_ATOMIC);
+
+			if (!extras_entry) {
+				__free_pages(newpage, order);
+				extra_pagedir_pages_allocated += num_added;
+				return 1;
+			}
+
+			extras_entry->page = newpage;
+			extras_entry->order = order;
+			extras_entry->next = NULL;
+
+			if (extras_list)
+				extras_entry->next = extras_list;
+
+			extras_list = extras_entry;
+
+			for (j = 0; j < (1 << order); j++) {
+				SetPageNosave(newpage + j);
+				SetPagePageset1Copy(newpage + j);
+			}
+			num_added+= (1 << order);
+		}
+	}
+
+	extra_pagedir_pages_allocated += num_added;
+	return 0;
+}
+
+int suspend_protect_pageset2(void)
+{
+#ifdef CONFIG_DEBUG_PAGEALLOC
+	int pfn;
+
+	BITMAP_FOR_EACH_SET(pageset2_map, pfn) {
+		struct page *page = pfn_to_page(pfn);
+		if (PageHighMem(page))
+			continue;
+
+		change_page_attr(page, 1, __pgprot(0));
+		__flush_tlb_all();
+	}
+#endif
+	
+	return 0;
+}
+
+void suspend_restore_pageset2_permissions(void)
+{
+#ifdef CONFIG_DEBUG_PAGEALLOC
+	int pfn;
+
+	BITMAP_FOR_EACH_SET(pageset2_map, pfn) {
+		struct page *page = pfn_to_page(pfn);
+		if (PageHighMem(page))
+			continue;
+
+		change_page_attr(page, 1, PAGE_KERNEL);
+		__flush_tlb_all();
+	}
+#endif
+}
+
+/*
+ * suspend_mark_task_as_pageset1
+ * Functionality   : Marks all the pages belonging to a given process as
+ *                   pageset 1 pages.
+ * Called From     : pagedir.c - mark_pages_for_pageset2
+ *
+ */
+extern struct page *suspend2_follow_page(struct mm_struct *mm, unsigned long address);
+
+static void suspend_mark_task_as_pageset1(struct task_struct *t)
+{
+	struct vm_area_struct *vma;
+	struct mm_struct *mm;
+
+	mm = t->active_mm;
+
+	if (!mm || !mm->mmap) return;
+
+	/* Don't try to take the sem when processes are frozen, 
+	 * drivers are suspended and irqs are disabled. We're
+	 * not racing with anything anyway.  */
+	BUG_ON(in_atomic() && !irqs_disabled());
+
+	if (!irqs_disabled())
+		down_read(&mm->mmap_sem);
+	
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if (vma->vm_flags & VM_PFNMAP)
+			continue;
+		if (vma->vm_start) {
+			unsigned long posn;
+			for (posn = vma->vm_start; posn < vma->vm_end;
+					posn += PAGE_SIZE) {
+				struct page *page = 
+					suspend2_follow_page(mm, posn);
+				if (page) {
+					ClearPagePageset2(page);
+					SetPagePageset1(page);
+				}
+			}
+		}
+	}
+
+	BUG_ON(in_atomic() && !irqs_disabled());
+
+	if (!irqs_disabled())
+		up_read(&mm->mmap_sem);
+}
+
+/* mark_pages_for_pageset2
+ *
+ * Description:	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.
+ */
+
+struct attention_list {
+	struct task_struct *task;
+	struct attention_list *next;
+};
+
+void suspend_mark_pages_for_pageset2(void)
+{
+	struct zone *zone;
+	struct task_struct *p;
+	struct attention_list *attention_list = NULL, *last = NULL;
+	unsigned long flags;
+
+	BUG_ON(in_atomic() && !irqs_disabled());
+
+	if (test_action_state(SUSPEND_NO_PAGESET2))
+		return;
+
+	clear_dyn_pageflags(pageset2_map);
+	
+	for_each_zone(zone) {
+		spin_lock_irqsave(&zone->lru_lock, flags);
+		if (zone->nr_inactive) {
+			struct page *page;
+			list_for_each_entry(page, &zone->inactive_list, lru)
+				SetPagePageset2(page);
+		}
+		if (zone->nr_active) {
+			struct page *page;
+			list_for_each_entry(page, &zone->active_list, lru)
+				SetPagePageset2(page);
+		}
+		spin_unlock_irqrestore(&zone->lru_lock, flags);
+	}
+
+	BUG_ON(in_atomic() && !irqs_disabled());
+
+	/* Now we find all userspace process (with task->mm) marked PF_NOFREEZE
+	 * and move them into pageset1.
+	 */
+	read_lock(&tasklist_lock);
+	for_each_process(p)
+		if ((p->flags & PF_NOFREEZE) || p == current) {
+			struct attention_list *this = kmalloc(sizeof(struct attention_list), GFP_ATOMIC);
+			BUG_ON(!this);
+			this->task = p;
+			this->next = NULL;
+			if (attention_list) {
+				last->next = this;
+				last = this;
+			} else
+				attention_list = last = this;
+		}
+	read_unlock(&tasklist_lock);
+
+	BUG_ON(in_atomic() && !irqs_disabled());
+
+	/* Because the tasks in attention_list are ones related to suspending,
+	 * we know that they won't go away under us.
+	 */
+
+	while (attention_list) {
+		suspend_mark_task_as_pageset1(attention_list->task);
+		last = attention_list;
+		attention_list = attention_list->next;
+		kfree(last);
+	}
+
+	BUG_ON(in_atomic() && !irqs_disabled());
+
+}
+
+/* suspend_get_nonconflicting_page
+ *
+ * Description: Gets order zero pages that won't be overwritten
+ *		while copying the original pages.
+ */
+
+unsigned long suspend_get_nonconflicting_page(void)
+{
+	struct page *page;
+
+	do {
+		page = alloc_pages(GFP_ATOMIC | __GFP_NOWARN | __GFP_ZERO, 0);
+		BUG_ON(!page);
+	} while(PagePageset1(page));
+
+	return (unsigned long) page_address(page);
+}
+
+/* relocate_page_if_required
+ *
+ * Description: Given the address of a pointer to a page, we check if the page
+ * 		needs relocating and do so if needs be, adjusting the pointer
+ * 		too.
+ */
+
+void suspend_relocate_if_required(unsigned long *current_value, unsigned int size)
+{
+	if (PagePageset1(virt_to_page(*current_value))) {
+		unsigned long new_page = suspend_get_nonconflicting_page();
+		memcpy((char *) new_page, (char *) *current_value, size);
+		if (PageSlab(virt_to_page(*current_value)))
+			kfree((void *) *current_value);
+		else
+			free_page((unsigned long) *current_value);
+		*current_value = new_page;
+	}
+}
+
+/* get_pageset1_load_addresses
+ * 
+ * Description: 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.
+ * Returns:	Zero on success, one if couldn't find enough pages (shouldn't
+ * 		happen).
+ */
+
+int suspend_get_pageset1_load_addresses(void)
+{
+	int i, result = 0;
+	void *this;
+
+	for(i=0; i < pagedir1.pageset_size; i++) {
+		this = (void *) suspend_get_nonconflicting_page();
+		if (!this) {
+			abort_suspend("Error: Ran out of memory seeking locations for reloading data.");
+			result = 1;
+			break;
+		}
+		SetPagePageset1Copy(virt_to_page(this));
+	}
+
+	return result;
+}
diff -urN oldtree/kernel/power/pagedir.h newtree/kernel/power/pagedir.h
--- oldtree/kernel/power/pagedir.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/pagedir.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,40 @@
+/*
+ * kernel/power/pagedir.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Declarations for routines for handling pagesets.
+ */
+
+/* Pagedir
+ *
+ * Contains the metadata for a set of pages saved in the image.
+ */
+
+struct pagedir {
+	long pageset_size;
+	long lastpageset_size;
+};
+
+extern struct pagedir pagedir1, pagedir2;
+
+extern void suspend_copy_pageset1(void);
+
+extern void suspend_free_extra_pagedir_memory(void);
+
+extern int suspend_allocate_extra_pagedir_memory(struct pagedir *p, int pageset_size, int alloc_from);
+
+//extern void suspend_mark_task_as_pageset1 (struct task_struct *t);
+extern void suspend_mark_pages_for_pageset2(void);
+
+extern void suspend_relocate_if_required(unsigned long *current_value, unsigned int size);
+extern int suspend_get_pageset1_load_addresses(void);
+
+extern int extra_pagedir_pages_allocated;
+
+extern unsigned long suspend_get_nonconflicting_page(void);
+
+int suspend_protect_pageset2(void);
+void suspend_restore_pageset2_permissions(void);
diff -urN oldtree/kernel/power/pageflags.c newtree/kernel/power/pageflags.c
--- oldtree/kernel/power/pageflags.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/pageflags.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,169 @@
+/*
+ * kernel/power/pageflags.c
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ * 
+ * This file is released under the GPLv2.
+ *
+ * Routines for serialising and relocating pageflags in which we
+ * store our image metadata.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/list.h>
+#include <linux/suspend.h>
+#include "pageflags.h"
+#include "modules.h"
+#include "pagedir.h"
+
+/* Maps used in copying the image back are in builtin.c */
+dyn_pageflags_t pageset1_map;
+dyn_pageflags_t pageset1_copy_map;
+dyn_pageflags_t pageset2_map;
+dyn_pageflags_t pageset2_rw_map;
+dyn_pageflags_t in_use_map;
+
+static int pages_for_zone(struct zone *zone)
+{
+	return (zone->spanned_pages + (PAGE_SIZE << 3) - 1) /
+			(PAGE_SIZE << 3);
+}
+
+int suspend_pageflags_space_needed(void)
+{
+	int total = 0;
+	struct zone *zone;
+
+	for_each_zone(zone)
+		if (populated_zone(zone))
+			total += sizeof(int) * 2 + pages_for_zone(zone) * PAGE_SIZE;
+
+	total += sizeof(int);
+
+	return total;
+}
+
+/* save_dyn_pageflags
+ *
+ * Description: Save a set of pageflags.
+ * Arguments:   dyn_pageflags_t *: Pointer to the bitmap being saved.
+ */
+
+void save_dyn_pageflags(dyn_pageflags_t pagemap)
+{
+	int i, zone_num, size;
+	struct zone *zone;
+
+	if (!*pagemap)
+		return;
+
+	for_each_zone(zone) {
+		if (!populated_zone(zone))
+			continue;
+
+		size = pages_for_zone(zone);
+		zone_num = page_zone_id(pfn_to_page(zone->zone_start_pfn));
+
+		suspend_active_writer->rw_header_chunk(WRITE, NULL,
+				(char *) &zone_num, sizeof(int));
+		suspend_active_writer->rw_header_chunk(WRITE, NULL,
+				(char *) &size, sizeof(int));
+
+		for (i = 0; i < size; i++)
+			suspend_active_writer->rw_header_chunk(WRITE, NULL,
+				(char *) pagemap[zone_num][i], PAGE_SIZE);
+	}
+	zone_num = -1;
+	suspend_active_writer->rw_header_chunk(WRITE, NULL,
+			(char *) &zone_num, sizeof(int));
+}
+
+/* load_dyn_pageflags
+ *
+ * Description: Load a set of pageflags.
+ * Arguments:   dyn_pageflags_t *: Pointer to the bitmap being loaded.
+ *              (It must be allocated before calling this routine).
+ */
+
+void load_dyn_pageflags(dyn_pageflags_t pagemap)
+{
+	int i, zone_num, zone_check = 0, size;
+	struct zone *zone;
+
+	if (!pagemap)
+		return;
+
+	for_each_zone(zone) {
+		if (!populated_zone(zone))
+			continue;
+
+		zone_num = page_zone_id(pfn_to_page(zone->zone_start_pfn));
+
+		suspend_active_writer->rw_header_chunk(READ, NULL,
+				(char *) &zone_check, sizeof(int));
+		if (zone_check != zone_num) {
+			printk("Zone check (%d) != zone_num (%d).\n",
+					zone_check, zone_num);
+			BUG();
+		}
+		suspend_active_writer->rw_header_chunk(READ, NULL,
+				(char *) &size, sizeof(int));
+
+		for (i = 0; i < size; i++)
+			suspend_active_writer->rw_header_chunk(READ, NULL,
+					(char *) pagemap[zone_num][i],
+					PAGE_SIZE);
+	}
+	suspend_active_writer->rw_header_chunk(READ, NULL, (char *) &zone_check,
+			sizeof(int));
+	if (zone_check != -1) {
+		printk("Didn't read end of dyn pageflag data marker.(%x)\n",
+				zone_check);
+		BUG();
+	}
+}
+
+/* relocate_dyn_pageflags
+ *
+ * Description: Relocate a set of pageflags to ensure they don't collide with
+ *              pageset 1 data which will get overwritten on copyback.
+ * Arguments:   dyn_pageflags_t *: Pointer to the bitmap being relocated.
+ */
+
+void relocate_dyn_pageflags(dyn_pageflags_t *pagemap)
+{
+	int i, zone_num;
+	struct zone *zone;
+
+	if (!*pagemap)
+		return;
+
+	suspend_relocate_if_required((unsigned long *) pagemap,
+			(unsigned int) (sizeof(void *) * (1 << ZONEID_SHIFT)));
+	BUG_ON(PagePageset1(virt_to_page(*pagemap)));
+
+	for_each_zone(zone) {
+		int pages = pages_for_zone(zone);
+
+		if (!populated_zone(zone))
+			continue;
+
+		zone_num = page_zone_id(pfn_to_page(zone->zone_start_pfn));
+
+		suspend_relocate_if_required((void *) &((*pagemap)[zone_num]),
+			       sizeof(void *) * pages);
+		BUG_ON(PagePageset1(virt_to_page((*pagemap)[zone_num])));
+
+		for (i = 0; i < pages; i++) {
+			suspend_relocate_if_required(
+				(void *) &((*pagemap)[zone_num][i]),
+				PAGE_SIZE);
+			BUG_ON(PagePageset1(virt_to_page(
+						(*pagemap)[zone_num][i])));
+		}
+	}
+}
diff -urN oldtree/kernel/power/pageflags.h newtree/kernel/power/pageflags.h
--- oldtree/kernel/power/pageflags.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/pageflags.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,56 @@
+/*
+ * kernel/power/pageflags.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Suspend2 needs a few pageflags while working that aren't otherwise
+ * used. To save the struct page pageflags, we dynamically allocate
+ * a bitmap and use that. These are the only non order-0 allocations
+ * we do.
+ *
+ * NOTE!!!
+ * We assume that PAGE_SIZE - sizeof(void *) is a multiple of
+ * sizeof(unsigned long). Is this ever false?
+ */
+
+#include <linux/dyn_pageflags.h>
+#include <linux/suspend.h>
+
+extern dyn_pageflags_t in_use_map;
+extern dyn_pageflags_t pageset2_map;
+extern dyn_pageflags_t pageset2_rw_map;
+
+/* 
+ * inusemap is used in two ways: 
+ * - During suspend, to tag pages which are not used (to speed up 
+ *   count_data_pages);
+ * - During resume, to tag pages which are in pagedir1. This does not tag 
+ *   pagedir2 pages, so !== first use.
+ */
+
+#define PageInUse(page) (test_dynpageflag(&in_use_map, page))
+#define SetPageInUse(page) (set_dynpageflag(&in_use_map, page))
+#define ClearPageInUse(page) (clear_dynpageflag(&in_use_map, page))
+
+#define PagePageset1(page) (test_dynpageflag(&pageset1_map, page))
+#define SetPagePageset1(page) (set_dynpageflag(&pageset1_map, page))
+#define ClearPagePageset1(page) (clear_dynpageflag(&pageset1_map, page))
+
+#define PagePageset1Copy(page) (test_dynpageflag(&pageset1_copy_map, page))
+#define SetPagePageset1Copy(page) (set_dynpageflag(&pageset1_copy_map, page))
+#define ClearPagePageset1Copy(page) (clear_dynpageflag(&pageset1_copy_map, page))
+
+#define PagePageset2(page) (test_dynpageflag(&pageset2_map, page))
+#define SetPagePageset2(page) (set_dynpageflag(&pageset2_map, page))
+#define ClearPagePageset2(page) (clear_dynpageflag(&pageset2_map, page))
+
+#define PageWasRW(page) (test_dynpageflag(&pageset2_map, page))
+#define SetPageWasRW(page) (set_dynpageflag(&pageset2_map, page))
+#define ClearPageWasRW(page) (clear_dynpageflag(&pageset2_map, page))
+
+extern void save_dyn_pageflags(dyn_pageflags_t pagemap);
+extern void load_dyn_pageflags(dyn_pageflags_t pagemap);
+extern void relocate_dyn_pageflags(dyn_pageflags_t *pagemap);
+extern int suspend_pageflags_space_needed(void);
diff -urN oldtree/kernel/power/power.h newtree/kernel/power/power.h
--- oldtree/kernel/power/power.h	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/power/power.h	2006-10-06 17:28:20.000000000 -0400
@@ -1,5 +1,6 @@
 #include <linux/suspend.h>
 #include <linux/utsname.h>
+#include "suspend.h"
 
 struct swsusp_info {
 	struct new_utsname	uts;
@@ -19,7 +20,15 @@
 #else
 static inline int pm_suspend_disk(void)
 {
+#ifdef CONFIG_SUSPEND2
+	if (suspend_start_anything(1))
+		return -EBUSY;
+	suspend_main(1);
+	suspend_finish_anything(1);
+	return 0;
+#else
 	return -EPERM;
+#endif
 }
 #endif
 extern struct semaphore pm_sem;
@@ -35,9 +44,6 @@
 
 extern struct subsystem power_subsys;
 
-/* References to section boundaries */
-extern const void __nosave_begin, __nosave_end;
-
 /* Preferred image size in bytes (default 500 MB) */
 extern unsigned long image_size;
 extern int in_suspend;
diff -urN oldtree/kernel/power/power_off.c newtree/kernel/power/power_off.c
--- oldtree/kernel/power/power_off.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/power_off.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,86 @@
+/*
+ * kernel/power/power_off.c
+ *
+ * Copyright (C) 2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Support for powering down.
+ */
+
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <linux/mm.h>
+#include <linux/pm.h>
+#include <linux/reboot.h>
+#include "suspend.h"
+#include "ui.h"
+
+unsigned long suspend_powerdown_method = 0; /* 0 - Kernel power off */
+
+extern struct pm_ops *pm_ops;
+
+/* Use suspend_enter from main.c */
+extern int suspend_enter(suspend_state_t state);
+
+int try_pm_state_powerdown(void)
+{
+	if (pm_ops && pm_ops->prepare && suspend_powerdown_method &&
+	    pm_ops->prepare(suspend_powerdown_method))
+			return 0;
+
+	if (suspend_powerdown_method > 3)
+		kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK);
+	else {
+		if (device_suspend(PMSG_SUSPEND)) {
+			printk(KERN_ERR "Some devices failed to suspend\n");
+			return 0;
+		}
+	}
+
+	mdelay(1000); /* Give time for devices to power down */
+	
+	if (suspend_enter(suspend_powerdown_method))
+		return 0;
+
+	device_resume();
+
+	if (pm_ops && pm_ops->finish && suspend_powerdown_method)
+		pm_ops->finish(suspend_powerdown_method);
+
+	return 1;
+}
+
+/*
+ * suspend_power_down
+ * Functionality   : Powers down or reboots the computer once the image
+ *                   has been written to disk.
+ * Key Assumptions : Able to reboot/power down via code called or that
+ *                   the warning emitted if the calls fail will be visible
+ *                   to the user (ie printk resumes devices).
+ * Called From     : do_suspend2_suspend_2
+ */
+
+void suspend_power_down(void)
+{
+	if (test_action_state(SUSPEND_REBOOT)) {
+		suspend_prepare_status(DONT_CLEAR_BAR, "Ready to reboot.");
+		kernel_restart(NULL);
+	}
+
+	suspend_prepare_status(DONT_CLEAR_BAR, "Powering down.");
+
+	if (pm_ops && pm_ops->enter && suspend_powerdown_method && try_pm_state_powerdown())
+		return;
+
+	kernel_shutdown_prepare(SYSTEM_POWER_OFF);
+
+	mdelay(1000); /* Give time for devices to power down */
+
+	machine_power_off();
+	machine_halt();
+	suspend_prepare_status(DONT_CLEAR_BAR, "Powerdown failed");
+	while (1)
+		cpu_relax();
+}
+
diff -urN oldtree/kernel/power/power_off.h newtree/kernel/power/power_off.h
--- oldtree/kernel/power/power_off.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/power_off.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,13 @@
+/*
+ * kernel/power/power_off.h
+ *
+ * Copyright (C) 2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Support for the powering down.
+ */
+
+int suspend_pm_state_finish(void);
+void suspend_power_down(void);
+extern unsigned long suspend_powerdown_method;
diff -urN oldtree/kernel/power/prepare_image.c newtree/kernel/power/prepare_image.c
--- oldtree/kernel/power/prepare_image.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/prepare_image.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,689 @@
+/*
+ * kernel/power/prepare_image.c
+ *
+ * Copyright (C) 2003-2006 Nigel Cunningham <nigel@suspend.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * We need to eat memory until we can:
+ * 1. Perform the save without changing anything (RAM_NEEDED < max_pfn)
+ * 2. Fit it all in available space (suspend_active_writer->available_space() >=
+ *    storage_needed())
+ * 3. Reload the pagedir and pageset1 to places that don't collide with their
+ *    final destinations, not knowing to what extent the resumed kernel will
+ *    overlap with the one loaded at boot time. I think the resumed kernel
+ *    should overlap completely, but I don't want to rely on this as it is 
+ *    an unproven assumption. We therefore assume there will be no overlap at
+ *    all (worse case).
+ * 4. Meet the user's requested limit (if any) on the size of the image.
+ *    The limit is in MB, so pages/256 (assuming 4K pages).
+ *
+ */
+
+#include <linux/highmem.h>
+#include <linux/freezer.h>
+#include <linux/hardirq.h>
+
+#include "pageflags.h"
+#include "modules.h"
+#include "io.h"
+#include "ui.h"
+#include "extent.h"
+#include "prepare_image.h"
+#include "block_io.h"
+#include "suspend.h"
+
+static int are_frozen = 0, num_nosave = 0;
+static long header_space_allocated = 0;
+static long storage_allocated = 0;
+static long storage_available = 0;
+long extra_pd1_pages_allowance = MIN_EXTRA_PAGES_ALLOWANCE;
+
+/*
+ * num_pcp_pages: Count pcp pages.
+ */
+static long num_pcp_pages(void)
+{
+	struct zone *zone;
+	long result = 0, i = 0;
+
+	/* PCP lists */
+	for_each_zone(zone) {
+		struct per_cpu_pageset *pset;
+		int cpu;
+		
+		if (!zone->present_pages)
+			continue;
+		
+		for (cpu = 0; cpu < NR_CPUS; cpu++) {
+			if (!cpu_possible(cpu))
+				continue;
+
+			pset = zone_pcp(zone, cpu);
+
+			for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) {
+				struct per_cpu_pages *pcp;
+
+				pcp = &(pset->pcp[i]);
+				result += pcp->count;
+			}
+		}
+	}
+	return result;
+}
+
+/*
+ * Number of free pages, including pcp pages.
+ */
+long real_nr_free_pages(void)
+{
+	return nr_free_pages() + num_pcp_pages();
+}
+
+/*
+ * Discover how much extra memory will be required by the drivers
+ * when they're asked to suspend. We can then ensure that amount
+ * of memory is available when we really want it.
+ */
+static void get_extra_pd1_allowance(void)
+{
+	int orig_num_free = real_nr_free_pages(), final;
+	
+	suspend_prepare_status(CLEAR_BAR, "Finding allowance for drivers.");
+	device_suspend(PMSG_FREEZE);
+	local_irq_disable(); /* irqs might have been re-enabled on us */
+	device_power_down(PMSG_FREEZE);
+	
+	final = real_nr_free_pages();
+
+	device_power_up();
+	local_irq_enable();
+
+	device_resume();
+
+	extra_pd1_pages_allowance = max(
+		orig_num_free - final + MIN_EXTRA_PAGES_ALLOWANCE,
+		MIN_EXTRA_PAGES_ALLOWANCE);
+}
+
+/*
+ * Amount of storage needed, possibly taking into account the
+ * expected compression ratio and possibly also ignoring our
+ * allowance for extra pages.
+ */
+static long main_storage_needed(int use_ecr,
+		int ignore_extra_pd1_allow)
+{
+	return ((pagedir1.pageset_size + pagedir2.pageset_size +
+	  (ignore_extra_pd1_allow ? 0 : extra_pd1_pages_allowance)) *
+	 (use_ecr ? suspend_expected_compression_ratio() : 100) / 100);
+}
+
+/*
+ * Storage needed for the image header, in bytes until the return.
+ */
+static int header_storage_needed(void)
+{
+	unsigned long bytes =
+		(int) sizeof(struct suspend_header) +
+	 	(int) suspend_header_storage_for_modules() +
+		suspend_pageflags_space_needed();
+
+	return ((int) ((bytes + (int) PAGE_SIZE - 1) >> PAGE_SHIFT));
+}
+
+static void display_stats(int always, int sub_extra_pd1_allow)
+{ 
+	char buffer[255];
+	snprintf(buffer, 254, 
+		"Free:%d(%d). Sets:%ld(%ld),%ld(%ld). Header:%d. Nosave:%d-%d=%d. Storage:%lu/%lu(%lu). Needed:%ld|%ld|%ld.\n", 
+		
+		/* Free */
+		nr_free_pages(),
+		nr_free_pages() - nr_free_highpages(),
+		
+		/* Sets */
+		pagedir1.pageset_size, pageset1_sizelow,
+		pagedir2.pageset_size, pageset2_sizelow,
+
+		/* Header */
+		header_storage_needed(),
+
+		/* Nosave */
+		num_nosave, extra_pagedir_pages_allocated,
+		num_nosave - extra_pagedir_pages_allocated,
+
+		/* Storage - converted to pages for comparison */
+		storage_allocated,
+		storage_needed(1, sub_extra_pd1_allow),
+		storage_available,
+
+		/* Needed */
+		ram_to_suspend() - nr_free_pages() - nr_free_highpages(),
+		storage_needed(1, sub_extra_pd1_allow) - storage_available, 
+		(image_size_limit > 0) ? (storage_needed(1, sub_extra_pd1_allow) - (image_size_limit << 8)) : 0);
+	if (always)
+		printk(buffer);
+	else
+		suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_MEDIUM, 1, buffer);
+}
+
+/* generate_free_page_map
+ *
+ * Description:	This routine generates a bitmap of free pages from the
+ * 		lists used by the memory manager. We then use the bitmap
+ * 		to quickly calculate which pages to save and in which
+ * 		pagesets.
+ */
+static void generate_free_page_map(void) 
+{
+	int order, loop, cpu;
+	struct page *page;
+	unsigned long flags, i;
+	struct zone *zone;
+	struct per_cpu_pageset *pset;
+
+	for_each_zone(zone) {
+		if (!zone->present_pages)
+			continue;
+		for(i=0; i < zone->spanned_pages; i++)
+			SetPageInUse(pfn_to_page(zone->zone_start_pfn + i));
+	}
+	
+	for_each_zone(zone) {
+		if (!zone->present_pages)
+			continue;
+		spin_lock_irqsave(&zone->lock, flags);
+		for (order = MAX_ORDER - 1; order >= 0; --order) {
+			list_for_each_entry(page, &zone->free_area[order].free_list, lru)
+				for(loop=0; loop < (1 << order); loop++)
+					ClearPageInUse(page+loop);
+		}
+
+		
+		for (cpu = 0; cpu < NR_CPUS; cpu++) {
+			if (!cpu_possible(cpu))
+				continue;
+
+			pset = zone_pcp(zone, cpu);
+
+			for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) {
+				struct per_cpu_pages *pcp;
+				struct page *page;
+
+				pcp = &pset->pcp[i];
+				list_for_each_entry(page, &pcp->list, lru)
+					ClearPageInUse(page);
+			}
+		}
+		
+		spin_unlock_irqrestore(&zone->lock, flags);
+	}
+}
+
+/* size_of_free_region
+ * 
+ * Description:	Return the number of pages that are free, beginning with and 
+ * 		including this one.
+ */
+static int size_of_free_region(struct page *page)
+{
+	struct zone *zone = page_zone(page);
+	struct page *posn = page, *last_in_zone =
+		pfn_to_page(zone->zone_start_pfn) + zone->spanned_pages - 1;
+
+	while (posn < last_in_zone && !PageInUse(posn)) {
+		BUG_ON(PagePageset2(posn));
+		posn++;
+	}
+	return (posn - page);
+}
+
+static struct page *rotext_start, *rotext_end;
+static struct page *rodata_start, *rodata_end;
+static struct page *nosave_start, *nosave_end;
+
+static __init int page_nosave_init(void)
+{
+	rodata_start = rodata_start_page();
+	rodata_end = rodata_end_page();
+
+	rotext_start = rotext_start_page();
+	rotext_end = rotext_end_page();
+	
+	nosave_start = nosave_start_page();
+	nosave_end = nosave_end_page();
+
+	return 0;
+}
+
+subsys_initcall(page_nosave_init);
+
+/* count_data_pages
+ *
+ * This routine generates our lists of pages to be stored in each
+ * pageset. Since we store the data using extents, and adding new
+ * extents might allocate a new extent page, this routine may well
+ * be called more than once.
+ */
+static struct pageset_sizes_result count_data_pages(void)
+{
+	int num_free = 0;
+	unsigned long loop;
+	int use_pagedir2;
+	struct pageset_sizes_result result;
+	struct zone *zone;
+
+	result.size1 = 0;
+	result.size1low = 0;
+	result.size2 = 0;
+	result.size2low = 0;
+
+	num_nosave = 0;
+
+	clear_dyn_pageflags(pageset1_map);
+
+	generate_free_page_map();
+
+	if (test_result_state(SUSPEND_ABORTED))
+		return result;
+
+	/*
+	 * Pages not to be saved are marked Nosave irrespective of being reserved
+	 */
+	for_each_zone(zone) {
+		if (!populated_zone(zone))
+			continue;
+
+		for (loop = 0; loop < zone->spanned_pages; loop++) {
+			unsigned long pfn = zone->zone_start_pfn + loop;
+			struct page *page;
+			int chunk_size;
+
+			if (!pfn_valid(pfn))
+				continue;
+
+			page = pfn_to_page(pfn);
+			chunk_size = size_of_free_region(page);
+
+			if (PageNosave(page) ||
+			    (page >= rodata_start && page < rodata_end) ||
+			    (PageReserved(page) &&
+			     ((page >= nosave_start && page < nosave_end) ||
+			      is_highmem(zone)))) {
+				num_nosave++;
+				continue;
+			}
+
+			if (chunk_size) {
+				num_free += chunk_size;
+				loop += chunk_size - 1;
+				continue;
+			}
+
+			use_pagedir2 = PagePageset2(page);
+
+			if (use_pagedir2) {
+				result.size2++;
+				if (!PageHighMem(page)) {
+					result.size2low++;
+					SetPagePageset1Copy(page);
+				}
+			} else {
+				result.size1++;
+				SetPagePageset1(page);
+				if (!PageHighMem(page))
+					result.size1low++;
+			}
+		}
+	}
+
+	suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_MEDIUM, 0,
+		"Count data pages: Set1 (%d) + Set2 (%d) + Nosave (%d) + NumFree (%d) = %d.\n",
+		result.size1, result.size2, num_nosave, num_free,
+		result.size1 + result.size2 + num_nosave + num_free);
+	return result;
+}
+
+/* amount_needed
+ *
+ * Calculates the amount by which the image size needs to be reduced to meet
+ * our constraints.
+ */
+static int amount_needed(int use_image_size_limit)
+{
+
+	int max1 = max( (int) (ram_to_suspend() - real_nr_free_pages() - 
+			  nr_free_highpages()),
+			((int) (storage_needed(1, 0) -  
+			  storage_available)));
+	if (use_image_size_limit)
+		return max( max1,
+			    (image_size_limit > 0) ? 
+			    ((int) (storage_needed(1, 0) - (image_size_limit << 8))) : 0);
+	return max1;
+}
+
+/* suspend_recalculate_image_contents
+ *
+ * Eaten is the number of pages which have been eaten.
+ * Pagedirincluded is the number of pages which have been allocated for the pagedir.
+ */
+void suspend_recalculate_image_contents(int atomic_copy) 
+{
+	struct pageset_sizes_result result;
+
+	clear_dyn_pageflags(pageset1_map);
+	if (!atomic_copy) {
+		int pfn;
+		BITMAP_FOR_EACH_SET(pageset2_map, pfn)
+			ClearPagePageset1Copy(pfn_to_page(pfn));
+		/* Need to call this before getting pageset1_size! */
+		suspend_mark_pages_for_pageset2();
+	}
+	BUG_ON(in_atomic() && !irqs_disabled());
+	result = count_data_pages();
+	pageset1_sizelow = result.size1low;
+	pageset2_sizelow = result.size2low;
+	pagedir1.lastpageset_size = pagedir1.pageset_size = result.size1;
+	pagedir2.lastpageset_size = pagedir2.pageset_size = result.size2;
+
+	if (!atomic_copy) {
+		storage_available = suspend_active_writer->storage_available();
+		display_stats(1, 0);
+	}
+	BUG_ON(in_atomic() && !irqs_disabled());
+	return;
+}
+
+static void try_freeze_processes(void)
+{
+	if (freeze_processes()) {
+		set_result_state(SUSPEND_FREEZING_FAILED);
+		set_result_state(SUSPEND_ABORTED);
+	}
+}
+
+/* update_image
+ *
+ * Allocate [more] memory and storage for the image.
+ */
+static int update_image(void) 
+{ 
+	int result2, param_used;
+
+	suspend_recalculate_image_contents(0);
+
+	/* Include allowance for growth in pagedir1 while writing pagedir 2 */
+	if (suspend_allocate_extra_pagedir_memory(&pagedir1,
+		pagedir1.pageset_size + extra_pd1_pages_allowance,
+				pageset2_sizelow)) {
+		suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_LOW, 1,
+			"Still need to get more pages for pagedir 1.\n");
+		return 1;
+	}
+
+	thaw_processes(FREEZER_KERNEL_THREADS);
+
+	param_used = main_storage_needed(1, 0);
+	if ((result2 = suspend_active_writer->allocate_storage(param_used))) {
+		suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_LOW, 1,
+			"Allocate storage returned %d. Still need to get more"
+			" storage space for the image proper.\n",
+			result2);
+		storage_allocated = suspend_active_writer->storage_allocated();
+		try_freeze_processes();
+		return 1;
+	}
+
+	/* 
+	 * Allocate remaining storage space, if possible, up to the
+	 * maximum we know we'll need. It's okay to allocate the
+	 * maximum if the writer is the swapwriter, but
+	 * we don't want to grab all available space on an NFS share.
+	 * We therefore ignore the expected compression ratio here,
+	 * thereby trying to allocate the maximum image size we could
+	 * need (assuming compression doesn't expand the image), but
+	 * don't complain if we can't get the full amount we're after.
+	 */
+
+	suspend_active_writer->allocate_storage(
+		min(storage_available, main_storage_needed(0, 1)));
+
+	storage_allocated = suspend_active_writer->storage_allocated();
+
+	/* Allocate the header storage after allocating main storage
+	 * so that the overhead for metadata doesn't change the amount
+	 * of storage needed for the header itself.
+	 */
+
+	param_used = header_storage_needed();
+
+	result2 = suspend_active_writer->allocate_header_space(param_used);
+
+	try_freeze_processes();
+
+	if (result2) {
+		suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_LOW, 1,
+			"Still need to get more storage space for header.\n");
+		return 1;
+	}
+
+	header_space_allocated = param_used;
+
+	suspend_recalculate_image_contents(0);
+
+	suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_LOW, 1,
+		"Amount still needed (%d) > 0:%d. Header: %d < %d: %d,"
+		" Storage allocd: %d < %d + %d: %d.\n",
+			amount_needed(0),
+			(amount_needed(0) > 0),
+			header_space_allocated, header_storage_needed(),
+			header_space_allocated < header_storage_needed(),
+		 	storage_allocated,
+			header_storage_needed(), main_storage_needed(1, 1),
+			storage_allocated <
+			(header_storage_needed() + main_storage_needed(1, 1)));
+
+	suspend_cond_pause(0, NULL);
+
+	return ((amount_needed(0) > 0) ||
+		header_space_allocated < header_storage_needed() ||
+		 storage_allocated < 
+		 (header_storage_needed() + main_storage_needed(1, 1)));
+}
+
+/* attempt_to_freeze
+ * 
+ * Try to freeze processes.
+ */
+
+static int attempt_to_freeze(void)
+{
+	int result;
+	
+	/* Stop processes before checking again */
+	thaw_processes(FREEZER_ALL_THREADS);
+	suspend_prepare_status(CLEAR_BAR, "Freezing processes");
+	result = freeze_processes();
+
+	if (result) {
+		set_result_state(SUSPEND_ABORTED);
+		set_result_state(SUSPEND_FREEZING_FAILED);
+	} else
+		are_frozen = 1;
+
+	return result;
+}
+
+long storage_needed(int use_ecr, int ignore_extra_pd1_allow)
+{
+	return 	(main_storage_needed(use_ecr, ignore_extra_pd1_allow)
+		       + header_storage_needed());
+}
+
+long ram_to_suspend(void)
+{
+	return (1 + 
+		max_t(long, (pagedir1.pageset_size + extra_pd1_pages_allowance - 
+			pageset2_sizelow - extra_pagedir_pages_allocated) / 2, 0) +
+		MIN_FREE_RAM + suspend_memory_for_modules());
+}
+
+/* eat_memory
+ *
+ * Try to free some memory, either to meet hard or soft constraints on the image
+ * characteristics.
+ * 
+ * Hard constraints:
+ * - Pageset1 must be < half of memory;
+ * - We must have enough memory free at resume time to have pageset1
+ *   be able to be loaded in pages that don't conflict with where it has to
+ *   be restored.
+ * Soft constraints
+ * - User specificied image size limit.
+ */
+static int eat_memory(void)
+{
+	int amount_wanted = 0;
+	int free_flags = 0, did_eat_memory = 0;
+	
+	/*
+	 * Note that if we have enough storage space 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.
+	 *
+	 * We freeze processes before beginning, and then unfreeze them if we
+	 * need to eat memory until we think we have enough. If our attempts
+	 * to freeze fail, we give up and abort.
+	 */
+
+	/* -- Stage 1: Freeze Processes -- */
+
+	
+	suspend_recalculate_image_contents(0);
+	amount_wanted = amount_needed(1);
+
+	switch (image_size_limit) {
+		case -1: /* Don't eat any memory */
+			if (amount_wanted > 0) {
+				set_result_state(SUSPEND_ABORTED);
+				set_result_state(SUSPEND_WOULD_EAT_MEMORY);
+			}
+			break;
+		case -2:  /* Free caches only */
+			free_flags = GFP_NOIO | __GFP_HIGHMEM;
+			amount_wanted = 1 << 31; /* As much cache as we can get */
+			break;
+		default:
+			free_flags = GFP_ATOMIC | __GFP_HIGHMEM;
+	}
+		
+	thaw_processes(FREEZER_KERNEL_THREADS);
+
+	/* -- Stage 2: Eat memory -- */
+
+	if (amount_wanted > 0 && !test_result_state(SUSPEND_ABORTED) &&
+			image_size_limit != -1) {
+
+		suspend_prepare_status(CLEAR_BAR, "Seeking to free %dMB of memory.", MB(amount_wanted));
+
+		shrink_all_memory(amount_wanted);
+		suspend_recalculate_image_contents(0);
+
+		did_eat_memory = 1;
+
+		suspend_cond_pause(0, NULL);
+	}
+
+	if (freeze_processes()) {
+		set_result_state(SUSPEND_FREEZING_FAILED);
+		set_result_state(SUSPEND_ABORTED);
+	}
+	
+	if (did_eat_memory) {
+		unsigned long orig_state = get_suspend_state();
+		/* Freeze_processes will call sys_sync too */
+		restore_suspend_state(orig_state);
+		suspend_recalculate_image_contents(0);
+	}
+
+	/* Blank out image size display */
+	suspend_update_status(100, 100, NULL);
+
+	if (!test_result_state(SUSPEND_ABORTED) &&
+	    (amount_needed(0) - extra_pd1_pages_allowance > 0)) {
+		printk("Unable to free sufficient memory to suspend. Still need %d pages.\n",
+			amount_needed(1));
+		display_stats(1, 1);
+		set_result_state(SUSPEND_ABORTED);
+		set_result_state(SUSPEND_UNABLE_TO_FREE_ENOUGH_MEMORY);
+	}
+
+	return 0;
+}
+
+/* suspend_prepare_image
+ *
+ * Entry point to the whole image preparation section.
+ *
+ * We do four things:
+ * - Freeze processes;
+ * - Ensure image size constraints are met;
+ * - Complete all the preparation for saving the image,
+ *   including allocation of storage. The only memory
+ *   that should be needed when we're finished is that
+ *   for actually storing the image (and we know how
+ *   much is needed for that because the modules tell
+ *   us).
+ * - Make sure that all dirty buffers are written out.
+ */
+#define MAX_TRIES 4
+int suspend_prepare_image(void)
+{
+	int result = 1, tries = 0;
+
+	are_frozen = 0;
+
+	header_space_allocated = 0;
+
+	if (attempt_to_freeze())
+		return 1;
+
+	if (!extra_pd1_pages_allowance)
+		get_extra_pd1_allowance();
+
+	storage_available = suspend_active_writer->storage_available();
+
+	if (!storage_available) {
+		printk(KERN_ERR "You need some storage available to be able to suspend.\n");
+		set_result_state(SUSPEND_ABORTED);
+		set_result_state(SUSPEND_NOSTORAGE_AVAILABLE);
+		return 1;
+	}
+
+	do {
+		suspend_prepare_status(CLEAR_BAR, "Preparing Image.");
+	
+		if (eat_memory() || test_result_state(SUSPEND_ABORTED))
+			break;
+
+		result = update_image();
+
+		suspend_cond_pause(0, NULL);
+		
+		tries++;
+
+	} while ((result) && (tries < MAX_TRIES) && (!test_result_state(SUSPEND_ABORTED)) &&
+		(!test_result_state(SUSPEND_UNABLE_TO_FREE_ENOUGH_MEMORY)));
+
+	if (tries == MAX_TRIES) {
+		abort_suspend("Unable to successfully prepare the image.\n");
+		display_stats(1, 0);
+	}
+
+	suspend_cond_pause(1, "Image preparation complete.");
+
+	return result;
+}
diff -urN oldtree/kernel/power/prepare_image.h newtree/kernel/power/prepare_image.h
--- oldtree/kernel/power/prepare_image.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/prepare_image.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,89 @@
+/*
+ * kernel/power/prepare_image.h
+ *
+ * Copyright (C) 2003-2006 Nigel Cunningham <nigel@suspend.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ */
+
+#include <asm/sections.h>
+
+extern int suspend_prepare_image(void);
+extern void suspend_recalculate_image_contents(int storage_available);
+extern long real_nr_free_pages(void);
+extern long image_size_limit;
+extern long pageset1_sizelow, pageset2_sizelow;
+
+struct pageset_sizes_result {
+	long size1; /* Can't be unsigned - breaks MAX function */
+	long size1low;
+	long size2;
+	long size2low;
+};
+
+#ifdef CONFIG_CRYPTO
+extern int suspend_expected_compression_ratio(void);
+#else
+static inline int suspend_expected_compression_ratio(void)
+{
+	return 0;
+};
+#endif
+
+#define MIN_FREE_RAM 2000
+#define MIN_EXTRA_PAGES_ALLOWANCE 500
+
+extern long extra_pd1_pages_allowance;
+extern long storage_needed(int use_ecr, int ignore_extra_p1_allowance);
+extern long ram_to_suspend(void);
+
+#ifdef CONFIG_DEBUG_RODATA
+static inline struct page* rodata_start_page(void)
+{
+	return virt_to_page(&__start_rodata);
+}
+
+static inline struct page* rodata_end_page(void)
+{
+	return virt_to_page(&__end_rodata);
+}
+
+#else
+static inline struct page* rodata_start_page(void)
+{
+	return NULL;
+}
+
+static inline struct page* rodata_end_page(void)
+{
+	return NULL;
+}
+#endif
+
+#ifdef CONFIG_PPC
+static inline struct page* rotext_start_page(void)
+{
+	return virt_to_page(PAGE_OFFSET);
+}
+#else
+static inline struct page* rotext_start_page(void)
+{
+	return virt_to_page(_text);
+}
+#endif
+
+static inline struct page* rotext_end_page(void)
+{
+	return virt_to_page(_etext);
+}
+
+static inline struct page* nosave_start_page(void)
+{
+	return virt_to_page(&__nosave_begin);
+}
+
+static inline struct page* nosave_end_page(void)
+{
+	return virt_to_page(&__nosave_end);
+}
diff -urN oldtree/kernel/power/process.c newtree/kernel/power/process.c
--- oldtree/kernel/power/process.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/power/process.c	2006-10-06 17:28:20.000000000 -0400
@@ -5,7 +5,6 @@
  * Originally from swsusp.
  */
 
-
 #undef DEBUG
 
 #include <linux/smp_lock.h>
@@ -13,12 +12,74 @@
 #include <linux/suspend.h>
 #include <linux/module.h>
 #include <linux/syscalls.h>
+#include <linux/buffer_head.h>
+#include <linux/freezer.h>
+
+unsigned long freezer_state = 0;
+
+#ifdef CONFIG_PM_DEBUG
+#define freezer_message(msg, a...) do { printk(msg, ##a); } while(0)
+#else
+#define freezer_message(msg, a...) do { } while(0)
+#endif
 
 /* 
  * Timeout for stopping processes
  */
 #define TIMEOUT	(20 * HZ)
 
+struct frozen_fs
+{
+	struct list_head fsb_list;
+	struct super_block *sb;
+};
+
+LIST_HEAD(frozen_fs_list);
+
+void freezer_make_fses_rw(void)
+{
+	struct frozen_fs *fs, *next_fs;
+
+	list_for_each_entry_safe(fs, next_fs, &frozen_fs_list, fsb_list) {
+		thaw_bdev(fs->sb->s_bdev, fs->sb);
+
+		list_del(&fs->fsb_list);
+		kfree(fs);
+	}
+}
+
+/* 
+ * Done after userspace is frozen, so there should be no danger of
+ * fses being unmounted while we're in here.
+ */
+int freezer_make_fses_ro(void)
+{
+	struct frozen_fs *fs;
+	struct super_block *sb;
+
+	/* Generate the list */
+	list_for_each_entry(sb, &super_blocks, s_list) {
+		if (!sb->s_root || !sb->s_bdev ||
+		    (sb->s_frozen == SB_FREEZE_TRANS) ||
+		    (sb->s_flags & MS_RDONLY))
+			continue;
+
+		fs = kmalloc(sizeof(struct frozen_fs), GFP_ATOMIC);
+		if (!fs)
+			return 1;
+		fs->sb = sb;
+		list_add_tail(&fs->fsb_list, &frozen_fs_list);
+	};
+
+	/* Do the freezing in reverse order so filesystems dependant
+	 * upon others are frozen in the right order. (Eg loopback
+	 * on ext3). */
+	list_for_each_entry_reverse(fs, &frozen_fs_list, fsb_list)
+		freeze_bdev(fs->sb->s_bdev);
+
+	return 0;
+}
+
 
 static inline int freezeable(struct task_struct * p)
 {
@@ -39,7 +100,7 @@
 	long save;
 	save = current->state;
 	pr_debug("%s entered refrigerator\n", current->comm);
-	printk("=");
+	freezer_message("=");
 
 	frozen_process(current);
 	spin_lock_irq(&current->sighand->siglock);
@@ -86,9 +147,13 @@
 	unsigned long start_time;
 	struct task_struct *g, *p;
 
-	printk( "Stopping tasks: " );
+	user_frozen = test_freezer_state(FREEZER_ON);
+	
+	if (!user_frozen)
+		set_freezer_state(FREEZER_ON);
+
+	freezer_message( "Stopping tasks: " );
 	start_time = jiffies;
-	user_frozen = 0;
 	do {
 		nr_user = todo = 0;
 		read_lock(&tasklist_lock);
@@ -119,8 +184,10 @@
 		read_unlock(&tasklist_lock);
 		todo += nr_user;
 		if (!user_frozen && !nr_user) {
-			sys_sync();
+			freezer_message("Freezing bdevs... ");
+			freezer_make_fses_ro();
 			start_time = jiffies;
+			freezer_message("Freezing kernel threads... ");
 		}
 		user_frozen = !nr_user;
 		yield();			/* Yield is okay here */
@@ -134,41 +201,70 @@
 	 * but it cleans up leftover PF_FREEZE requests.
 	 */
 	if (todo) {
-		printk( "\n" );
-		printk(KERN_ERR " stopping tasks timed out "
+		freezer_message( "\n" );
+		freezer_message(KERN_ERR " stopping tasks timed out "
 			"after %d seconds (%d tasks remaining):\n",
 			TIMEOUT / HZ, todo);
 		read_lock(&tasklist_lock);
 		do_each_thread(g, p) {
 			if (freezeable(p) && !frozen(p))
-				printk(KERN_ERR "  %s\n", p->comm);
+				freezer_message(KERN_ERR "  %s\n", p->comm);
 			cancel_freezing(p);
 		} while_each_thread(g, p);
 		read_unlock(&tasklist_lock);
 		return todo;
 	}
 
-	printk( "|\n" );
+	freezer_message( "|\n" );
 	BUG_ON(in_atomic());
+	set_freezer_state(FREEZING_COMPLETE);
 	return 0;
 }
 
-void thaw_processes(void)
+void thaw_processes(int all)
 {
 	struct task_struct *g, *p;
+	int pass = 0; /* Start on kernel space */
 
-	printk( "Restarting tasks..." );
+	if (!test_freezer_state(FREEZER_ON))
+		return;
+
+	if (!test_freezer_state(FREEZING_COMPLETE))
+		pass++;
+
+	clear_freezer_state(FREEZING_COMPLETE);
+
+	freezer_message( "Restarting tasks... " );
 	read_lock(&tasklist_lock);
-	do_each_thread(g, p) {
-		if (!freezeable(p))
-			continue;
-		if (!thaw_process(p))
-			printk(KERN_INFO " Strange, %s not stopped\n", p->comm );
-	} while_each_thread(g, p);
+	do {
+		do_each_thread(g, p) {
+			/* 
+			 * is_user = 0 if kernel thread or borrowed mm,
+			 * 1 otherwise.
+			 */
+			int is_user = !!(p->mm && !(p->flags & PF_BORROWED_MM));
+			if (!freezeable(p) || (is_user != pass))
+				continue;
+			if (!thaw_process(p))
+				freezer_message(KERN_INFO " Strange, %s not stopped\n", p->comm );
+		} while_each_thread(g, p);
+
+		if (!pass) {
+			read_unlock(&tasklist_lock);
+			freezer_message("Thawing bdevs... ");
+			freezer_make_fses_rw();
+			read_lock(&tasklist_lock);
+		}
+
+		pass++;
+	} while(pass < 2 && all);
 
 	read_unlock(&tasklist_lock);
 	schedule();
-	printk( " done\n" );
+	freezer_message( "Done\n" );
+
+	if (all)
+		clear_freezer_state(FREEZER_ON);
 }
 
 EXPORT_SYMBOL(refrigerator);
diff -urN oldtree/kernel/power/snapshot.c newtree/kernel/power/snapshot.c
--- oldtree/kernel/power/snapshot.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/power/snapshot.c	2006-10-06 17:28:20.000000000 -0400
@@ -34,6 +34,13 @@
 
 #include "power.h"
 
+#ifdef CONFIG_SUSPEND2
+#include "pagedir.h"
+
+extern int suspend2_running;
+int suspend_post_context_save(void);
+#endif
+
 /* List of PBEs used for creating and restoring the suspend image */
 struct pbe *restore_pblist;
 
@@ -195,6 +202,10 @@
 
 unsigned long get_safe_page(gfp_t gfp_mask)
 {
+#ifdef CONFIG_SUSPEND2
+	if (suspend2_running)
+		return suspend_get_nonconflicting_page();
+#endif
 	return (unsigned long)alloc_image_page(gfp_mask, PG_SAFE);
 }
 
@@ -868,6 +879,11 @@
 {
 	unsigned int nr_pages;
 
+#ifdef CONFIG_SUSPEND2
+	if (suspend2_running)
+		return suspend_post_context_save();
+#endif
+
 	pr_debug("swsusp: critical section: \n");
 
 	drain_local_pages();
diff -urN oldtree/kernel/power/storage.c newtree/kernel/power/storage.c
--- oldtree/kernel/power/storage.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/storage.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,304 @@
+/*
+ * kernel/power/storage.c
+ *
+ * Copyright (C) 2005-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Routines for talking to a userspace program that manages storage.
+ *
+ * The kernel side:
+ * - starts the userspace program;
+ * - sends messages telling it when to open and close the connection;
+ * - tells it when to quit;
+ *
+ * The user space side:
+ * - passes messages regarding status;
+ *
+ */
+
+#include <linux/suspend.h>
+#include <linux/freezer.h>
+ 
+#include "sysfs.h"
+#include "modules.h"
+#include "netlink.h"
+#include "storage.h"
+#include "ui.h"
+
+static struct user_helper_data usm_helper_data;
+static struct suspend_module_ops usm_ops;
+static int message_received = 0;
+static int activations = 0;
+static int usm_prepare_count = 0;
+static int storage_manager_last_action = 0;
+static int storage_manager_action = 0;
+       
+static int usm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+	int type;
+	int *data;
+
+	type = nlh->nlmsg_type;
+
+	/* A control message: ignore them */
+	if (type < NETLINK_MSG_BASE)
+		return 0;
+
+	/* Unknown message: reply with EINVAL */
+	if (type >= USM_MSG_MAX)
+		return -EINVAL;
+
+	/* All operations require privileges, even GET */
+	if (security_netlink_recv(skb, CAP_NET_ADMIN))
+		return -EPERM;
+
+	/* Only allow one task to receive NOFREEZE privileges */
+	if (type == NETLINK_MSG_NOFREEZE_ME && usm_helper_data.pid != -1)
+		return -EBUSY;
+
+	data = (int*)NLMSG_DATA(nlh);
+
+	switch (type) {
+		case USM_MSG_SUCCESS:
+		case USM_MSG_FAILED:
+			message_received = type;
+			complete(&usm_helper_data.wait_for_process);
+			break;
+		default:
+			printk("Storage manager doesn't recognise message %d.\n", type);
+	}
+
+	return 1;
+}
+
+int suspend_activate_storage(int force)
+{
+	int tries = 1;
+
+	if (usm_helper_data.pid == -1 || !usm_ops.enabled)
+		return 0;
+
+	message_received = 0;
+	activations++;
+
+	if (activations > 1 && !force)
+		return 0;
+
+	while ((!message_received || message_received == USM_MSG_FAILED) && tries < 2) {
+		suspend_prepare_status(DONT_CLEAR_BAR, "Activate storage attempt %d.\n", tries);
+
+		init_completion(&usm_helper_data.wait_for_process);
+
+		suspend_send_netlink_message(&usm_helper_data,
+			USM_MSG_CONNECT,
+			NULL, 0);
+
+		/* Wait 2 seconds for the userspace process to make contact */
+		wait_for_completion_timeout(&usm_helper_data.wait_for_process, 2*HZ);
+
+		tries++;
+	}
+
+	return 0;
+}
+
+int suspend_deactivate_storage(int force)
+{
+	if (usm_helper_data.pid == -1 || !usm_ops.enabled)
+		return 0;
+	
+	message_received = 0;
+	activations--;
+
+	if (activations && !force)
+		return 0;
+
+	init_completion(&usm_helper_data.wait_for_process);
+
+	suspend_send_netlink_message(&usm_helper_data,
+			USM_MSG_DISCONNECT,
+			NULL, 0);
+
+	wait_for_completion_timeout(&usm_helper_data.wait_for_process, 2*HZ);
+
+	if (!message_received || message_received == USM_MSG_FAILED) {
+		printk("Returning failure disconnecting storage.\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_DEBUG
+static void storage_manager_simulate(void)
+{
+	printk("--- Storage manager simulate ---\n");
+	suspend_prepare_usm();
+	schedule();
+	printk("--- Activate storage 1 ---\n");
+	suspend_activate_storage(1);
+	schedule();
+	printk("--- Deactivate storage 1 ---\n");
+	suspend_deactivate_storage(1);
+	schedule();
+	printk("--- Cleanup usm ---\n");
+	suspend_cleanup_usm();
+	schedule();
+	printk("--- Storage manager simulate ends ---\n");
+}
+#endif
+
+static unsigned long usm_storage_needed(void)
+{
+	return strlen(usm_helper_data.program);
+}
+
+static int usm_save_config_info(char *buf)
+{
+	int len = strlen(usm_helper_data.program);
+	memcpy(buf, usm_helper_data.program, len);
+	return len;
+}
+
+static void usm_load_config_info(char *buf, int size)
+{
+	/* Don't load the saved path if one has already been set */
+	if (usm_helper_data.program[0])
+		return;
+
+	memcpy(usm_helper_data.program, buf, size);
+}
+
+static unsigned long usm_memory_needed(void)
+{
+	/* ball park figure of 32 pages */
+	return (32 * PAGE_SIZE);
+}
+
+/* suspend_prepare_usm
+ */
+int suspend_prepare_usm(void)
+{
+	usm_prepare_count++;
+
+	if (usm_prepare_count > 1 || !usm_ops.enabled)
+		return 0;
+	
+	usm_helper_data.pid = -1;
+
+	if (!*usm_helper_data.program)
+		return 0;
+
+	suspend_netlink_setup(&usm_helper_data);
+
+	if (usm_helper_data.pid == -1)
+		printk("Suspend2 Storage Manager wanted, but couldn't start it.\n");
+
+	suspend_activate_storage(0);
+
+	return (usm_helper_data.pid != -1);
+}
+
+void suspend_cleanup_usm(void)
+{
+	usm_prepare_count--;
+
+	if (usm_helper_data.pid > -1 && !usm_prepare_count) {
+		struct task_struct *t;
+
+		suspend_deactivate_storage(0);
+
+		suspend_send_netlink_message(&usm_helper_data,
+				NETLINK_MSG_CLEANUP, NULL, 0);
+
+		read_lock(&tasklist_lock);
+		if ((t = find_task_by_pid(usm_helper_data.pid)))
+			t->flags &= ~PF_NOFREEZE;
+		read_unlock(&tasklist_lock);
+
+		suspend_netlink_close(&usm_helper_data);
+
+		usm_helper_data.pid = -1;
+	}
+}
+
+static void storage_manager_activate(void)
+{
+	if (storage_manager_action == storage_manager_last_action)
+		return;
+
+	if (storage_manager_action)
+		suspend_prepare_usm();
+	else
+		suspend_cleanup_usm();
+
+	storage_manager_last_action = storage_manager_action;
+}
+
+/*
+ * User interface specific /sys/power/suspend2 entries.
+ */
+
+static struct suspend_sysfs_data sysfs_params[] = {
+	{ SUSPEND2_ATTR("enabled", SYSFS_RW),
+	  SYSFS_INT(&usm_ops.enabled, 0, 1)
+	},
+
+	{ SUSPEND2_ATTR("program", SYSFS_RW),
+	  SYSFS_STRING(usm_helper_data.program, 254, SYSFS_SM_NOT_NEEDED)
+	},
+
+	{ SUSPEND2_ATTR("activate_storage", SYSFS_RW),
+	  SYSFS_INT(&storage_manager_action, 0, 1),
+	  .write_side_effect		= storage_manager_activate,
+	},
+
+#ifdef CONFIG_PM_DEBUG
+	{ SUSPEND2_ATTR("simulate_atomic_copy", SYSFS_RW),
+	  .type				= SUSPEND_SYSFS_DATA_NONE,
+	  .write_side_effect		= storage_manager_simulate,
+	}
+#endif
+};
+
+static struct suspend_module_ops usm_ops = {
+	.type				= MISC_MODULE,
+	.name				= "Userspace Storage Manager",
+	.module				= THIS_MODULE,
+	.storage_needed			= usm_storage_needed,
+	.save_config_info		= usm_save_config_info,
+	.load_config_info		= usm_load_config_info,
+	.memory_needed			= usm_memory_needed,
+};
+       
+/* suspend_usm_sysfs_init
+ * Description: Boot time initialisation for user interface.
+ */
+static __init int suspend_usm_sysfs_init(void)
+{
+	int result, i,
+	    numfiles = sizeof(sysfs_params) / sizeof(struct suspend_sysfs_data);
+
+	if (!(result = suspend_register_module(&usm_ops))) {
+		struct kobject *kobj = make_suspend2_sysdir("storage_manager");
+		for (i=0; i< numfiles; i++)
+			suspend_register_sysfs_file(kobj, &sysfs_params[i]);
+	}
+
+	usm_helper_data.nl = NULL;
+	usm_helper_data.program[0] = '\0';
+	usm_helper_data.pid = -1;
+	usm_helper_data.skb_size = 0;
+	usm_helper_data.pool_limit = 6;
+	usm_helper_data.netlink_id = NETLINK_SUSPEND2_USM;
+	usm_helper_data.name = "userspace storage manager";
+	usm_helper_data.rcv_msg = usm_user_rcv_msg;
+	usm_helper_data.interface_version = 1;
+	usm_helper_data.must_init = 0;
+	init_completion(&usm_helper_data.wait_for_process);
+
+	return result;
+}
+
+late_initcall(suspend_usm_sysfs_init);
diff -urN oldtree/kernel/power/storage.h newtree/kernel/power/storage.h
--- oldtree/kernel/power/storage.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/storage.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,37 @@
+/*
+ * kernel/power/storage.h
+ *
+ * Copyright (C) 2005-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ */
+
+int suspend_prepare_usm(void);
+void suspend_cleanup_usm(void);
+
+#ifdef CONFIG_NET
+int suspend_activate_storage(int force);
+int suspend_deactivate_storage(int force);
+#else
+static inline int suspend_activate_storage(int force)
+{
+	return 0;
+}
+
+static inline int suspend_deactivate_storage(int force)
+{
+	return 0;
+}
+#endif
+
+enum {
+	USM_MSG_BASE = 0x10,
+
+	/* Kernel -> Userspace */
+	USM_MSG_CONNECT = 0x30,
+	USM_MSG_DISCONNECT = 0x31,
+	USM_MSG_SUCCESS = 0x40,
+	USM_MSG_FAILED = 0x41,
+
+	USM_MSG_MAX,
+};
diff -urN oldtree/kernel/power/suspend.c newtree/kernel/power/suspend.c
--- oldtree/kernel/power/suspend.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/suspend.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,954 @@
+/*
+ * kernel/power/suspend.c
+ */
+/** \mainpage Suspend2.
+ *
+ * Suspend2 provides support for saving and restoring an image of
+ * system memory to an arbitrary storage device, either on the local computer,
+ * or across some network. The support is entirely OS based, so Suspend2 
+ * works without requiring BIOS, APM or ACPI support. The vast majority of the
+ * code is also architecture independant, so it should be very easy to port
+ * the code to new architectures. Suspend includes support for SMP, 4G HighMem
+ * and preemption. Initramfses and initrds are also supported.
+ *
+ * Suspend2 uses a modular design, in which the method of storing the image is
+ * completely abstracted from the core code, as are transformations on the data
+ * such as compression and/or encryption (multiple 'modules' can be used to
+ * provide arbitrary combinations of functionality). The user interface is also
+ * modular, so that arbitrarily simple or complex interfaces can be used to
+ * provide anything from debugging information through to eye candy.
+ * 
+ * \section Copyright
+ *
+ * Suspend2 is released under the GPLv2.
+ *
+ * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu><BR>
+ * Copyright (C) 1998,2001,2002 Pavel Machek <pavel@suse.cz><BR>
+ * Copyright (C) 2002-2003 Florent Chabaud <fchabaud@free.fr><BR>
+ * Copyright (C) 2002-2006 Nigel Cunningham <nigel@suspend2.net><BR>
+ *
+ * \section Credits
+ * 
+ * Nigel would like to thank the following people for their work:
+ * 
+ * Pavel Machek <pavel@ucw.cz><BR>
+ * 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><BR> 
+ * Support the possibility of hardware state restoring.
+ *
+ * Raph <grey.havens@earthling.net><BR>
+ * Support for preserving states of network devices and virtual console
+ * (including X and svgatextmode)
+ *
+ * Kurt Garloff <garloff@suse.de><BR>
+ * 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><BR>
+ * Fixed runaway init
+ *
+ * Jeff Snyder <je4d@pobox.com><BR>
+ * ACPI patch
+ *
+ * Nathan Friess <natmanz@shaw.ca><BR>
+ * Some patches.
+ *
+ * Michael Frank <mhf@linuxmail.org><BR>
+ * Extensive testing and help with improving stability. Nigel was constantly
+ * amazed by the quality and quantity of Michael's help.
+ *
+ * Bernard Blackham <bernard@blackham.com.au><BR>
+ * Web page & Wiki administration, some coding. Another person without whom
+ * Suspend would not be where it is.
+ *
+ * ..and of course the myriads of Suspend2 users who have helped diagnose
+ * and fix bugs, made suggestions on how to improve the code, proofread
+ * documentation, and donated time and money.
+ *
+ * Thanks also to corporate sponsors:
+ *
+ * <B>Cyclades.com.</B> Nigel's employers from Dec 2004, who allow him to work on
+ * Suspend and PM related issues on company time.
+ * 
+ * <B>LinuxFund.org.</B> Sponsored Nigel's work on Suspend for four months Oct 2003
+ * to Jan 2004.
+ *
+ * <B>LAC Linux.</B> Donated P4 hardware that enabled development and ongoing
+ * maintenance of SMP and Highmem support.
+ *
+ * <B>OSDL.</B> Provided access to various hardware configurations, make occasional
+ * small donations to the project.
+ */
+
+#define SUSPEND_MAIN_C
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/version.h>
+#include <linux/reboot.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/freezer.h>
+#include <linux/utsrelease.h>
+#include <linux/swap.h> /* For LRU unlinking prototypes */
+#include <linux/cpu.h>
+#include <asm/uaccess.h>
+#include <asm/setup.h>
+
+#include "suspend.h"
+#include "modules.h"
+#include "sysfs.h"
+#include "pageflags.h"
+#include "prepare_image.h"
+#include "io.h"
+#include "ui.h"
+#include "extent.h"
+#include "power_off.h"
+#include "storage.h"
+
+#ifdef  CONFIG_X86
+#include <asm/i387.h> /* for kernel_fpu_end */
+#endif
+
+int suspend2_running;
+ 
+/* Variables to be preserved over suspend */
+long pageset1_sizelow = 0, pageset2_sizelow = 0, image_size_limit = 0;
+unsigned long suspend_orig_mem_free = 0;
+
+static char suspend_core_version[] = SUSPEND_CORE_VERSION;
+
+extern __nosavedata char suspend_resume_commandline[COMMAND_LINE_SIZE];
+
+#ifdef CONFIG_SUSPEND2_REPLACE_SWSUSP
+unsigned long suspend_action = 1 << SUSPEND_REPLACE_SWSUSP;
+#else
+unsigned long suspend_action = 0;
+#endif
+unsigned long suspend_result = 0;
+unsigned long suspend_debug_state = 0;
+
+int suspend2_in_suspend __nosavedata;
+extern void copyback_post(void);
+extern int suspend2_suspend(void);
+extern int extra_pd1_pages_used;
+
+static int orig_system_state;
+extern int driver_model_beeping;
+
+/* 
+ * ---  Variables -----
+ * 
+ * The following are used by the arch specific low level routines 
+ * and only needed if suspend2 is compiled in. Other variables,
+ * used by the freezer even if suspend2 is not compiled in, are
+ * found in process.c
+ */
+
+/*! How long I/O took. */
+int suspend_io_time[2][2];
+
+/* Compression ratio */
+__nosavedata unsigned long bytes_in = 0, bytes_out = 0;
+
+/*! Pageset metadata. */
+struct pagedir pagedir1 = { 0, 0}, pagedir2 = { 0, 0}; 
+
+/* Suspend2 variables used by built-in routines. */
+
+/*! The number of suspends we have started (some may have been cancelled) */
+unsigned int nr_suspends = 0;
+
+/* 
+ * For resume2= kernel option. It's pointless to compile
+ * suspend2 without any writers, but compilation shouldn't
+ * fail if you do.
+ */
+
+unsigned long suspend_state = ((1 << SUSPEND_BOOT_TIME) |
+		(1 << SUSPEND_RESUME_NOT_DONE) | (1 << SUSPEND_IGNORE_LOGLEVEL));
+
+mm_segment_t	oldfs;
+
+char resume2_file[256] = CONFIG_SUSPEND2_DEFAULT_RESUME2;
+#ifdef CONFIG_SOFTWARE_SUSPEND
+extern char resume_file[256];
+#endif
+
+static atomic_t actions_running;
+
+extern int block_dump;
+
+int block_dump_save;
+
+/*
+ * Basic clean-up routine.
+ */
+void suspend_finish_anything(int finishing_cycle)
+{
+	if (atomic_dec_and_test(&actions_running)) {
+		suspend_cleanup_modules(finishing_cycle);
+		suspend_put_modules();
+		clear_suspend_state(SUSPEND_RUNNING);
+	}
+
+	set_fs(oldfs);
+
+	if (finishing_cycle)
+		block_dump = block_dump_save;
+}
+
+/*
+ * Basic set-up routine.
+ */
+int suspend_start_anything(int starting_cycle)
+{
+	oldfs = get_fs();
+
+	if (atomic_add_return(1, &actions_running) == 1) {
+       		set_fs(KERNEL_DS);
+
+		set_suspend_state(SUSPEND_RUNNING);
+
+		if (suspend_get_modules()) {
+			printk(name_suspend "Get modules failed!\n");
+			clear_suspend_state(SUSPEND_RUNNING);
+			set_fs(oldfs);
+			return -EBUSY;
+		}
+
+		if (suspend_initialise_modules(starting_cycle)) {
+			printk(name_suspend "Initialise modules failed!\n");
+			suspend_finish_anything(starting_cycle);
+			return -EBUSY;
+		}
+
+		if (starting_cycle) {
+			block_dump_save = block_dump;
+			block_dump = 0;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * save_image
+ * 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      : suspend_suspend_2
+ */
+static void save_image(void)
+{
+	int temp_result;
+
+	suspend_message(SUSPEND_ANY_SECTION, SUSPEND_LOW, 1,
+		" - Final values: %d and %d.\n",
+		pagedir1.pageset_size, 
+		pagedir2.pageset_size);
+
+	suspend_cond_pause(1, "About to write pagedir2.");
+
+	temp_result = write_pageset(&pagedir2, 2);
+	
+	if (temp_result == -1 || test_result_state(SUSPEND_ABORTED))
+		goto backout;
+
+	suspend_cond_pause(1, "About to copy pageset 1.");
+
+	if (test_result_state(SUSPEND_ABORTED))
+		goto backout;
+
+	suspend_deactivate_storage(1);
+
+	suspend_prepare_status(DONT_CLEAR_BAR, "Doing atomic copy.");
+	
+	suspend2_running = 1; /* For the swsusp code we use :< */
+
+	suspend2_in_suspend = 1;
+	
+	if (device_suspend(PMSG_FREEZE)) {
+		set_result_state(SUSPEND_DEVICE_REFUSED);
+		set_result_state(SUSPEND_ABORTED);
+		goto backout;
+	}
+	
+	if (suspend2_suspend()) {
+		device_resume();
+		return;
+	}
+
+	suspend2_running = 0;
+
+	device_resume();
+	
+	/* Resume time? */
+	if (!suspend2_in_suspend) {
+		copyback_post();
+		return;
+	}
+
+	/* Nope. Suspending. So, see if we can save the image... */
+	if (!save_image_part1()) {
+		suspend_power_down();
+
+		if (read_pageset2(1))
+			panic("Attempt to reload pagedir 2 failed. Try rebooting.");
+
+		if (!test_result_state(SUSPEND_ABORT_REQUESTED) &&
+		    !test_action_state(SUSPEND_TEST_FILTER_SPEED) &&
+		    !test_action_state(SUSPEND_TEST_BIO) &&
+		    suspend_powerdown_method != PM_SUSPEND_MEM)
+			printk(KERN_EMERG name_suspend
+				"Suspend failed, trying to recover...\n");
+		barrier();
+		mb();
+	}
+
+	return;
+	
+backout:
+	return;
+}
+
+/*
+ * Save the second part of the image.
+ */
+int save_image_part1(void)
+{
+	int temp_result;
+
+	if (suspend_activate_storage(1))
+		panic("Failed to reactivate our storage.");
+	
+	suspend_update_status(pagedir2.pageset_size,
+			pagedir1.pageset_size + pagedir2.pageset_size,
+			NULL);
+	
+	if (test_result_state(SUSPEND_ABORTED))
+		goto abort_reloading_pagedir_two;
+
+	suspend_cond_pause(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);
+
+	/* We didn't overwrite any memory, so no reread needs to be done. */
+	if (test_action_state(SUSPEND_TEST_FILTER_SPEED))
+		return -1;
+
+	if (temp_result == -1 || test_result_state(SUSPEND_ABORTED))
+		goto abort_reloading_pagedir_two;
+
+	suspend_cond_pause(1, "About to write header.");
+
+	if (test_result_state(SUSPEND_ABORTED))
+		goto abort_reloading_pagedir_two;
+
+	temp_result = write_image_header();
+
+	if (test_action_state(SUSPEND_TEST_BIO))
+		return -1;
+
+	if (temp_result || (test_result_state(SUSPEND_ABORTED)))
+		goto abort_reloading_pagedir_two;
+
+	suspend_cond_pause(1, "About to power down or reboot.");
+
+	return 0;
+
+abort_reloading_pagedir_two:
+	temp_result = read_pageset2(1);
+
+	/* If that failed, we're sunk. Panic! */
+	if (temp_result)
+		panic("Attempt to reload pagedir 2 while aborting "
+				"a suspend failed.");
+
+	return -1;		
+
+}
+
+static int io_MB_per_second(int read_write)
+{
+	if (!suspend_io_time[read_write][1])
+		return 0;
+
+	return MB((unsigned long) suspend_io_time[read_write][0]) * HZ /
+		suspend_io_time[read_write][1];
+}
+
+/* get_debug_info
+ * Functionality:	Store debug info in a buffer.
+ * Called from:		suspend2_try_suspend.
+ */
+#define SNPRINTF(a...) 	len += snprintf_used(((char *)buffer) + len, \
+		count - len - 1, ## a)
+static int get_suspend_debug_info(const char *buffer, int count)
+{
+	int len = 0;
+
+	SNPRINTF("Suspend2 debugging info:\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__);
+	SNPRINTF("- Attempt number : %d\n", nr_suspends);
+	SNPRINTF("- Parameters     : %ld %ld %ld %d %d %ld\n",
+			suspend_result,
+			suspend_action,
+			suspend_debug_state,
+			suspend_default_console_level,
+			image_size_limit,
+			suspend_powerdown_method);
+	SNPRINTF("- Overall expected compression percentage: %d.\n",
+			100 - suspend_expected_compression_ratio());
+	len+= suspend_print_module_debug_info(((char *) buffer) + len, 
+			PAGE_SIZE - len - 1);
+	if (suspend_io_time[0][1]) {
+		if ((io_MB_per_second(0) < 5) || (io_MB_per_second(1) < 5)) {
+			SNPRINTF("- I/O speed: Write %d KB/s",
+			  (KB((unsigned long) suspend_io_time[0][0]) * HZ /
+			  suspend_io_time[0][1]));
+			if (suspend_io_time[1][1])
+				SNPRINTF(", Read %d KB/s",
+				  (KB((unsigned long) suspend_io_time[1][0]) * HZ /
+				  suspend_io_time[1][1]));
+		} else {
+			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
+		SNPRINTF("- No I/O speed stats available.\n");
+	SNPRINTF("- Extra pages    : %d used/%d.\n",
+			extra_pd1_pages_used, extra_pd1_pages_allowance);
+
+	return len;
+}
+
+static int allocate_bitmaps(void)
+{
+	if (allocate_dyn_pageflags(&in_use_map) ||
+	    allocate_dyn_pageflags(&pageset1_map) ||
+	    allocate_dyn_pageflags(&pageset1_copy_map) ||
+	    allocate_dyn_pageflags(&pageset2_map) ||
+	    allocate_dyn_pageflags(&pageset2_rw_map))
+		return 1;
+
+	return 0;
+}
+
+static void free_metadata(void)
+{
+	free_dyn_pageflags(&pageset1_map);
+	free_dyn_pageflags(&pageset1_copy_map);
+	free_dyn_pageflags(&pageset2_map);
+	free_dyn_pageflags(&pageset2_rw_map);
+	free_dyn_pageflags(&in_use_map);
+}
+
+static int check_still_keeping_image(void)
+{
+	if (test_action_state(SUSPEND_KEEP_IMAGE)) {
+		printk("Image already stored: powering down immediately.");
+		suspend_power_down();
+		return 1;	/* Just in case we're using S3 */
+	}
+
+	printk("Invalidating previous image.\n");
+	suspend_active_writer->invalidate_image();
+
+	return 0;
+}
+
+static int suspend_init(void)
+{
+	suspend_result = 0;
+
+	printk(name_suspend "Initiating a software suspend cycle.\n");
+
+	nr_suspends++;
+	clear_suspend_state(SUSPEND_NOW_RESUMING);
+	
+	orig_system_state = system_state;
+	
+	suspend_io_time[0][0] = suspend_io_time[0][1] = 
+		suspend_io_time[1][0] =
+		suspend_io_time[1][1] = 0;
+
+	free_metadata();	/* We might have kept it */
+
+	if (!test_suspend_state(SUSPEND_CAN_SUSPEND))
+		return 0;
+	
+	if (allocate_bitmaps())
+		return 0;
+	
+	suspend_prepare_console();
+	disable_nonboot_cpus();
+
+	return 1;
+}
+
+void suspend_cleanup(int had_pmsem)
+{
+	int i = 0;
+	char *buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+
+	if (buffer)
+		i = get_suspend_debug_info(buffer, PAGE_SIZE);
+
+	suspend_free_extra_pagedir_memory();
+	
+	pagedir1.pageset_size = pagedir2.pageset_size = 0;
+
+	system_state = orig_system_state;
+
+	thaw_processes(FREEZER_KERNEL_THREADS);
+
+#ifdef CONFIG_SUSPEND2_KEEP_IMAGE
+	if (test_action_state(SUSPEND_KEEP_IMAGE) &&
+	    !test_result_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
+		if (suspend_active_writer)
+			suspend_active_writer->invalidate_image();
+
+	free_metadata();
+
+	if (buffer && i) {
+		/* Printk can only handle 1023 bytes, including
+		 * its level mangling. */
+		for (i = 0; i < 3; i++)
+			printk("%s", buffer + (1023 * i));
+		free_page((unsigned long) buffer);
+		buffer = NULL;
+	}
+
+	thaw_processes(FREEZER_ALL_THREADS);
+	enable_nonboot_cpus();
+	suspend_cleanup_console();
+	suspend2_running = 0;
+	if (!had_pmsem)
+		up(&pm_sem);
+}
+
+static int can_suspend(int had_pmsem)
+{
+	if (!had_pmsem && down_trylock(&pm_sem)) {
+		set_result_state(SUSPEND_ABORTED);
+		set_result_state(SUSPEND_PM_SEM);
+		return 0;
+	}
+
+	if (!test_suspend_state(SUSPEND_CAN_SUSPEND))
+		suspend_attempt_to_parse_resume_device();
+
+	if (!test_suspend_state(SUSPEND_CAN_SUSPEND)) {
+		printk(name_suspend "Software suspend is disabled.\n"
+			"This may be because you haven't put something along "
+			"the lines of\n\nresume2=swap:/dev/hda1\n\n"
+			"in lilo.conf or equivalent. (Where /dev/hda1 is your "
+			"swap partition).\n");
+		set_result_state(SUSPEND_ABORTED);
+		if (!had_pmsem)
+			up(&pm_sem);
+		return 0;
+	}
+	
+	return 1;
+}
+
+/*
+ * suspend_main
+ * 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_suspend, 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 suspend_main(int had_pmsem)
+{
+	if (suspend_activate_storage(0))
+		return;
+
+	if (!can_suspend(had_pmsem))
+		goto cleanup_deactivate_storage;
+
+	/*
+	 * If kept image and still keeping image and suspending to RAM, we will 
+	 * return 1 after suspending and resuming (provided the power doesn't
+	 * run out.
+	 */
+	if (test_result_state(SUSPEND_KEPT_IMAGE) && check_still_keeping_image()) 
+		goto cleanup;
+
+
+	if (suspend_init() && !suspend_prepare_image() && !test_result_state(SUSPEND_ABORTED) &&
+		!test_action_state(SUSPEND_FREEZER_TEST)) {
+		suspend_prepare_status(DONT_CLEAR_BAR, "Starting to save the image..");
+		unlink_lru_lists();
+		save_image();
+		relink_lru_lists();
+	}
+	
+cleanup:
+	suspend_cleanup(had_pmsem);
+cleanup_deactivate_storage:
+	suspend_deactivate_storage(0);
+}
+
+/* image_exists_read
+ * 
+ * Return 0 or 1, depending on whether an image is found.
+ * Incoming buffer is PAGE_SIZE and result is guaranteed
+ * to be far less than that, so we don't worry about
+ * overflow.
+ */
+static int image_exists_read(const char *page, int count)
+{
+	int len = 0;
+	char *result;
+	
+	if (suspend_activate_storage(0))
+		return count;
+
+	if (!test_suspend_state(SUSPEND_RESUME_DEVICE_OK))
+		suspend_attempt_to_parse_resume_device();
+
+	if (!suspend_active_writer) {
+		len = sprintf((char *) page, "-1\n");
+	} else {
+		result = get_have_image_data();
+		if (result) {
+			len = sprintf((char *) page, "%s",  result);
+			free_page((unsigned long) result);
+		}
+	}
+
+	suspend_deactivate_storage(0);
+
+	return len;
+}
+
+/* image_exists_write
+ * 
+ * Invalidate an image if one exists.
+ */
+static int image_exists_write(const char *buffer, int count)
+{
+	if (suspend_activate_storage(0))
+		return count;
+
+	if (suspend_active_writer && suspend_active_writer->image_exists())
+		suspend_active_writer->invalidate_image();
+
+	suspend_deactivate_storage(0);
+
+	return count;
+}
+
+/*
+ * Core sysfs entries that aren't built in.
+ *
+ * This array contains entries that are automatically registered at
+ * boot. Modules and the console code register their own entries separately.
+ */
+static struct suspend_sysfs_data sysfs_params[] = {
+	{ SUSPEND2_ATTR("driver_model_beeping", SYSFS_RW),
+	  SYSFS_INT(&driver_model_beeping, 0, 1)
+	},
+
+	{ SUSPEND2_ATTR("debug_info", SYSFS_READONLY),
+	  SYSFS_CUSTOM(get_suspend_debug_info, NULL, SYSFS_SM_NOT_NEEDED)
+	},
+	
+	{ SUSPEND2_ATTR("extra_pages_allowance", SYSFS_RW),
+	  SYSFS_UL(&extra_pd1_pages_allowance, 0, INT_MAX)
+	},
+	
+	{ SUSPEND2_ATTR("ignore_rootfs", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_IGNORE_ROOTFS)
+	},
+	
+	{ SUSPEND2_ATTR("image_exists", SYSFS_RW),
+	  SYSFS_CUSTOM(image_exists_read, image_exists_write,
+			  SYSFS_NEEDS_FOR_BOTH)
+	},
+
+	{ SUSPEND2_ATTR("image_size_limit", SYSFS_RW),
+	  SYSFS_LONG(&image_size_limit, -2, INT_MAX)
+	},
+
+	{ SUSPEND2_ATTR("last_result", SYSFS_READONLY),
+	  SYSFS_UL(&suspend_result, 0, 0)
+	},
+	
+	{ SUSPEND2_ATTR("reboot", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_REBOOT)
+	},
+
+#ifdef CONFIG_SOFTWARE_SUSPEND
+	{ SUSPEND2_ATTR("replace_swsusp", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_REPLACE_SWSUSP)
+	},
+#endif
+
+	{ SUSPEND2_ATTR("resume2", SYSFS_RW),
+	  SYSFS_STRING(resume2_file, 255, SYSFS_NEEDS_FOR_WRITE),
+	  .write_side_effect = attempt_to_parse_resume_device2,
+	},
+
+	{ SUSPEND2_ATTR("resume_commandline", SYSFS_RW),
+	  SYSFS_STRING(suspend_resume_commandline, COMMAND_LINE_SIZE,
+			  SYSFS_SM_NOT_NEEDED)
+	},
+
+	{ SUSPEND2_ATTR("version", SYSFS_READONLY),
+	  SYSFS_STRING(suspend_core_version, 0, SYSFS_SM_NOT_NEEDED)
+	},
+
+#ifdef CONFIG_PM_DEBUG
+	{ SUSPEND2_ATTR("freezer_test", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_FREEZER_TEST)
+	},
+
+	{ SUSPEND2_ATTR("test_bio", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_TEST_BIO)
+	},
+
+	{ SUSPEND2_ATTR("test_filter_speed", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_TEST_FILTER_SPEED)
+	},
+
+	{ SUSPEND2_ATTR("slow", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_SLOW)
+	},
+
+	{ SUSPEND2_ATTR("no_pageset2", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_NO_PAGESET2)
+	},
+#endif
+	  
+#if defined(CONFIG_ACPI)
+	{ SUSPEND2_ATTR("powerdown_method", SYSFS_RW),
+	  SYSFS_UL(&suspend_powerdown_method, 0, 5)
+	},
+#endif
+
+#ifdef CONFIG_SUSPEND2_KEEP_IMAGE
+	{ SUSPEND2_ATTR("keep_image", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_KEEP_IMAGE)
+	},
+#endif
+};
+ 
+static __init int core_load(void)
+{
+	int i,
+	    numfiles = sizeof(sysfs_params) / sizeof(struct suspend_sysfs_data);
+
+	printk("Suspend2 Core.\n");
+
+	suspend_initialise_module_lists();
+
+	for (i=0; i< numfiles; i++)
+		suspend_register_sysfs_file(&suspend2_subsys.kset.kobj,
+				&sysfs_params[i]);
+
+#ifdef CONFIG_SOFTWARE_SUSPEND
+	/* Overriding resume2= with resume=? */
+	if (test_action_state(SUSPEND_REPLACE_SWSUSP) && resume_file[0])
+		strncpy(resume2_file, resume_file, 256);
+#endif
+
+	return 0;
+}
+
+/*
+ * 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.
+ */
+int suspend_resume(void)
+{
+	int read_image_result = 0;
+
+	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 1;
+	}
+	
+	if (!resume2_file[0])
+		printk(KERN_WARNING name_suspend
+			"You need to use a resume2= command line parameter to "
+			"tell Suspend2 where to look for an image.\n");
+
+	suspend_activate_storage(0);
+
+	if (!(test_suspend_state(SUSPEND_RESUME_DEVICE_OK)) &&
+		!suspend_attempt_to_parse_resume_device()) {
+		/* 
+		 * Without a usable storage device we can do nothing - 
+		 * even if noresume is given
+		 */
+
+		if (!suspend_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");
+		suspend_deactivate_storage(0);
+		return 1;
+	}
+
+	suspend_orig_mem_free = real_nr_free_pages();
+
+	read_image_result = read_pageset1(); /* non fatal error ignored */
+
+	if (test_suspend_state(SUSPEND_NORESUME_SPECIFIED))
+		printk(KERN_WARNING name_suspend "Resuming disabled as requested.\n");
+
+	suspend_deactivate_storage(0);
+	
+	if (read_image_result) {
+		printk(KERN_WARNING name_suspend "Read image returned %d.\n", read_image_result);
+		return 1;
+	}
+
+	suspend2_running = 1;
+
+	suspend_atomic_restore();
+
+	BUG();
+
+	return 0;
+}
+
+/* -- Functions for kickstarting a suspend or resume --- */
+
+/*
+ * Check if we have an image and if so try to resume.
+ */
+void __suspend_try_resume(void)
+{
+	set_suspend_state(SUSPEND_TRYING_TO_RESUME);
+	clear_suspend_state(SUSPEND_RESUME_NOT_DONE);
+
+	suspend_resume();
+
+	clear_suspend_state(SUSPEND_IGNORE_LOGLEVEL);
+	clear_suspend_state(SUSPEND_TRYING_TO_RESUME);
+}
+
+/* Wrapper for when called from init/do_mounts.c */
+void suspend2_try_resume(void)
+{
+	if (suspend_start_anything(0))
+		return;
+
+	down(&pm_sem);
+	__suspend_try_resume();
+
+	/* 
+	 * For initramfs, we have to clear the boot time
+	 * flag after trying to resume
+	 */
+	clear_suspend_state(SUSPEND_BOOT_TIME);
+
+	up(&pm_sem);
+
+	suspend_finish_anything(0);
+}
+
+/*
+ * suspend2_try_suspend
+ * Functionality   : Wrapper around suspend_main.
+ * Called From     : drivers/acpi/sleep/main.c
+ *                   kernel/reboot.c
+ */
+void suspend2_try_suspend(int have_pmsem)
+{
+	if (suspend_start_anything(0))
+		return;
+
+	suspend_main(0);
+
+	suspend_finish_anything(0);
+}
+
+/* --  Commandline Parameter Handling ---
+ *
+ * Resume setup: obtain the storage device.
+ */
+static int __init resume2_setup(char *str)
+{
+	if (!*str)
+		return 0;
+	
+	strncpy(resume2_file, str, 255);
+	return 0;
+}
+
+/*
+ * Allow the user to set the debug parameter from lilo, prior to resuming.
+ */
+/*
+ * Allow the user to specify that we should ignore any image found and
+ * invalidate the image if necesssary. This is equivalent to running
+ * the task queue and a sync and then turning off the power. The same
+ * precautions should be taken: fsck if you're not journalled.
+ */
+static int __init noresume2_setup(char *str)
+{
+	set_suspend_state(SUSPEND_NORESUME_SPECIFIED);
+	return 0;
+}
+
+static int __init suspend_retry_resume_setup(char *str)
+{
+	set_suspend_state(SUSPEND_RETRY_RESUME);
+	return 0;
+}
+
+__setup("noresume2", noresume2_setup);
+__setup("resume2=", resume2_setup);
+__setup("suspend_retry_resume", suspend_retry_resume_setup);
+
+late_initcall(core_load);
+EXPORT_SYMBOL(suspend_state);
diff -urN oldtree/kernel/power/suspend.h newtree/kernel/power/suspend.h
--- oldtree/kernel/power/suspend.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/suspend.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,62 @@
+/*
+ * kernel/power/suspend.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * It contains declarations used throughout swsusp.
+ *
+ */
+
+#ifndef KERNEL_POWER_SUSPEND_H
+#define KERNEL_POWER_SUSPEND_H
+
+#include <linux/delay.h>
+#include <linux/bootmem.h>
+
+#define SUSPEND_CORE_VERSION "2.2.8"
+#define name_suspend "Suspend2 " SUSPEND_CORE_VERSION ": "
+
+extern __nosavedata struct pbe *restore_pblist;
+extern int save_image_part1(void);
+extern int suspend_atomic_restore(void);
+extern void suspend_main(int had_pmsem);
+
+extern unsigned long suspend_orig_mem_free;
+
+#define KB(x) ((x) << (PAGE_SHIFT - 10))
+#define MB(x) ((x) >> (20 - PAGE_SHIFT))
+
+extern int suspend_start_anything(int starting_cycle);
+extern void suspend_finish_anything(int finishing_cycle);
+
+#ifdef CONFIG_PM_DEBUG
+#define set_debug_state(bit) (test_and_set_bit(bit, &suspend_debug_state))
+#define clear_debug_state(bit) (test_and_clear_bit(bit, &suspend_debug_state))
+#else
+#define set_debug_state(bit) (0)
+#define clear_debug_state(bit) (0)
+#endif
+
+#define set_result_state(bit) (test_and_set_bit(bit, &suspend_result))
+#define clear_result_state(bit) (test_and_clear_bit(bit, &suspend_result))
+
+enum {
+	SUSPEND_ABORTED,
+	SUSPEND_ABORT_REQUESTED,
+	SUSPEND_NOSTORAGE_AVAILABLE,
+	SUSPEND_INSUFFICIENT_STORAGE,
+	SUSPEND_FREEZING_FAILED,
+	SUSPEND_UNEXPECTED_ALLOC,
+	SUSPEND_KEPT_IMAGE,
+	SUSPEND_WOULD_EAT_MEMORY,
+	SUSPEND_UNABLE_TO_FREE_ENOUGH_MEMORY,
+	SUSPEND_ENCRYPTION_SETUP_FAILED,
+	SUSPEND_PM_SEM,
+	SUSPEND_DEVICE_REFUSED,
+};
+
+extern unsigned int nr_suspends;
+extern char resume2_file[256];
+#endif
diff -urN oldtree/kernel/power/suspend_block_io.c newtree/kernel/power/suspend_block_io.c
--- oldtree/kernel/power/suspend_block_io.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/suspend_block_io.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,1085 @@
+/*
+ * kernel/power/suspend_block_io.c
+ *
+ * Copyright 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * Distributed under GPLv2.
+ * 
+ * This file contains block io functions for suspend2. These are
+ * used by the swapwriter and it is planned that they will also
+ * be used by the NFSwriter.
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/syscalls.h>
+#include <linux/suspend.h>
+
+#include "suspend.h"
+#include "sysfs.h"
+#include "modules.h"
+#include "prepare_image.h"
+#include "block_io.h"
+#include "ui.h"
+
+/* Bits in struct io_info->flags */
+enum {
+	IO_AWAITING_READ,
+	IO_AWAITING_SUBMIT,
+	IO_AWAITING_CLEANUP,
+};
+
+#define MAX_OUTSTANDING_IO 2048
+
+/*
+ *
+ *     IO in progress information storage and helpers
+ *
+ */
+
+struct io_info {
+	struct bio *sys_struct;
+	sector_t block[MAX_BUF_PER_PAGE];
+	struct page *buffer_page;
+	struct page *data_page;
+	unsigned long flags;
+	struct block_device *dev;
+	struct list_head list;
+	int readahead_index;
+};
+
+/*
+ * submit_params
+ */
+struct submit_params {
+	swp_entry_t swap_address;
+	struct page *page;
+	struct block_device *dev;
+	sector_t block[MAX_BUF_PER_PAGE];
+	int readahead_index;
+	struct submit_params *next;
+	int printme;
+};
+
+/* Locks separated to allow better SMP support.
+ * An io_struct moves through the lists as follows.
+ * free -> submit_batch -> busy -> ready_for_cleanup -> free
+ */
+static LIST_HEAD(ioinfo_free);
+static DEFINE_SPINLOCK(ioinfo_free_lock);
+
+static LIST_HEAD(ioinfo_ready_for_cleanup);
+static DEFINE_SPINLOCK(ioinfo_ready_lock);
+
+static LIST_HEAD(ioinfo_submit_batch);
+static DEFINE_SPINLOCK(ioinfo_submit_lock);
+
+static LIST_HEAD(ioinfo_busy);
+static DEFINE_SPINLOCK(ioinfo_busy_lock);
+
+static atomic_t submit_batch;
+static int submit_batch_size = 64;
+static int submit_batched(void);
+
+/* [Max] number of I/O operations pending */
+static atomic_t outstanding_io;
+static int max_outstanding_io = 0;
+static atomic_t buffer_allocs, buffer_frees;
+
+/* [Max] number of pages used for above struct */
+static int infopages = 0;
+static int maxinfopages = 0;
+
+static int extra_page_forward = 0;
+
+static volatile unsigned long suspend_readahead_flags[
+	(MAX_OUTSTANDING_IO + BITS_PER_LONG - 1) / BITS_PER_LONG];
+static spinlock_t suspend_readahead_flags_lock = SPIN_LOCK_UNLOCKED;
+static struct page *suspend_readahead_pages[MAX_OUTSTANDING_IO];
+static int readahead_index, readahead_submit_index;
+
+static int current_stream;
+struct extent_iterate_saved_state suspend_writer_posn_save[3];
+
+/* Pointer to current entry being loaded/saved. */
+struct extent_iterate_state suspend_writer_posn;
+
+/* Not static, so that the allocators can setup and complete
+ * writing the header */
+char *suspend_writer_buffer;
+int suspend_writer_buffer_posn;
+
+int suspend_read_fd;
+
+static unsigned long nr_schedule_calls[8];
+
+static char *sch_caller[] = {
+	"get_io_info_struct #1    ",
+	"get_io_info_struct #2    ",
+	"get_io_info_struct #3    ",
+	"suspend_finish_all_io    ",
+	"wait_on_one_page         ",
+	"submit                   ",
+	"start_one                ",
+	"suspend_wait_on_readahead",
+};
+
+static struct suspend_bdev_info *suspend_devinfo;
+
+int suspend_header_bytes_used = 0;
+
+/*
+ * suspend_reset_io_stats
+ *
+ * Description:	Reset all our sanity-checking statistics.
+ */
+static void suspend_reset_io_stats(void)
+{
+	int i;
+	
+	max_outstanding_io = 0;
+	maxinfopages = 0;
+	
+	for (i = 0; i < 8; i++)
+		nr_schedule_calls[i] = 0;
+}
+
+/*
+ * suspend_check_io_stats
+ *
+ * Description:	Check that our statistics look right and print
+ * 		any debugging info wanted.
+ */
+static void suspend_check_io_stats(void)
+{
+	int i;
+
+	BUG_ON(atomic_read(&outstanding_io));
+	BUG_ON(infopages);
+	BUG_ON(!list_empty(&ioinfo_submit_batch));
+	BUG_ON(!list_empty(&ioinfo_busy));
+	BUG_ON(!list_empty(&ioinfo_ready_for_cleanup));
+	BUG_ON(!list_empty(&ioinfo_free));
+	BUG_ON(atomic_read(&buffer_allocs) != atomic_read(&buffer_frees));
+
+	suspend_message(SUSPEND_WRITER, SUSPEND_LOW, 0,
+			"Maximum outstanding_io was %d.\n",
+			max_outstanding_io);
+	suspend_message(SUSPEND_WRITER, SUSPEND_LOW, 0,
+			"Max info pages was %d.\n",
+			maxinfopages);
+	if (atomic_read(&buffer_allocs) != atomic_read(&buffer_frees))
+		suspend_message(SUSPEND_WRITER, SUSPEND_MEDIUM, 0,
+			"Buffer allocs (%d) != buffer frees (%d)",
+				atomic_read(&buffer_allocs),
+				atomic_read(&buffer_frees));
+	for(i = 0; i < 8; i++)
+		suspend_message(SUSPEND_WRITER, SUSPEND_MEDIUM, 0,
+			"Nr schedule calls %s: %lu.\n", sch_caller[i],
+			nr_schedule_calls[i]);
+}
+
+/*
+ * __suspend_bio_cleanup_one
+ * 
+ * Description: Clean up after completing I/O on a page.
+ * Arguments:	struct io_info:	Data for I/O to be completed.
+ */
+static void __suspend_bio_cleanup_one(struct io_info *io_info)
+{
+	struct page *buffer_page;
+	struct page *data_page;
+	char *buffer_address, *data_address;
+	int reading;
+
+	buffer_page = io_info->buffer_page;
+	data_page = io_info->data_page;
+
+	reading = test_bit(IO_AWAITING_READ, &io_info->flags);
+	suspend_message(SUSPEND_WRITER, SUSPEND_HIGH, 0,
+		"Cleanup IO: [%p]\n", 
+		io_info);
+
+	if (reading && io_info->readahead_index == -1) {
+		/*
+		 * Copy the page we read into the buffer our caller provided.
+		 */
+		data_address = (char *) kmap(data_page);
+		buffer_address = (char *) kmap(buffer_page);
+		memcpy(data_address, buffer_address, PAGE_SIZE);
+		kunmap(data_page);
+		kunmap(buffer_page);
+	
+	}
+
+	if (!reading || io_info->readahead_index == -1) {
+		/* Sanity check */
+		if (page_count(buffer_page) != 2)
+			printk(KERN_EMERG "Cleanup IO: Page count on page %p"
+					" is %d. Not good!\n",
+					buffer_page, page_count(buffer_page));
+		put_page(buffer_page);
+		__free_page(buffer_page);
+		atomic_inc(&buffer_frees);
+	} else
+		put_page(buffer_page);
+	
+	bio_put(io_info->sys_struct);
+	io_info->sys_struct = NULL;
+	io_info->flags = 0;
+}
+
+/* __suspend_io_cleanup
+ */
+
+static int suspend_bio_cleanup_one(void *data)
+{
+	struct io_info *io_info = (struct io_info *) data;
+	int readahead_index;
+	unsigned long flags;
+
+	/*
+	 * If this I/O was a readahead, remember its index.
+	 */
+	readahead_index = io_info->readahead_index;
+
+	/*
+	 * Add it to the free list.
+	 */
+	list_del_init(&io_info->list);
+	
+	/*
+	 * Do the cleanup.
+	 */
+	__suspend_bio_cleanup_one(io_info);
+
+	/*
+	 * Record the readahead as done.
+	 */
+	if (readahead_index > -1) {
+		int index = readahead_index/BITS_PER_LONG;
+		int bit = readahead_index - (index * BITS_PER_LONG);
+		spin_lock_irqsave(&suspend_readahead_flags_lock, flags);
+		set_bit(bit, &suspend_readahead_flags[index]);
+		spin_unlock_irqrestore(&suspend_readahead_flags_lock, flags);
+	}
+
+	spin_lock_irqsave(&ioinfo_free_lock, flags);
+	list_add_tail(&io_info->list, &ioinfo_free);
+	spin_unlock_irqrestore(&ioinfo_free_lock, flags);
+	
+	/* Important: Must be last thing we do to avoid a race with
+	 * finish_all_io when using keventd to do the cleanup */
+	atomic_dec(&outstanding_io);
+
+	return 0;
+}
+
+/* suspend_cleanup_some_completed_io
+ *
+ * NB: This is designed so that multiple callers can be in here simultaneously.
+ */
+
+static void suspend_cleanup_some_completed_io(void)
+{
+	int num_cleaned = 0;
+	struct io_info *first;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ioinfo_ready_lock, flags);
+	while(!list_empty(&ioinfo_ready_for_cleanup)) {
+		int result;
+		first = list_entry(ioinfo_ready_for_cleanup.next,
+				struct io_info, list);
+
+		BUG_ON(!test_and_clear_bit(IO_AWAITING_CLEANUP, &first->flags));
+
+		list_del_init(&first->list);
+
+		spin_unlock_irqrestore(&ioinfo_ready_lock, flags);
+
+		result = suspend_bio_cleanup_one((void *) first);
+
+		spin_lock_irqsave(&ioinfo_ready_lock, flags);
+		if (result)
+			continue;
+		num_cleaned++;
+		if (num_cleaned == submit_batch_size)
+			break;
+	}
+	spin_unlock_irqrestore(&ioinfo_ready_lock, flags);
+}
+
+/* do_bio_wait
+ *
+ * Actions taken when we want some I/O to get run.
+ * 
+ * Submit any I/O that's batched up (if we're not already doing
+ * that, unplug queues, schedule and clean up whatever we can.
+ */
+static void do_bio_wait(int caller)
+{
+	int num_submitted = 0;
+
+	nr_schedule_calls[caller]++;
+	
+	/* Don't want to wait on I/O we haven't submitted! */
+	num_submitted = submit_batched();
+
+	kblockd_flush();
+	
+	io_schedule();
+
+	suspend_cleanup_some_completed_io();
+}
+
+/*
+ * suspend_finish_all_io
+ *
+ * Description:	Finishes all IO and frees all IO info struct pages.
+ */
+static void suspend_finish_all_io(void)
+{
+	struct io_info *this, *next = NULL;
+	unsigned long flags;
+
+	/* Wait for all I/O to complete. */
+	while (atomic_read(&outstanding_io))
+		do_bio_wait(2);
+
+	spin_lock_irqsave(&ioinfo_free_lock, flags);
+	
+	/* 
+	 * Two stages, to avoid using freed pages.
+	 *
+	 * First free all io_info structs on a page except the first.
+	 */
+	list_for_each_entry_safe(this, next, &ioinfo_free, list) {
+		if (((unsigned long) this) & ~PAGE_MASK)
+			list_del(&this->list);
+	}
+
+	/* 
+	 * Now we have only one reference to each page, and can safely
+	 * free pages, knowing we're not going to be trying to access the
+	 * same page after freeing it.
+	 */
+	list_for_each_entry_safe(this, next, &ioinfo_free, list) {
+		list_del(&this->list);
+		free_page((unsigned long) this);
+		infopages--;
+		suspend_message(SUSPEND_MEMORY, SUSPEND_VERBOSE, 0,
+				"[FreedIOPage %lx]", this);
+	}
+	
+	spin_unlock_irqrestore(&ioinfo_free_lock, flags);
+}
+
+/*
+ * wait_on_one_page
+ *
+ * Description:	Wait for a particular I/O to complete.
+ */
+static void wait_on_one_page(struct io_info *io_info)
+{
+	do { do_bio_wait(3); } while (io_info->flags);
+}
+
+/*
+ * wait_on_readahead
+ *
+ * Wait until a particular readahead is ready.
+ */
+static void suspend_wait_on_readahead(int readahead_index)
+{
+	int index = readahead_index / BITS_PER_LONG;
+	int bit = readahead_index - index * BITS_PER_LONG;
+
+	/* read_ahead_index is the one we want to return */
+	while (!test_bit(bit, &suspend_readahead_flags[index]))
+		do_bio_wait(6);
+}
+
+/*
+ * readahead_done
+ *
+ * Returns whether the readahead requested is ready.
+ */
+
+static int suspend_readahead_ready(int readahead_index)
+{
+	int index = readahead_index / BITS_PER_LONG;
+	int bit = readahead_index - (index * BITS_PER_LONG);
+
+	return test_bit(bit, &suspend_readahead_flags[index]);
+}
+
+/* suspend_readahead_prepare
+ * Set up for doing readahead on an image */
+static int suspend_prepare_readahead(int index)
+{
+	unsigned long new_page = get_zeroed_page(GFP_ATOMIC);
+
+	if(!new_page)
+		return -ENOMEM;
+
+	suspend_readahead_pages[index] = virt_to_page(new_page);
+	return 0;
+}
+
+/* suspend_readahead_cleanup
+ * Clean up structures used for readahead */
+static void suspend_cleanup_readahead(int page)
+{
+	__free_page(suspend_readahead_pages[page]);
+	suspend_readahead_pages[page] = 0;
+	return;
+}
+
+/*
+ * suspend_end_bio
+ *
+ * Description:	Function called by block driver from interrupt context when I/O
+ * 		is completed. This is the reason we use spinlocks in
+ * 		manipulating the io_info lists. 		
+ * 		Nearly the fs/buffer.c version, but we want to mark the page as 
+ * 		done in our own structures too.
+ */
+
+static int suspend_end_bio(struct bio *bio, unsigned int num, int err)
+{
+	struct io_info *io_info = bio->bi_private;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ioinfo_busy_lock, flags);
+	list_del_init(&io_info->list);
+	spin_unlock_irqrestore(&ioinfo_busy_lock, flags);
+
+	set_bit(IO_AWAITING_CLEANUP, &io_info->flags);
+		
+	spin_lock_irqsave(&ioinfo_ready_lock, flags);
+	list_add_tail(&io_info->list, &ioinfo_ready_for_cleanup);
+	spin_unlock_irqrestore(&ioinfo_ready_lock, flags);
+	return 0;
+}
+
+/**
+ *	submit - submit BIO request.
+ *	@rw:	READ or WRITE.
+ *	@io_info: IO info structure.
+ *
+ * 	Based on Patrick's pmdisk code from long ago:
+ *	"Straight from the textbook - allocate and initialize the bio.
+ *	If we're writing, make sure the page is marked as dirty.
+ *	Then submit it and carry on."
+ *
+ *	With a twist, though - we handle block_size != PAGE_SIZE.
+ *	Caller has already checked that our page is not fragmented.
+ */
+
+static int submit(int rw, struct io_info *io_info)
+{
+	int error = 0;
+	struct bio *bio = NULL;
+	unsigned long flags;
+
+	while (!bio) {
+		bio = bio_alloc(GFP_ATOMIC,1);
+		if (!bio)
+			do_bio_wait(4);
+	}
+
+	bio->bi_bdev = io_info->dev;
+	bio->bi_sector = io_info->block[0];
+	bio->bi_private = io_info;
+	bio->bi_end_io = suspend_end_bio;
+	bio->bi_flags |= (1 << BIO_SUSPEND_DATA);
+	io_info->sys_struct = bio;
+
+	if (bio_add_page(bio, io_info->buffer_page, PAGE_SIZE, 0) < PAGE_SIZE) {
+		printk("ERROR: adding page to bio at %lld\n",
+				(unsigned long long) io_info->block[0]);
+		bio_put(bio);
+		return -EFAULT;
+	}
+
+	if (rw == WRITE)
+		bio_set_pages_dirty(bio);
+
+	spin_lock_irqsave(&ioinfo_busy_lock, flags);
+	list_add_tail(&io_info->list, &ioinfo_busy);
+	spin_unlock_irqrestore(&ioinfo_busy_lock, flags);
+	
+	submit_bio(rw,bio);
+
+	return error;
+}
+
+/* 
+ * submit a batch. The submit function can wait on I/O, so we have
+ * simple locking to avoid infinite recursion.
+ */
+static int submit_batched(void)
+{
+	static int running_already = 0;
+	struct io_info *first;
+	unsigned long flags;
+	int num_submitted = 0;
+
+	running_already = 1;
+	spin_lock_irqsave(&ioinfo_submit_lock, flags);
+	while(!list_empty(&ioinfo_submit_batch)) {
+		first = list_entry(ioinfo_submit_batch.next, struct io_info,
+									list);
+
+		BUG_ON(!test_and_clear_bit(IO_AWAITING_SUBMIT, &first->flags));
+
+		list_del_init(&first->list);
+
+		atomic_dec(&submit_batch);
+
+		spin_unlock_irqrestore(&ioinfo_submit_lock, flags);
+
+		if (test_bit(IO_AWAITING_READ, &first->flags))
+			submit(READ, first);
+		else
+			submit(WRITE, first);
+
+		spin_lock_irqsave(&ioinfo_submit_lock, flags);
+		
+		num_submitted++;
+		if (num_submitted == submit_batch_size)
+			break;
+	}
+	spin_unlock_irqrestore(&ioinfo_submit_lock, flags);
+	running_already = 0;
+
+	return num_submitted;
+}
+
+static void add_to_batch(struct io_info *io_info)
+{
+	unsigned long flags;
+
+	set_bit(IO_AWAITING_SUBMIT, &io_info->flags);
+
+	/* Put our prepared I/O struct on the batch list. */
+	spin_lock_irqsave(&ioinfo_submit_lock, flags);
+	list_add_tail(&io_info->list, &ioinfo_submit_batch);
+	spin_unlock_irqrestore(&ioinfo_submit_lock, flags);
+
+	atomic_inc(&submit_batch);
+
+	if (atomic_read(&submit_batch) >= submit_batch_size)
+		submit_batched();
+}
+
+/*
+ * get_io_info_struct
+ *
+ * Description:	Get an I/O struct.
+ * Returns:	Pointer to the struct prepared for use.
+ */
+static struct io_info *get_io_info_struct(void)
+{
+	unsigned long newpage = 0, flags;
+	struct io_info *this = NULL;
+	int remaining = 0;
+
+	do {
+		while (atomic_read(&outstanding_io) >= MAX_OUTSTANDING_IO)
+			do_bio_wait(0);
+
+		/* Can start a new I/O. Is there a free one? */
+		if (!list_empty(&ioinfo_free)) {
+			/* Yes. Grab it. */
+			spin_lock_irqsave(&ioinfo_free_lock, flags);
+			break;
+		}
+
+		/* No. Need to allocate a new page for I/O info structs. */
+		newpage = get_zeroed_page(GFP_ATOMIC);
+		if (!newpage) {
+			do_bio_wait(1);
+			continue;
+		}
+
+		suspend_message(SUSPEND_MEMORY, SUSPEND_VERBOSE, 0,
+				"[NewIOPage %lx]", newpage);
+		infopages++;
+		if (infopages > maxinfopages)
+			maxinfopages++;
+
+		/* Prepare the new page for use. */
+		this = (struct io_info *) newpage;
+		remaining = PAGE_SIZE;
+		spin_lock_irqsave(&ioinfo_free_lock, flags);
+		while (remaining >= (sizeof(struct io_info))) {
+			list_add_tail(&this->list, &ioinfo_free);
+			this = (struct io_info *) (((char *) this) + 
+					sizeof(struct io_info));
+			remaining -= sizeof(struct io_info);
+		}
+		break;
+	} while (1);
+
+	/*
+	 * We have an I/O info struct. Remove it from the free list.
+	 * It will be added to the submit or busy list later.
+	 */
+	this = list_entry(ioinfo_free.next, struct io_info, list);
+	list_del_init(&this->list);
+	spin_unlock_irqrestore(&ioinfo_free_lock, flags);
+	return this;
+}
+
+/*
+ * start_one
+ *
+ * Description:	Prepare and start a read or write operation.
+ * 		Note that we use our own buffer for reading or writing.
+ * 		This simplifies doing readahead and asynchronous writing.
+ * 		We can begin a read without knowing the location into which
+ * 		the data will eventually be placed, and the buffer passed
+ * 		for a write can be reused immediately (essential for the
+ * 		modules system).
+ * 		Failure? What's that?
+ * Returns:	The io_info struct created.
+ */
+static struct io_info *start_one(int rw, struct submit_params *submit_info)
+{
+	struct io_info *io_info = get_io_info_struct();
+	unsigned long buffer_virt = 0;
+	char *to, *from;
+	struct page *buffer_page;
+
+	if (!io_info)
+		return NULL;
+
+	/* Get our local buffer */
+	suspend_message(SUSPEND_WRITER, SUSPEND_HIGH, 1,
+			"Start_IO: [%p]", io_info);
+	
+	/* Copy settings to the io_info struct */
+	io_info->data_page = submit_info->page;
+	io_info->readahead_index = submit_info->readahead_index;
+
+	if (io_info->readahead_index == -1) {
+		while (!(buffer_virt = get_zeroed_page(GFP_ATOMIC)))
+			do_bio_wait(5);
+
+		atomic_inc(&buffer_allocs);
+		suspend_message(SUSPEND_WRITER, SUSPEND_HIGH, 0,
+				"[ALLOC BUFFER]->%d",
+				real_nr_free_pages());
+		buffer_page = virt_to_page(buffer_virt);
+	
+		io_info->buffer_page = buffer_page;
+	} else {
+		unsigned long flags;
+		int index = io_info->readahead_index / BITS_PER_LONG;
+		int bit = io_info->readahead_index - index * BITS_PER_LONG;
+
+		spin_lock_irqsave(&suspend_readahead_flags_lock, flags);
+		clear_bit(bit, &suspend_readahead_flags[index]);
+		spin_unlock_irqrestore(&suspend_readahead_flags_lock, flags);
+
+		io_info->buffer_page = buffer_page = submit_info->page;
+	}
+
+	/* If writing, copy our data. The data is probably in
+	 * lowmem, but we cannot be certain. If there is no
+	 * compression/encryption, we might be passed the
+	 * actual source page's address. */
+	if (rw == WRITE) {
+		to = (char *) buffer_virt;
+		from = kmap_atomic(io_info->data_page, KM_USER1);
+		memcpy(to, from, PAGE_SIZE);
+		kunmap_atomic(from, KM_USER1);
+	}
+
+	/* Submit the page */
+	get_page(buffer_page);
+	
+	io_info->dev = submit_info->dev;
+	io_info->block[0] = submit_info->block[0];
+
+	if (rw == READ)
+		set_bit(IO_AWAITING_READ, &io_info->flags);
+
+	suspend_message(SUSPEND_WRITER, SUSPEND_HIGH, 1,
+			"-> (PRE BRW) %d\n",
+			real_nr_free_pages());
+
+	if (submit_batch_size > 1)
+		add_to_batch(io_info);
+	else
+	 	submit(rw, io_info);
+	
+	atomic_inc(&outstanding_io);
+	if (atomic_read(&outstanding_io) > max_outstanding_io)
+		max_outstanding_io++;
+	
+	return io_info;
+}
+
+static int suspend_do_io(int rw, 
+		struct submit_params *submit_info, int syncio)
+{
+	struct io_info *io_info;
+
+	if(!submit_info->dev) {
+		printk("Suspend_do_io: submit_info->dev is NULL!\n");
+		return 1;
+	}
+	
+	io_info = start_one(rw, submit_info);
+
+	if (!io_info) {
+		printk("Unable to allocate an io_info struct.\n");
+		return 1;
+	} else if (syncio)
+		wait_on_one_page(io_info);
+
+	/* If we were the only one, clean everything up */
+	if (!atomic_read(&outstanding_io))
+		suspend_finish_all_io();
+	return 0;
+} 
+
+/* We used to use bread here, but it doesn't correctly handle
+ * blocksize != PAGE_SIZE. Now we create a submit_info to get the data we
+ * want and use our normal routines (synchronously).
+ */
+
+static int suspend_bdev_page_io(int rw, struct block_device *bdev, long pos,
+		struct page *page)
+{
+	struct submit_params submit_info;
+
+	if (!bdev)
+		return 0;
+
+	submit_info.page = page;
+	submit_info.dev = bdev;
+	submit_info.block[0] = pos;
+	submit_info.readahead_index = -1;
+	return suspend_do_io(rw, &submit_info, 1);
+}
+
+static unsigned long suspend_bio_memory_needed(void)
+{
+	/* We want to have at least enough memory so as to have
+	 * MAX_OUTSTANDING_IO transactions on the fly at once. If we 
+	 * can to more, fine. */
+	return (MAX_OUTSTANDING_IO * (PAGE_SIZE + sizeof(struct request) +
+				sizeof(struct bio) + sizeof(struct io_info)));
+}
+
+static void suspend_set_devinfo(struct suspend_bdev_info *info)
+{
+	suspend_devinfo = info;
+}
+
+static int forward_extra_blocks(void)
+{
+	int i;
+
+	for (i = 1; i < suspend_devinfo[suspend_writer_posn.current_chain].
+							blocks_per_page; i++)
+		suspend_extent_state_next(&suspend_writer_posn);
+
+	if (suspend_extent_state_eof(&suspend_writer_posn)) {
+		printk("Extent state eof.\n");
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+static int forward_one_page(void)
+{
+	int at_start = (suspend_writer_posn.current_chain == -1);
+
+	/* Have to go forward one to ensure we're on the right chain,
+	 * before we can know how many more blocks to skip.*/
+	suspend_extent_state_next(&suspend_writer_posn);
+
+	if (!at_start)
+		if (forward_extra_blocks())
+			return -ENODATA;
+
+	if (extra_page_forward) {
+		extra_page_forward = 0;
+		return forward_one_page();
+	}
+
+	return 0;
+}
+
+static void set_extra_page_forward(void)
+{
+	extra_page_forward = 1;
+}
+
+static int suspend_rw_page(int rw, struct page *page,
+		int readahead_index, int sync, int debug)
+{
+	int i, current_chain;
+	struct submit_params submit_params;
+
+	if (test_action_state(SUSPEND_TEST_FILTER_SPEED))
+		return 0;
+		
+	submit_params.readahead_index = readahead_index;
+	submit_params.page = page;
+	
+	if (forward_one_page()) {
+		printk("Failed to advance a page in the extent data.\n");
+		return -ENODATA;
+	}
+
+	current_chain = suspend_writer_posn.current_chain;
+	submit_params.dev = suspend_devinfo[current_chain].bdev;
+	submit_params.block[0] = suspend_writer_posn.current_offset <<
+		suspend_devinfo[current_chain].bmap_shift;
+
+	if (debug)
+		printk("%s: %lx:%lx.\n", rw ? "Write" : "Read",
+				(long) submit_params.dev->bd_dev,
+				(long) submit_params.block[0]);
+
+	i = suspend_do_io(rw, &submit_params, sync);
+
+	if (i)
+		return -EIO;
+
+	return 0;
+}
+
+static int suspend_bio_read_chunk(struct page *buffer_page, int sync)
+{
+	static int last_result;
+	unsigned long *virt;
+
+	if (sync == SUSPEND_ASYNC)
+		return suspend_rw_page(READ, buffer_page, -1, sync, 0);
+
+	/* Start new readahead while we wait for our page */
+	if (readahead_index == -1) {
+		last_result = 0;
+		readahead_index = readahead_submit_index = 0;
+	}
+
+	/* Start a new readahead? */
+	if (last_result) {
+		/* We failed to submit a read, and have cleaned up
+		 * all the readahead previously submitted */
+		if (readahead_submit_index == readahead_index)
+			return -EPERM;
+		goto wait;
+	}
+	
+	do {
+		if (suspend_prepare_readahead(readahead_submit_index))
+			break;
+
+		last_result = suspend_rw_page(
+			READ,
+			suspend_readahead_pages[readahead_submit_index], 
+			readahead_submit_index, SUSPEND_ASYNC, 0);
+		if (last_result) {
+			printk("Begin read chunk for page %d returned %d.\n",
+				readahead_submit_index, last_result);
+			suspend_cleanup_readahead(readahead_submit_index);
+			break;
+		}
+
+		readahead_submit_index++;
+
+		if (readahead_submit_index == MAX_OUTSTANDING_IO)
+			readahead_submit_index = 0;
+
+	} while((!last_result) && (readahead_submit_index != readahead_index) &&
+			(!suspend_readahead_ready(readahead_index)));
+
+wait:
+	suspend_wait_on_readahead(readahead_index);
+
+	virt = kmap_atomic(buffer_page, KM_USER1);
+	memcpy(virt, page_address(suspend_readahead_pages[readahead_index]),
+			PAGE_SIZE);
+	kunmap_atomic(virt, KM_USER1);
+
+	suspend_cleanup_readahead(readahead_index);
+
+	readahead_index++;
+	if (readahead_index == MAX_OUTSTANDING_IO)
+		readahead_index = 0;
+
+	return 0;
+}
+
+static int suspend_rw_init(int rw, int stream_number)
+{
+	suspend_extent_state_restore(&suspend_writer_posn,
+			&suspend_writer_posn_save[stream_number]);
+	current_stream = stream_number;
+
+	BUG_ON(!suspend_writer_posn.current_extent);
+
+	suspend_reset_io_stats();
+
+	readahead_index = readahead_submit_index = -1;
+
+	return 0;
+}
+
+static int suspend_rw_cleanup(int rw)
+{
+	if (rw == WRITE && current_stream == 2)
+		suspend_extent_state_save(&suspend_writer_posn,
+				&suspend_writer_posn_save[1]);
+	
+	suspend_finish_all_io();
+	
+	if (rw == READ) {
+		while (readahead_index != readahead_submit_index) {
+			suspend_cleanup_readahead(readahead_index);
+			readahead_index++;
+			if (readahead_index == MAX_OUTSTANDING_IO)
+				readahead_index = 0;
+		}
+	}
+
+	suspend_check_io_stats();
+
+	return 0;
+}
+
+static int suspend_write_chunk(struct page *buffer_page)
+{
+	return suspend_rw_page(WRITE, buffer_page, -1, 0, 0);
+}
+
+static int suspend_rw_header_chunk(int rw, struct suspend_module_ops *owner,
+		char *buffer, int buffer_size)
+{
+	int bytes_left = buffer_size;
+	
+	if (owner) {
+		owner->header_used += buffer_size;
+		if (owner->header_used > owner->header_requested) {
+			printk(KERN_EMERG "Suspend2 module %s is using more"
+				"header space (%lu) than it requested (%lu).\n",
+				owner->name,
+				owner->header_used,
+				owner->header_requested);
+			BUG();
+		}
+	}
+
+	/* Read a chunk of the header */
+	while (bytes_left) {
+		char *source_start = buffer + buffer_size - bytes_left;
+		char *dest_start = suspend_writer_buffer + suspend_writer_buffer_posn;
+		int capacity = PAGE_SIZE - suspend_writer_buffer_posn;
+		char *to = rw ? dest_start : source_start;
+		char *from = rw ? source_start : dest_start;
+
+		if (bytes_left <= capacity) {
+			if (test_debug_state(SUSPEND_HEADER))
+				printk("Copy %d bytes %d-%d from %p to %p.\n",
+						bytes_left,
+						suspend_header_bytes_used,
+						suspend_header_bytes_used + bytes_left,
+						from, to);
+			memcpy(to, from, bytes_left);
+			suspend_writer_buffer_posn += bytes_left;
+			suspend_header_bytes_used += bytes_left;
+			return rw ? 0 : buffer_size;
+		}
+
+		/* Next to read the next page */
+		if (test_debug_state(SUSPEND_HEADER))
+			printk("Copy %d bytes (%d-%d) from %p to %p.\n",
+					capacity,
+					suspend_header_bytes_used,
+					suspend_header_bytes_used + capacity,
+					from, to);
+		memcpy(to, from, capacity);
+		bytes_left -= capacity;
+		suspend_header_bytes_used += capacity;
+
+		if (rw == READ && test_suspend_state(SUSPEND_TRY_RESUME_RD))
+			sys_read(suspend_read_fd,
+				suspend_writer_buffer, BLOCK_SIZE);
+		else {
+			if (suspend_rw_page(rw,
+					virt_to_page(suspend_writer_buffer),
+					-1, !rw,
+					test_debug_state(SUSPEND_HEADER)))
+				return -EIO;
+		}
+
+		suspend_writer_buffer_posn = 0;
+		suspend_cond_pause(0, NULL);
+	}
+
+	return rw ? 0 : buffer_size;
+}
+
+static int write_header_chunk_finish(void)
+{
+	return suspend_rw_page(WRITE,
+		virt_to_page(suspend_writer_buffer),
+		-1, 0, test_debug_state(SUSPEND_HEADER)) ? -EIO : 0;
+}
+
+struct suspend_bio_ops suspend_bio_ops = {
+	.bdev_page_io = suspend_bdev_page_io,
+	.check_io_stats = suspend_check_io_stats,
+	.reset_io_stats = suspend_reset_io_stats,
+	.finish_all_io = suspend_finish_all_io,
+	.prepare_readahead = suspend_prepare_readahead,
+	.cleanup_readahead = suspend_cleanup_readahead,
+	.readahead_pages = suspend_readahead_pages,
+	.readahead_ready = suspend_readahead_ready,
+	.forward_one_page = forward_one_page,
+	.set_extra_page_forward = set_extra_page_forward,
+	.set_devinfo = suspend_set_devinfo,
+	.read_chunk = suspend_bio_read_chunk,
+	.write_chunk = suspend_write_chunk,
+	.rw_init = suspend_rw_init,
+	.rw_cleanup = suspend_rw_cleanup,
+	.rw_header_chunk = suspend_rw_header_chunk,
+	.write_header_chunk_finish = write_header_chunk_finish,
+};
+
+static struct suspend_module_ops suspend_blockwriter_ops = 
+{
+	.name					= "Block I/O",
+	.type					= MISC_MODULE,
+	.module					= THIS_MODULE,
+	.memory_needed				= suspend_bio_memory_needed,
+};
+
+static __init int suspend_block_io_load(void)
+{
+	return suspend_register_module(&suspend_blockwriter_ops);
+}
+
+#ifdef MODULE
+static __exit void suspend_block_io_unload(void)
+{
+	suspend_unregister_module(&suspend_blockwriter_ops);
+}
+
+module_init(suspend_block_io_load);
+module_exit(suspend_block_io_unload);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nigel Cunningham");
+MODULE_DESCRIPTION("Suspend2 block io functions");
+#else
+late_initcall(suspend_block_io_load);
+#endif
diff -urN oldtree/kernel/power/suspend_file.c newtree/kernel/power/suspend_file.c
--- oldtree/kernel/power/suspend_file.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/suspend_file.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,1125 @@
+/*
+ * kernel/power/suspend_file.c
+ *
+ * Copyright 2005-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * Distributed under GPLv2.
+ * 
+ * This file encapsulates functions for usage of a simple file as a
+ * backing store. It is based upon the swapwriter, and shares the
+ * same basic working. Here, though, we have nothing to do with
+ * swapspace, and only one device to worry about.
+ *
+ * The user can just
+ *
+ * echo Suspend2 > /path/to/my_file
+ *
+ * and
+ *
+ * echo /path/to/my_file > /sys/power/suspend2/filewriter/target
+ *
+ * then put what they find in /sys/power/suspend2/resume2
+ * as their resume2= parameter in lilo.conf (and rerun lilo if using it).
+ *
+ * Having done this, they're ready to suspend and resume.
+ *
+ * TODO:
+ * - File resizing.
+ */
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/mount.h>
+#include <linux/statfs.h>
+#include <linux/syscalls.h>
+#include <linux/namei.h>
+#include <linux/fs.h>
+
+#include "suspend.h"
+#include "sysfs.h"
+#include "modules.h"
+#include "ui.h"
+#include "extent.h"
+#include "io.h"
+#include "storage.h"
+#include "block_io.h"
+
+static struct suspend_module_ops filewriterops;
+
+/* Details of our target.  */
+
+char filewriter_target[256];
+static struct inode *target_inode;
+static struct file *target_file;
+static struct block_device *filewriter_target_bdev;
+static dev_t resume_dev_t;
+static int used_devt = 0;
+static int setting_filewriter_target = 0;
+static sector_t target_firstblock = 0, target_header_start = 0;
+static int target_storage_available = 0;
+static int target_claim = 0;
+
+static char HaveImage[] = "HaveImage\n";
+static char NoImage[] =   "Suspend2\n";
+#define sig_size (sizeof(HaveImage) + 1)
+
+struct filewriter_header {
+	char sig[sig_size];
+	int resumed_before;
+	unsigned long first_header_block;
+};
+
+extern dev_t ROOT_DEV;
+extern char *__initdata root_device_name;
+
+/* Header_pages must be big enough for signature */
+static int header_pages, main_pages;
+
+#define target_is_normal_file() (S_ISREG(target_inode->i_mode))
+
+static struct suspend_bdev_info devinfo;
+
+/* Extent chain for blocks */
+static struct extent_chain block_chain;
+
+/* Signature operations */
+enum {
+	GET_IMAGE_EXISTS,
+	INVALIDATE,
+	MARK_RESUME_ATTEMPTED,
+};
+
+static void set_devinfo(struct block_device *bdev, int target_blkbits)
+{
+	devinfo.bdev = bdev;
+	if (!target_blkbits) {
+		devinfo.bmap_shift = devinfo.blocks_per_page = 0;
+	} else {
+		devinfo.bmap_shift = target_blkbits - 9;
+		devinfo.blocks_per_page = (1 << (PAGE_SHIFT - target_blkbits));
+	}
+}
+
+static int filewriter_storage_available(void)
+{
+	int result = 0;
+	struct block_device *bdev=filewriter_target_bdev;
+
+	if (!target_inode)
+		return 0;
+
+	switch (target_inode->i_mode & S_IFMT) {
+		case S_IFSOCK:
+		case S_IFCHR:
+		case S_IFIFO: /* Socket, Char, Fifo */
+			return -1;
+		case S_IFREG: /* Regular file: current size - holes + free
+				 space on part */
+			result = target_storage_available;
+			break;
+		case S_IFBLK: /* Block device */
+			if (!bdev->bd_disk) {
+				printk("bdev->bd_disk null.\n");
+				return 0;
+			}
+
+			result = (bdev->bd_part ?
+				bdev->bd_part->nr_sects :
+				bdev->bd_disk->capacity) >> (PAGE_SHIFT - 9);
+	}
+
+	return result;
+}
+
+static int has_contiguous_blocks(int page_num)
+{
+	int j;
+	sector_t last = 0;
+
+	for (j = 0; j < devinfo.blocks_per_page; j++) {
+		sector_t this = bmap(target_inode,
+				page_num * devinfo.blocks_per_page + j);
+
+		if (!this || (last && (last + 1) != this))
+			break;
+
+		last = this;
+	}
+			
+	return (j == devinfo.blocks_per_page);
+}
+
+static int size_ignoring_ignored_pages(void)
+{
+	int mappable = 0, i;
+	
+	if (target_is_normal_file()) {
+		for (i = 0; i < (target_inode->i_size >> PAGE_SHIFT) ; i++)
+			if (has_contiguous_blocks(i))
+				mappable++;
+	
+		return mappable;
+	} else
+		return filewriter_storage_available();
+}
+
+static void __populate_block_list(int min, int max)
+{
+	if (test_action_state(SUSPEND_TEST_BIO))
+		printk("Adding extent %d-%d.\n", min << devinfo.bmap_shift,
+		        ((max + 1) << devinfo.bmap_shift) - 1);
+
+	suspend_add_to_extent_chain(&block_chain, min, max);
+}
+
+static void populate_block_list(void)
+{
+	int i;
+	
+	if (block_chain.first)
+		suspend_put_extent_chain(&block_chain);
+
+	if (target_is_normal_file()) {
+		int extent_min = -1, extent_max = -1, got_header = 0;
+
+		for (i = 0;
+		     i < (target_inode->i_size >> PAGE_SHIFT);
+		     i++) {
+			sector_t new_sector;
+
+			if (!has_contiguous_blocks(i))
+				continue;
+
+			new_sector = bmap(target_inode,
+				(i * devinfo.blocks_per_page));
+
+			/* 
+			 * Ignore the first block in the file.
+			 * It gets the header.
+			 */
+			if (new_sector == target_firstblock >> devinfo.bmap_shift) {
+				got_header = 1;
+				continue;
+			}
+
+			/* 
+			 * I'd love to be able to fill in holes and resize 
+			 * files, but not yet...
+			 */
+
+			if (new_sector == extent_max + 1)
+				extent_max+= devinfo.blocks_per_page;
+			else {
+				if (extent_min > -1)
+					__populate_block_list(extent_min,
+							extent_max);
+
+				extent_min = new_sector;
+				extent_max = extent_min +
+					devinfo.blocks_per_page - 1;
+			}
+		}
+		if (extent_min > -1)
+			__populate_block_list(extent_min, extent_max);
+
+		BUG_ON(!got_header);
+	} else
+		if (target_storage_available > 0)
+			__populate_block_list(devinfo.blocks_per_page, 
+				(min(main_pages, target_storage_available) + 1) *
+			 	devinfo.blocks_per_page - 1);
+}
+
+static void filewriter_cleanup(int finishing_cycle)
+{
+	if (filewriter_target_bdev) {
+		if (target_claim) {
+			bd_release(filewriter_target_bdev);
+			target_claim = 0;
+		}
+
+		if (used_devt) {
+			blkdev_put(filewriter_target_bdev);
+			used_devt = 0;
+		}
+		filewriter_target_bdev = NULL;
+		target_inode = NULL;
+		set_devinfo(NULL, 0);
+		target_storage_available = 0;
+	}
+
+	if (target_file > 0) {
+		filp_close(target_file, NULL);
+		target_file = NULL;
+	}
+}
+
+/* 
+ * reopen_resume_devt
+ *
+ * Having opened resume2= once, we remember the major and
+ * minor nodes and use them to reopen the bdev for checking
+ * whether an image exists (possibly when starting a resume).
+ */
+static void reopen_resume_devt(void)
+{
+	filewriter_target_bdev = open_by_devnum(resume_dev_t, FMODE_READ);
+	if (IS_ERR(filewriter_target_bdev)) {
+		printk("Got a dev_num (%lx) but failed to open it.\n",
+				(unsigned long) resume_dev_t);
+		return;
+	}
+	target_inode = filewriter_target_bdev->bd_inode;
+	set_devinfo(filewriter_target_bdev, target_inode->i_blkbits);
+}
+
+static void filewriter_get_target_info(char *target, int get_size,
+		int resume2)
+{
+	if (target_file)
+		filewriter_cleanup(0);
+
+	if (!target || !strlen(target))
+		return;
+
+	target_file = filp_open(target, O_RDWR, 0);
+
+	if (IS_ERR(target_file) || !target_file) {
+
+		if (!resume2) {
+			printk("Open file %s returned %p.\n",
+					target, target_file);
+			target_file = NULL;
+			return;
+		}
+
+		target_file = NULL;
+		resume_dev_t = name_to_dev_t(target);
+		if (!resume_dev_t) {
+			printk("Open file %s returned %p and name_to_devt "
+					"failed.\n",
+					target, target_file);
+			if (!resume_dev_t) {
+				struct kstat stat;
+				int error = vfs_stat(target, &stat);
+				if (error) {
+					printk("Stating the file also failed."
+						" Nothing more we can do.\n");
+					return;
+				}
+				resume_dev_t = stat.rdev;
+			}
+			return;
+		}
+	     	filewriter_target_bdev = open_by_devnum(resume_dev_t, FMODE_READ);
+		if (IS_ERR(filewriter_target_bdev)) {
+			printk("Got a dev_num (%lx) but failed to open it.\n",
+					(unsigned long) resume_dev_t);
+			return;
+		}
+		used_devt = 1;
+		target_inode = filewriter_target_bdev->bd_inode;
+	} else
+		target_inode = target_file->f_mapping->host;
+
+	if (S_ISLNK(target_inode->i_mode) ||
+	    S_ISDIR(target_inode->i_mode) ||
+	    S_ISSOCK(target_inode->i_mode) ||
+	    S_ISFIFO(target_inode->i_mode)) {
+		printk("The filewriter works with regular files, character "
+				"files and block devices.\n");
+		goto cleanup;
+	}
+
+	if (!used_devt) {
+		if (S_ISBLK(target_inode->i_mode)) {
+			filewriter_target_bdev = I_BDEV(target_inode);
+			if (!bd_claim(filewriter_target_bdev, &filewriterops))
+				target_claim = 1;
+		} else
+			filewriter_target_bdev = target_inode->i_sb->s_bdev;
+		resume_dev_t = filewriter_target_bdev->bd_dev;
+	}
+
+	set_devinfo(filewriter_target_bdev, target_inode->i_blkbits);
+
+	if (get_size)
+		target_storage_available = size_ignoring_ignored_pages();
+
+	if (!resume2)
+		target_firstblock = bmap(target_inode, 0) << devinfo.bmap_shift;
+	
+	return;
+cleanup:
+	target_inode = NULL;
+	if (target_file) {
+		filp_close(target_file, NULL);
+		target_file = NULL;
+	}
+	set_devinfo(NULL, 0);
+	target_storage_available = 0;
+}
+
+static int parse_signature(struct filewriter_header *header)
+{
+	int have_image = !memcmp(HaveImage, header->sig, sizeof(HaveImage) - 1);
+	int no_image_header = !memcmp(NoImage, header->sig, sizeof(NoImage) - 1);
+
+	if (no_image_header)
+		return 0;
+
+	if (!have_image)
+		return -1;
+
+	if (header->resumed_before)
+		set_suspend_state(SUSPEND_RESUMED_BEFORE);
+	else
+		clear_suspend_state(SUSPEND_RESUMED_BEFORE);
+
+	target_header_start = header->first_header_block;
+	return 1;
+}
+
+/* prepare_signature */
+
+static int prepare_signature(struct filewriter_header *current_header,
+		unsigned long first_header_block)
+{
+	strncpy(current_header->sig, HaveImage, sizeof(HaveImage));
+	current_header->resumed_before = 0;
+	current_header->first_header_block = first_header_block;
+	return 0;
+}
+
+static int filewriter_storage_allocated(void)
+{
+	int result;
+
+	if (!target_inode)
+		return 0;
+
+	if (target_is_normal_file()) {
+		result = (int) target_storage_available;
+	} else
+		result = header_pages + main_pages;
+
+	return result;
+}
+
+static int filewriter_release_storage(void)
+{
+	if ((test_action_state(SUSPEND_KEEP_IMAGE)) &&
+	     test_suspend_state(SUSPEND_NOW_RESUMING))
+		return 0;
+
+	suspend_put_extent_chain(&block_chain);
+
+	header_pages = main_pages = 0;
+	return 0;
+}
+
+static int filewriter_allocate_header_space(int space_requested)
+{
+	int i;
+
+	if (!block_chain.first)
+		return 0;
+
+	suspend_extent_state_goto_start(&suspend_writer_posn);
+	suspend_bio_ops.forward_one_page(); /* To first page */
+	
+	for (i = 0; i < space_requested; i++)
+		if (suspend_bio_ops.forward_one_page())
+			return -ENOSPC;
+
+	/* The end of header pages will be the start of pageset 2 */
+	suspend_extent_state_save(&suspend_writer_posn,
+			&suspend_writer_posn_save[2]);
+	header_pages = space_requested;
+	return 0;
+}
+
+static int filewriter_allocate_storage(int space_requested)
+{
+	int result = 0, prev_header_pages;
+	int blocks_to_get = (space_requested << devinfo.bmap_shift) -
+		block_chain.size;
+	
+	/* Only release_storage reduces the size */
+	if (blocks_to_get < 1)
+		return 0;
+
+	main_pages = space_requested;
+
+	populate_block_list();
+
+	suspend_message(SUSPEND_WRITER, SUSPEND_MEDIUM, 0,
+		"Finished with block_chain.size == %d.\n",
+		block_chain.size);
+
+	if (block_chain.size < (header_pages + main_pages)) {
+		printk("Block chain size (%d) < header pages (%d) + main pages (%d) (=%d).\n",
+				block_chain.size,
+				header_pages, main_pages,
+				header_pages + main_pages);
+		result = -ENOSPC;
+	}
+
+	prev_header_pages = header_pages;
+	header_pages = 0;
+	filewriter_allocate_header_space(prev_header_pages);
+	return result;
+}
+
+static int filewriter_write_header_init(void)
+{
+	suspend_extent_state_goto_start(&suspend_writer_posn);
+
+	suspend_writer_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	suspend_writer_buffer_posn = suspend_header_bytes_used = 0;
+
+	/* Info needed to bootstrap goes at the start of the header.
+	 * First we save the basic info needed for reading, including the number
+	 * of header pages. Then we save the structs containing data needed
+	 * for reading the header pages back.
+	 * Note that even if header pages take more than one page, when we
+	 * read back the info, we will have restored the location of the
+	 * next header page by the time we go to use it.
+	 */
+
+	suspend_bio_ops.rw_header_chunk(WRITE, &filewriterops,
+			(char *) &suspend_writer_posn_save, 
+			sizeof(suspend_writer_posn_save));
+
+	suspend_bio_ops.rw_header_chunk(WRITE, &filewriterops,
+			(char *) &devinfo, sizeof(devinfo));
+
+	suspend_serialise_extent_chain(&filewriterops, &block_chain);
+	
+	return 0;
+}
+
+static int filewriter_write_header_cleanup(void)
+{
+	struct filewriter_header *header;
+
+	/* Write any unsaved data */
+	if (suspend_writer_buffer_posn)
+		suspend_bio_ops.write_header_chunk_finish();
+
+	suspend_bio_ops.finish_all_io();
+
+	suspend_extent_state_goto_start(&suspend_writer_posn);
+	suspend_bio_ops.forward_one_page();
+
+	/* Adjust image header */
+	suspend_bio_ops.bdev_page_io(READ, filewriter_target_bdev,
+			target_firstblock,
+			virt_to_page(suspend_writer_buffer));
+
+	header = (struct filewriter_header *) suspend_writer_buffer;
+
+	prepare_signature(header,
+			suspend_writer_posn.current_offset <<
+			devinfo.bmap_shift);
+		
+	suspend_bio_ops.bdev_page_io(WRITE, filewriter_target_bdev,
+			target_firstblock,
+			virt_to_page(suspend_writer_buffer));
+
+	free_page((unsigned long) suspend_writer_buffer);
+	suspend_writer_buffer = NULL;
+	
+	suspend_bio_ops.finish_all_io();
+
+	return 0;
+}
+
+/* HEADER READING */
+
+#ifdef CONFIG_DEVFS_FS
+int  create_dev(char *name, dev_t dev, char *devfs_name);
+#else
+static int create_dev(char *name, dev_t dev, char *devfs_name)
+{
+	sys_unlink(name);
+	return sys_mknod(name, S_IFBLK|0600, new_encode_dev(dev));
+}
+#endif
+
+static int rd_init(void)
+{
+	suspend_writer_buffer_posn = 0;
+
+	create_dev("/dev/root", ROOT_DEV, root_device_name);
+	create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, 0), NULL);
+
+	suspend_read_fd = sys_open("/dev/root", O_RDONLY, 0);
+	if (suspend_read_fd < 0)
+		goto out;
+	
+	sys_read(suspend_read_fd, suspend_writer_buffer, BLOCK_SIZE);
+
+	memcpy(&suspend_writer_posn_save,
+		suspend_writer_buffer + suspend_writer_buffer_posn,
+		sizeof(suspend_writer_posn_save));
+	
+	suspend_writer_buffer_posn += sizeof(suspend_writer_posn_save);
+
+	return 0;
+out:
+	sys_unlink("/dev/ram");
+	sys_unlink("/dev/root");
+	return -EIO;
+}
+
+static int file_init(void)
+{
+	suspend_writer_buffer_posn = 0;
+
+	/* Read filewriter configuration */
+	suspend_bio_ops.bdev_page_io(READ, filewriter_target_bdev,
+			target_header_start,
+			virt_to_page((unsigned long) suspend_writer_buffer));
+	
+	return 0;
+}
+
+/*
+ * read_header_init()
+ * 
+ * Ramdisk support based heavily on init/do_mounts_rd.c
+ *
+ * Description:
+ * 1. Attempt to read the device specified with resume2=.
+ * 2. Check the contents of the header for our signature.
+ * 3. Warn, ignore, reset and/or continue as appropriate.
+ * 4. If continuing, read the filewriter configuration section
+ *    of the header and set up block device info so we can read
+ *    the rest of the header & image.
+ *
+ * Returns:
+ * May not return if user choose to reboot at a warning.
+ * -EINVAL if cannot resume at this time. Booting should continue
+ * normally.
+ */
+
+static int filewriter_read_header_init(void)
+{
+	int result;
+	struct block_device *tmp;
+
+	suspend_writer_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	
+	if (test_suspend_state(SUSPEND_TRY_RESUME_RD))
+		result = rd_init();
+	else
+		result = file_init();
+	
+	if (result) {
+		printk("Filewriter read header init: Failed to initialise "
+				"reading the first page of data.\n");
+		return result;
+	}
+
+	memcpy(&suspend_writer_posn_save,
+	       suspend_writer_buffer + suspend_writer_buffer_posn,
+	       sizeof(suspend_writer_posn_save));
+	
+	suspend_writer_buffer_posn += sizeof(suspend_writer_posn_save);
+
+	tmp = devinfo.bdev;
+
+	memcpy(&devinfo,
+	       suspend_writer_buffer + suspend_writer_buffer_posn,
+	       sizeof(devinfo));
+
+	devinfo.bdev = tmp;
+	suspend_writer_buffer_posn += sizeof(devinfo);
+
+	suspend_extent_state_goto_start(&suspend_writer_posn);
+	suspend_bio_ops.set_extra_page_forward();
+
+	suspend_header_bytes_used = suspend_writer_buffer_posn;
+
+	suspend_load_extent_chain(&block_chain);
+	
+	return 0;
+}
+
+static int filewriter_read_header_cleanup(void)
+{
+	free_page((unsigned long) suspend_writer_buffer);
+	suspend_writer_buffer = NULL;
+	return 0;
+}
+
+static int filewriter_signature_op(int op)
+{
+	char *cur;
+	int result = 0, changed = 0;
+	struct filewriter_header *header;
+	
+	if(filewriter_target_bdev <= 0)
+		return -1;
+
+	cur = (char *) get_zeroed_page(GFP_ATOMIC);
+	if (!cur) {
+		printk("Unable to allocate a page for reading the image "
+				"signature.\n");
+		return -ENOMEM;
+	}
+
+	suspend_bio_ops.bdev_page_io(READ, filewriter_target_bdev,
+			target_firstblock,
+			virt_to_page(cur));
+
+	header = (struct filewriter_header *) cur;
+	result = parse_signature(header);
+		
+	switch (op) {
+		case INVALIDATE:
+			if (result == -1)
+				goto out;
+
+			strcpy(header->sig, NoImage);
+			header->resumed_before = 0;
+			result = changed = 1;
+			break;
+		case MARK_RESUME_ATTEMPTED:
+			if (result == 1) {
+				header->resumed_before = 1;
+				changed = 1;
+			}
+			break;
+	}
+
+	if (changed)
+		suspend_bio_ops.bdev_page_io(WRITE, filewriter_target_bdev,
+				target_firstblock,
+				virt_to_page(cur));
+
+out:
+	suspend_bio_ops.finish_all_io();
+	free_page((unsigned long) cur);
+	return result;
+}
+
+/* Print debug info
+ *
+ * Description:
+ */
+
+static int filewriter_print_debug_stats(char *buffer, int size)
+{
+	int len = 0;
+	
+	if (suspend_active_writer != &filewriterops) {
+		len = snprintf_used(buffer, size, "- Filewriter inactive.\n");
+		return len;
+	}
+
+	len = snprintf_used(buffer, size, "- Filewriter active.\n");
+
+	len+= snprintf_used(buffer+len, size-len, "  Storage available for image: "
+			"%ld pages.\n",
+			filewriter_storage_allocated());
+
+	return len;
+}
+
+/*
+ * Storage needed
+ *
+ * Returns amount of space in the image header required
+ * for the filewriter's data.
+ *
+ * We ensure the space is allocated, but actually save the
+ * data from write_header_init and therefore don't also define a
+ * save_config_info routine.
+ */
+static unsigned long filewriter_storage_needed(void)
+{
+	return sig_size + strlen(filewriter_target) + 1 +
+		3 * sizeof(struct extent_iterate_saved_state) +
+		sizeof(devinfo) +
+		sizeof(struct extent_chain) - 2 * sizeof(void *) +
+		(2 * sizeof(unsigned long) *
+		 (block_chain.allocs - block_chain.frees));
+}
+
+/* 
+ * filewriter_invalidate_image
+ * 
+ */
+static int filewriter_invalidate_image(void)
+{
+	int result;
+
+	if (nr_suspends > 0)
+		filewriter_release_storage();
+
+	result = filewriter_signature_op(INVALIDATE);
+	if (result == 1 && !nr_suspends)
+		printk(KERN_WARNING name_suspend "Image invalidated.\n");
+
+	return result;
+}
+
+/*
+ * Image_exists
+ *
+ */
+
+static int filewriter_image_exists(void)
+{
+	if (!filewriter_target_bdev)
+		reopen_resume_devt();
+
+	return filewriter_signature_op(GET_IMAGE_EXISTS);
+}
+
+/*
+ * Mark resume attempted.
+ *
+ * Record that we tried to resume from this image.
+ */
+
+static void filewriter_mark_resume_attempted(void)
+{
+	filewriter_signature_op(MARK_RESUME_ATTEMPTED);
+}
+
+static void filewriter_set_resume2(void)
+{
+	char *buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	char *buffer2 = (char *) get_zeroed_page(GFP_ATOMIC);
+	unsigned long sector = bmap(target_inode, 0);
+	int offset = 0;
+
+	if (filewriter_target_bdev) {
+		set_devinfo(filewriter_target_bdev, target_inode->i_blkbits);
+
+		bdevname(filewriter_target_bdev, buffer2);
+		offset += snprintf(buffer + offset, PAGE_SIZE - offset, 
+				"/dev/%s", buffer2);
+		
+		if (sector)
+			offset += snprintf(buffer + offset, PAGE_SIZE - offset,
+				":0x%lx", sector << devinfo.bmap_shift);
+	} else
+		offset += snprintf(buffer + offset, PAGE_SIZE - offset,
+				"%s is not a valid target.", filewriter_target);
+			
+	sprintf(resume2_file, "file:%s", buffer);
+
+	free_page((unsigned long) buffer);
+	free_page((unsigned long) buffer2);
+
+	suspend_attempt_to_parse_resume_device();
+}
+
+static int __test_filewriter_target(char *target, int resume_time)
+{
+	filewriter_get_target_info(target, 0, resume_time);
+	if (filewriter_signature_op(GET_IMAGE_EXISTS) > -1) {
+		printk(name_suspend "Filewriter: File signature found.\n");
+		if (!resume_time)
+			filewriter_set_resume2();
+		
+		suspend_bio_ops.set_devinfo(&devinfo);
+		suspend_writer_posn.chains = &block_chain;
+		suspend_writer_posn.num_chains = 1;
+
+		set_suspend_state(SUSPEND_CAN_SUSPEND);
+		return 0;
+	}
+
+	clear_suspend_state(SUSPEND_CAN_SUSPEND);
+
+	if (*target)
+		printk(name_suspend
+			"Filewriter: Sorry. No signature found at %s.\n",
+			target);
+	else
+		if (!resume_time)
+			printk(name_suspend
+				"Filewriter: Sorry. Target is not set for suspending.\n");
+
+	return 1;
+}
+
+static void test_filewriter_target(void)
+{
+	int cant_suspend;
+
+	setting_filewriter_target = 1;
+       	
+	printk(name_suspend "Filewriter: Testing whether you can suspend:\n");
+	cant_suspend =__test_filewriter_target(filewriter_target, 0);
+
+	printk(name_suspend "Suspending %sabled.\n",  cant_suspend ? "dis" : "en");
+	
+	setting_filewriter_target = 0;
+}
+
+/*
+ * Parse Image Location
+ *
+ * Attempt to parse a resume2= parameter.
+ * Swap Writer accepts:
+ * resume2=file:DEVNAME[:FIRSTBLOCK]
+ *
+ * Where:
+ * DEVNAME is convertable to a dev_t by name_to_dev_t
+ * FIRSTBLOCK is the location of the first block in the file.
+ * BLOCKSIZE is the logical blocksize >= SECTOR_SIZE & <= PAGE_SIZE, 
+ * mod SECTOR_SIZE == 0 of the device.
+ * Data is validated by attempting to read a header from the
+ * location given. Failure will result in filewriter refusing to
+ * save an image, and a reboot with correct parameters will be
+ * necessary.
+ */
+
+static int filewriter_parse_sig_location(char *commandline, int only_writer)
+{
+	char *thischar, *devstart = NULL, *colon = NULL, *at_symbol = NULL;
+	int result = -EINVAL, target_blocksize = 0;
+
+	if (strncmp(commandline, "file:", 5)) {
+		if (!only_writer)
+			return 1;
+	} else
+		commandline += 5;
+
+	/* 
+	 * Don't check signature again if we're beginning a cycle. If we already
+	 * did the initialisation successfully, assume we'll be okay when it comes
+	 * to resuming.
+	 */
+	if (filewriter_target_bdev)
+		return 0;
+	
+	devstart = thischar = commandline;
+	while ((*thischar != ':') && (*thischar != '@') &&
+		((thischar - commandline) < 250) && (*thischar))
+		thischar++;
+
+	if (*thischar == ':') {
+		colon = thischar;
+		*colon = 0;
+		thischar++;
+	}
+
+	while ((*thischar != '@') && ((thischar - commandline) < 250) && (*thischar))
+		thischar++;
+
+	if (*thischar == '@') {
+		at_symbol = thischar;
+		*at_symbol = 0;
+	}
+	
+	/* 
+	 * For the filewriter, you can be able to resume, but not suspend,
+	 * because the resume2= is set correctly, but the filewriter_target
+	 * isn't. 
+	 *
+	 * We may have come here as a result of setting resume2 or
+	 * filewriter_target. We only test the filewriter target in the
+	 * former case (it's already done in the later), and we do it before
+	 * setting the block number ourselves. It will overwrite the values
+	 * given on the command line if we don't.
+	 */
+
+	if (!setting_filewriter_target)
+		__test_filewriter_target(filewriter_target, 1);
+
+	if (colon)
+		target_firstblock = (int) simple_strtoul(colon + 1, NULL, 0);
+	else
+		target_firstblock = 0;
+
+	if (at_symbol) {
+		target_blocksize = (int) simple_strtoul(at_symbol + 1, NULL, 0);
+		if (target_blocksize & (SECTOR_SIZE - 1)) {
+			printk("Filewriter: Blocksizes are multiples of %d.\n", SECTOR_SIZE);
+			result = -EINVAL;
+			goto out;
+		}
+	}
+	
+	printk("Suspend2 Filewriter: Testing whether you can resume:\n");
+
+	filewriter_get_target_info(commandline, 0, 1);
+
+	if (!filewriter_target_bdev || IS_ERR(filewriter_target_bdev)) {
+		filewriter_target_bdev = NULL;
+		result = -1;
+		goto out;
+	}
+
+	if (target_blocksize)
+		set_devinfo(filewriter_target_bdev, ffs(target_blocksize));
+
+	result = __test_filewriter_target(commandline, 1);
+
+out:
+	if (result)
+		clear_suspend_state(SUSPEND_CAN_SUSPEND);
+	else
+		set_suspend_state(SUSPEND_CAN_SUSPEND);
+
+	printk("Resuming %sabled.\n",  result ? "dis" : "en");
+
+	if (colon)
+		*colon = ':';
+	if (at_symbol)
+		*at_symbol = '@';
+
+	return result;
+}
+
+/* filewriter_save_config_info
+ *
+ * Description:	Save the target's name, not for resume time, but for all_settings.
+ * Arguments:	Buffer:		Pointer to a buffer of size PAGE_SIZE.
+ * Returns:	Number of bytes used for saving our data.
+ */
+
+static int filewriter_save_config_info(char *buffer)
+{
+	strcpy(buffer, filewriter_target);
+	return strlen(filewriter_target) + 1;
+}
+
+/* filewriter_load_config_info
+ *
+ * Description:	Reload target's name.
+ * Arguments:	Buffer:		Pointer to the start of the data.
+ *		Size:		Number of bytes that were saved.
+ */
+
+static void filewriter_load_config_info(char *buffer, int size)
+{
+	strcpy(filewriter_target, buffer);
+}
+
+static int filewriter_initialise(int starting_cycle)
+{
+	int result = 0;
+
+	if (starting_cycle) {
+	       if (suspend_active_writer != &filewriterops)
+			return 0;
+
+		if (!*filewriter_target) {
+			printk("Filewriter is the active writer,  but no filename has been set.\n");
+			return 1;
+		}
+	}
+
+	if (filewriter_target)
+		filewriter_get_target_info(filewriter_target, starting_cycle, 0);
+
+	if (starting_cycle && (filewriter_image_exists() == -1)) {
+		printk("%s is does not have a valid signature for suspending.\n",
+				filewriter_target);
+		result = 1;
+	}
+
+	return result;
+}
+
+static struct suspend_sysfs_data filewriter_sysfs_data[] = {
+
+	{
+	 SUSPEND2_ATTR("target", SYSFS_RW),
+	 SYSFS_STRING(filewriter_target, 256, SYSFS_NEEDS_FOR_WRITE),
+	 .write_side_effect		= test_filewriter_target,
+	},
+
+	{
+	  SUSPEND2_ATTR("enabled", SYSFS_RW),
+	  SYSFS_INT(&filewriterops.enabled, 0, 1),
+	  .write_side_effect		= attempt_to_parse_resume_device2,
+	}
+};
+
+static struct suspend_module_ops filewriterops = {
+	.type					= WRITER_MODULE,
+	.name					= "File Writer",
+	.module					= THIS_MODULE,
+	.print_debug_info			= filewriter_print_debug_stats,
+	.save_config_info			= filewriter_save_config_info,
+	.load_config_info			= filewriter_load_config_info,
+	.storage_needed				= filewriter_storage_needed,
+	.initialise				= filewriter_initialise,
+	.cleanup				= filewriter_cleanup,
+
+	.storage_available 	= filewriter_storage_available,
+	.storage_allocated	= filewriter_storage_allocated,
+	.release_storage	= filewriter_release_storage,
+	.allocate_header_space	= filewriter_allocate_header_space,
+	.allocate_storage	= filewriter_allocate_storage,
+	.image_exists		= filewriter_image_exists,
+	.mark_resume_attempted	= filewriter_mark_resume_attempted,
+	.write_header_init	= filewriter_write_header_init,
+	.write_header_cleanup	= filewriter_write_header_cleanup,
+	.read_header_init	= filewriter_read_header_init,
+	.read_header_cleanup	= filewriter_read_header_cleanup,
+	.invalidate_image	= filewriter_invalidate_image,
+	.parse_sig_location	= filewriter_parse_sig_location,
+};
+
+/* ---- Registration ---- */
+static __init int filewriter_load(void)
+{
+	int result;
+	int i,
+	    numfiles = sizeof(filewriter_sysfs_data) / 
+		    sizeof(struct suspend_sysfs_data);
+	
+	printk("Suspend2 FileWriter loading.\n");
+
+	filewriterops.read_chunk = suspend_bio_ops.read_chunk;
+	filewriterops.write_chunk = suspend_bio_ops.write_chunk;
+	filewriterops.rw_init = suspend_bio_ops.rw_init;
+	filewriterops.rw_cleanup = suspend_bio_ops.rw_cleanup;
+	filewriterops.rw_header_chunk =
+		suspend_bio_ops.rw_header_chunk;
+
+	if (!(result = suspend_register_module(&filewriterops))) {
+		struct kobject *kobj = make_suspend2_sysdir("filewriter");
+		for (i=0; i< numfiles; i++)
+			suspend_register_sysfs_file(kobj,
+					&filewriter_sysfs_data[i]);
+	} else
+		printk("Suspend2 FileWriter unable to register!\n");
+
+	return result;
+}
+
+#ifdef MODULE
+static __exit void filewriter_unload(void)
+{
+	int i, numfiles = sizeof(filewriter_sysfs_data) /
+		sizeof(struct suspend_sysfs_data);
+
+	printk("Suspend2 FileWriter unloading.\n");
+
+	for (i=0; i< numfiles; i++)
+		suspend_unregister_sysfs_file(&filewriter_sysfs_data[i]);
+	suspend_unregister_module(&filewriterops);
+}
+
+module_init(filewriter_load);
+module_exit(filewriter_unload);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nigel Cunningham");
+MODULE_DESCRIPTION("Suspend2 filewriter");
+#else
+late_initcall(filewriter_load);
+#endif
diff -urN oldtree/kernel/power/suspend_swap.c newtree/kernel/power/suspend_swap.c
--- oldtree/kernel/power/suspend_swap.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/suspend_swap.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,1229 @@
+/*
+ * kernel/power/suspend_swap.c
+ *
+ * Copyright 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * Distributed under GPLv2.
+ * 
+ * This file encapsulates functions for usage of swap space as a
+ * backing store.
+ */
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/swapops.h>
+#include <linux/swap.h>
+
+#include "suspend.h"
+#include "sysfs.h"
+#include "modules.h"
+#include "io.h"
+#include "ui.h"
+#include "extent.h"
+#include "block_io.h"
+
+static struct suspend_module_ops swapwriterops;
+
+#define SIGNATURE_VER 6
+
+/* --- Struct of pages stored on disk */
+
+union diskpage {
+	union swap_header swh;	/* swh.magic is the only member used */
+};
+
+union p_diskpage {
+	union diskpage *pointer;
+	char *ptr;
+        unsigned long address;
+};
+
+/* Devices used for swap */
+static struct suspend_bdev_info devinfo[MAX_SWAPFILES];
+
+/* Extent chains for swap & blocks */
+struct extent_chain swapextents;
+struct extent_chain block_chain[MAX_SWAPFILES];
+
+static dev_t header_dev_t;
+static struct block_device *header_block_device;
+static unsigned long headerblock;
+
+/* For swapfile automatically swapon/off'd. */
+static char swapfilename[32] = "";
+extern asmlinkage long sys_swapon(const char *specialfile, int swap_flags);
+extern asmlinkage long sys_swapoff(const char *specialfile);
+static int suspend_swapon_status;
+
+/* Header Page Information */
+static int header_pages_allocated;
+
+/* User Specified Parameters. */
+
+static unsigned long resume_firstblock;
+static int resume_blocksize;
+static dev_t resume_dev_t;
+static struct block_device *resume_block_device;
+
+struct sysinfo swapinfo;
+static int swapwriter_invalidate_image(void);
+
+/* Block devices open. */
+struct bdev_opened
+{
+	dev_t device;
+	struct block_device *bdev;
+	int claimed;
+};
+
+/* 
+ * Entry MAX_SWAPFILES is the resume block device, which may
+ * not be a swap device enabled when we suspend.
+ * Entry MAX_SWAPFILES + 1 is the header block device, which
+ * is needed before we find out which slot it occupies.
+ */
+static struct bdev_opened *bdev_info_list[MAX_SWAPFILES + 2];
+       
+static void close_bdev(int i)
+{
+	struct bdev_opened *this = bdev_info_list[i];
+
+	if (this->claimed)
+		bd_release(this->bdev);
+
+	/* Release our reference. */
+	blkdev_put(this->bdev);
+
+	/* Free our info. */
+	kfree(this);
+
+	bdev_info_list[i] = NULL;
+}
+
+static void close_bdevs(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_SWAPFILES; i++)
+		if (bdev_info_list[i])
+			close_bdev(i);
+
+	resume_block_device = header_block_device = NULL;
+}
+
+static struct block_device *open_bdev(int index, dev_t device)
+{
+	struct bdev_opened *this;
+	struct block_device *bdev;
+
+	if (bdev_info_list[index] && (bdev_info_list[index]->device == device)){
+		bdev = bdev_info_list[index]->bdev;
+		return bdev;
+	}
+	
+	if (bdev_info_list[index] && bdev_info_list[index]->device != device)
+		close_bdev(index);
+
+	bdev = open_by_devnum(device, FMODE_READ);
+
+	if (IS_ERR(bdev) || !bdev) {
+		suspend_early_boot_message(1,SUSPEND_CONTINUE_REQ,  
+				"Failed to get access to block device "
+				"\"%s\" (error %d).\n Maybe you need "
+				"to run mknod and/or lvmsetup in an "
+				"initrd/ramfs?", device, bdev);
+		return ERR_PTR(-EINVAL);
+	}
+
+	this = kmalloc(sizeof(struct bdev_opened), GFP_KERNEL);
+	BUG_ON(!this);
+
+	bdev_info_list[index] = this;
+	this->device = device;
+	this->bdev = bdev;
+
+	if (index < MAX_SWAPFILES)
+		devinfo[index].bdev = bdev;
+
+	return bdev;
+}
+
+/* Must be silent - might be called from cat /sys/power/suspend2/debug_info
+ * Returns 0 if was off, -EBUSY if was on, error value otherwise.
+ */
+static int enable_swapfile(void)
+{
+	int activateswapresult = -EINVAL;
+
+	if (suspend_swapon_status)
+		return 0;
+
+	if (swapfilename[0]) {
+		/* Attempt to swap on with maximum priority */
+		activateswapresult = sys_swapon(swapfilename, 0xFFFF);
+		if ((activateswapresult) && (activateswapresult != -EBUSY))
+			printk(name_suspend
+				"The swapfile/partition specified by "
+				"/sys/power/suspend2/swapwriter/swapfile (%s) could not"
+				" be turned on (error %d). Attempting "
+				"to continue.\n",
+				swapfilename, activateswapresult);
+		if (!activateswapresult)
+			suspend_swapon_status = 1;
+	}
+	return activateswapresult;
+}
+
+/* Returns 0 if was on, -EINVAL if was off, error value otherwise */
+static int disable_swapfile(void)
+{
+	int result = -EINVAL;
+	
+	if (!suspend_swapon_status)
+		return 0;
+
+	if (swapfilename[0]) {
+		result = sys_swapoff(swapfilename);
+		if (result == -EINVAL)
+	 		return 0;	/* Wasn't on */
+		if (!result)
+			suspend_swapon_status = 0;
+	}
+
+	return result;
+}
+
+static int try_to_parse_resume_device(char *commandline)
+{
+	struct kstat stat;
+	int error;
+
+	resume_dev_t = name_to_dev_t(commandline);
+
+	if (!resume_dev_t) {
+		error = vfs_stat(commandline, &stat);
+		if (!error)
+			resume_dev_t = stat.rdev;
+	}
+
+	if (!resume_dev_t) {
+		if (test_suspend_state(SUSPEND_TRYING_TO_RESUME))
+			suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ,
+			  "Failed to translate \"%s\" into a device id.\n",
+			  commandline);
+		else
+			printk(name_suspend
+			  "Can't translate \"%s\" into a device id yet.\n",
+			  commandline);
+		return 1;
+	}
+
+	if (IS_ERR(resume_block_device =
+	     open_bdev(MAX_SWAPFILES, resume_dev_t))) {
+		suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ,
+			"Failed to get access to \"%s\", where"
+			" the swap header should be found.",
+			commandline);
+		return 1;
+	}
+
+	return 0;
+}
+
+/* 
+ * If we have read part of the image, we might have filled  memory with
+ * data that should be zeroed out.
+ */
+static void swapwriter_noresume_reset(void)
+{
+	memset((char *) &devinfo, 0, sizeof(devinfo));
+	close_bdevs();
+}
+
+static int parse_signature(char *header, int restore)
+{
+	int type = -1;
+
+	if (!memcmp("SWAP-SPACE",header,10))
+		return 0;
+	else if (!memcmp("SWAPSPACE2",header,10))
+		return 1;
+
+	else if (!memcmp("S1SUSP",header,6))
+		type = 2;
+	else if (!memcmp("S2SUSP",header,6))
+		type = 3;
+	else if (!memcmp("S1SUSPEND",header,9))
+		type = 4;
+	
+	else if (!memcmp("z",header,1))
+		type = 12;
+	else if (!memcmp("Z",header,1))
+		type = 13;
+	
+	/* 
+	 * Put bdev of suspend header in last byte of swap header
+	 * (unsigned short)
+	 */
+	if (type > 11) {
+		dev_t *header_ptr = (dev_t *) &header[1];
+		unsigned char *headerblocksize_ptr =
+			(unsigned char *) &header[5];
+		u32 *headerblock_ptr = (u32 *) &header[6];
+		header_dev_t = *header_ptr;
+		/* 
+		 * We are now using the highest bit of the char to indicate
+		 * whether we have attempted to resume from this image before.
+		 */
+		clear_suspend_state(SUSPEND_RESUMED_BEFORE);
+		if (((int) *headerblocksize_ptr) & 0x80)
+			set_suspend_state(SUSPEND_RESUMED_BEFORE);
+		headerblock = (unsigned long) *headerblock_ptr;
+	}
+
+	if ((restore) && (type > 5)) {
+		/* We only reset our own signatures */
+		if (type & 1)
+			memcpy(header,"SWAPSPACE2",10);
+		else
+			memcpy(header,"SWAP-SPACE",10);
+	}
+
+	return type;
+}
+
+/*
+ * prepare_signature
+ */
+static int prepare_signature(dev_t bdev, unsigned long block,
+		char *current_header)
+{
+	int current_type = parse_signature(current_header, 0);
+	dev_t *header_ptr = (dev_t *) (&current_header[1]);
+	unsigned long *headerblock_ptr =
+		(unsigned long *) (&current_header[6]);
+
+	if ((current_type > 1) && (current_type < 6))
+		return 1;
+
+	/* At the moment, I don't have a way to handle the block being
+	 * > 32 bits. Not enough room in the signature and no way to
+	 * safely put the data elsewhere. */
+
+	if (BITS_PER_LONG == 64 && ffs(block) > 31) {
+		suspend_prepare_status(DONT_CLEAR_BAR,
+			"Header sector requires 33+ bits. "
+			"Would not be able to resume.");
+		return 1;
+	}
+
+	if (current_type & 1)
+		current_header[0] = 'Z';
+	else
+		current_header[0] = 'z';
+	*header_ptr = bdev;
+	/* prev is the first/last swap page of the resume area */
+	*headerblock_ptr = (unsigned long) block; 
+	return 0;
+}
+
+static int swapwriter_allocate_storage(int space_requested);
+
+static int swapwriter_allocate_header_space(int space_requested)
+{
+	int i;
+
+	if (!swapextents.size)
+		swapwriter_allocate_storage(space_requested);
+
+	suspend_extent_state_goto_start(&suspend_writer_posn);
+	suspend_bio_ops.forward_one_page(); /* To first page */
+	
+	for (i = 0; i < space_requested; i++) {
+		if (suspend_bio_ops.forward_one_page()) {
+			printk("Out of space while seeking to allocate "
+					"header pages,\n");
+			header_pages_allocated = i;
+			return -ENOSPC;
+		}
+
+	}
+
+	header_pages_allocated = space_requested;
+
+	/* The end of header pages will be the start of pageset 2;
+	 * we are now sitting on the first pageset2 page. */
+	suspend_extent_state_save(&suspend_writer_posn,
+			&suspend_writer_posn_save[2]);
+	return 0;
+}
+
+static void get_main_pool_phys_params(void)
+{
+	struct extent *extentpointer = NULL;
+	unsigned long address;
+	int i, extent_min = -1, extent_max = -1, last_chain = -1;
+
+	for (i = 0; i < MAX_SWAPFILES; i++)
+		if (block_chain[i].first)
+			suspend_put_extent_chain(&block_chain[i]);
+
+	suspend_extent_for_each(&swapextents, extentpointer, address) {
+		swp_entry_t swap_address = extent_val_to_swap_entry(address);
+		pgoff_t offset = swp_offset(swap_address);
+		unsigned swapfilenum = swp_type(swap_address);
+		struct swap_info_struct *sis = get_swap_info_struct(swapfilenum);
+		sector_t new_sector = map_swap_page(sis, offset);
+
+		if ((new_sector == extent_max + 1) &&
+		    (last_chain == swapfilenum))
+			extent_max++;
+		else {
+			if (extent_min > -1) {
+				if (test_action_state(SUSPEND_TEST_BIO))
+					printk("Adding extent chain %d %d-%d.\n",
+						swapfilenum,
+						extent_min <<
+						 devinfo[last_chain].bmap_shift,
+						extent_max <<
+						 devinfo[last_chain].bmap_shift);
+						
+				suspend_add_to_extent_chain(
+					&block_chain[last_chain],
+					extent_min, extent_max);
+			}
+			extent_min = extent_max = new_sector;
+			last_chain = swapfilenum;
+		}
+	}
+
+	if (extent_min > -1) {
+		if (test_action_state(SUSPEND_TEST_BIO))
+			printk("Adding extent chain %d %d-%d.\n",
+				last_chain,
+				extent_min <<
+					devinfo[last_chain].bmap_shift,
+				extent_max <<
+					devinfo[last_chain].bmap_shift);
+		suspend_add_to_extent_chain(
+			&block_chain[last_chain],
+			extent_min, extent_max);
+	}
+
+	swapwriter_allocate_header_space(header_pages_allocated);
+}
+
+static int swapwriter_storage_allocated(void)
+{
+	return swapextents.size;
+}
+
+static int swapwriter_storage_available(void)
+{
+	si_swapinfo(&swapinfo);
+	return swapinfo.freeswap + swapwriter_storage_allocated();
+}
+
+static int swapwriter_initialise(int starting_cycle)
+{
+	if (!starting_cycle)
+		return 0;
+
+	enable_swapfile();
+
+	if (resume_dev_t && !resume_block_device &&
+	    IS_ERR(resume_block_device =
+	    		open_bdev(MAX_SWAPFILES, resume_dev_t)))
+		return 1;
+	
+	return 0;
+}
+
+static void swapwriter_cleanup(int ending_cycle)
+{
+	if (ending_cycle)
+		disable_swapfile();
+	
+	close_bdevs();
+}
+
+static int swapwriter_release_storage(void)
+{
+	int i = 0;
+
+	if (test_action_state(SUSPEND_KEEP_IMAGE) &&
+	    test_suspend_state(SUSPEND_NOW_RESUMING))
+		return 0;
+
+	header_pages_allocated = 0;
+
+	if (swapextents.first) {
+		/* Free swap entries */
+		struct extent *extentpointer;
+		unsigned long extentvalue;
+		swp_entry_t entry;
+		suspend_extent_for_each(&swapextents, extentpointer, 
+				extentvalue) {
+			entry = extent_val_to_swap_entry(extentvalue);
+			swap_free(entry);
+		}
+
+		suspend_put_extent_chain(&swapextents);
+
+		for (i = 0; i < MAX_SWAPFILES; i++)
+			if (block_chain[i].first)
+				suspend_put_extent_chain(&block_chain[i]);
+	}
+
+	return 0;
+}
+
+/* 
+ * Round robin allocation (where swap storage has the same priority).
+ * could make this very inefficient, so we track extents allocated on
+ * a per-swapfiles basis.
+ */
+static int swapwriter_allocate_storage(int space_requested)
+{
+	int i, result = 0, first[MAX_SWAPFILES];
+	int pages_to_get = space_requested - swapextents.size;
+	unsigned long extent_min[MAX_SWAPFILES], extent_max[MAX_SWAPFILES];
+
+	if (pages_to_get < 1)
+		return 0;
+
+	for (i=0; i < MAX_SWAPFILES; i++) {
+		struct swap_info_struct *si = get_swap_info_struct(i);
+		if ((devinfo[i].bdev = si->bdev))
+			devinfo[i].dev_t = si->bdev->bd_dev;
+		devinfo[i].bmap_shift = 3;
+		devinfo[i].blocks_per_page = 1;
+		first[i] = 1;
+	}
+
+	for(i=0; i < pages_to_get; i++) {
+		swp_entry_t entry;
+		unsigned long new_value;
+		unsigned swapfilenum;
+
+		entry = get_swap_page();
+		if (!entry.val) {
+			printk("Failed to get a swap page.\n");
+			result = -ENOSPC;
+			break;
+		}
+
+		swapfilenum = swp_type(entry);
+		new_value = swap_entry_to_extent_val(entry);
+		if (first[swapfilenum]) {
+			first[swapfilenum] = 0;
+			extent_min[swapfilenum] = extent_max[swapfilenum] =
+				new_value;
+		} else {
+			if (new_value == extent_max[swapfilenum] + 1)
+				extent_max[swapfilenum]++;
+			else {
+				suspend_add_to_extent_chain(
+					&swapextents,
+					extent_min[swapfilenum],
+					extent_max[swapfilenum]);
+				extent_min[swapfilenum] =
+					extent_max[swapfilenum] = new_value;
+			}
+		}
+	}
+
+	for (i = 0; i < MAX_SWAPFILES; i++)
+		if (!first[i])
+			suspend_add_to_extent_chain(
+				&swapextents,
+				extent_min[i], extent_max[i]);
+
+	get_main_pool_phys_params();
+	return result;
+}
+
+static int swapwriter_write_header_init(void)
+{
+	int i, result;
+	struct swap_info_struct *si;
+
+	suspend_header_bytes_used = 0;
+
+	suspend_extent_state_goto_start(&suspend_writer_posn);
+	/* Forward one page will be done prior to the read */
+
+	for (i = 0; i < MAX_SWAPFILES; i++) {
+		si = get_swap_info_struct(i);
+		if (si->swap_file)
+			devinfo[i].dev_t = si->bdev->bd_dev;
+		else
+			devinfo[i].dev_t = (dev_t) 0;
+	}
+
+	suspend_writer_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	if (!suspend_writer_buffer) {
+		printk("Failed to get swapwriter buffer.\n");
+		return -ENOMEM;
+	}
+
+	suspend_writer_buffer_posn = 0;
+
+	/* Info needed to bootstrap goes at the start of the header.
+	 * First we save the positions and devinfo, including the number
+	 * of header pages. Then we save the structs containing data needed
+	 * for reading the header pages back.
+	 * Note that even if header pages take more than one page, when we
+	 * read back the info, we will have restored the location of the
+	 * next header page by the time we go to use it.
+	 */
+
+	if ((result = suspend_bio_ops.rw_header_chunk(WRITE,
+			&swapwriterops,
+			(char *) &suspend_writer_posn_save, 
+			sizeof(suspend_writer_posn_save))))
+		return result;
+
+	if ((result = suspend_bio_ops.rw_header_chunk(WRITE,
+			&swapwriterops,
+			(char *) &devinfo, sizeof(devinfo))))
+		return result;
+
+	for (i=0; i < MAX_SWAPFILES; i++)
+		suspend_serialise_extent_chain(&swapwriterops, &block_chain[i]);
+
+	return 0;
+}
+
+static int swapwriter_write_header_cleanup(void)
+{
+	int result;
+	struct swap_info_struct *si;
+
+	/* Write any unsaved data */
+	if (suspend_writer_buffer_posn)
+		suspend_bio_ops.write_header_chunk_finish();
+
+	suspend_bio_ops.finish_all_io();
+
+	suspend_extent_state_goto_start(&suspend_writer_posn);
+	suspend_bio_ops.forward_one_page();
+
+	/* Adjust swap header */
+	suspend_bio_ops.bdev_page_io(READ, resume_block_device,
+			resume_firstblock,
+			virt_to_page(suspend_writer_buffer));
+
+	si = get_swap_info_struct(suspend_writer_posn.current_chain);
+	result = prepare_signature(si->bdev->bd_dev,
+			suspend_writer_posn.current_offset,
+		((union swap_header *) suspend_writer_buffer)->magic.magic);
+		
+	if (!result)
+		suspend_bio_ops.bdev_page_io(WRITE, resume_block_device,
+			resume_firstblock,
+			virt_to_page(suspend_writer_buffer));
+
+	free_page((unsigned long) suspend_writer_buffer);
+	suspend_writer_buffer = NULL;
+	
+	suspend_bio_ops.finish_all_io();
+
+	return result;
+}
+
+/* ------------------------- HEADER READING ------------------------- */
+
+/*
+ * read_header_init()
+ * 
+ * Description:
+ * 1. Attempt to read the device specified with resume2=.
+ * 2. Check the contents of the swap header for our signature.
+ * 3. Warn, ignore, reset and/or continue as appropriate.
+ * 4. If continuing, read the swapwriter configuration section
+ *    of the header and set up block device info so we can read
+ *    the rest of the header & image.
+ *
+ * Returns:
+ * May not return if user choose to reboot at a warning.
+ * -EINVAL if cannot resume at this time. Booting should continue
+ * normally.
+ */
+
+static int swapwriter_read_header_init(void)
+{
+	int i;
+
+	BUG_ON(!resume_block_device);
+	BUG_ON(!resume_dev_t);
+
+	suspend_header_bytes_used = 0;
+
+	suspend_writer_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+
+	BUG_ON(!suspend_writer_buffer);
+
+	if (!header_dev_t) {
+		printk("read_header_init called when we haven't "
+				"verified there is an image!\n");
+		return -EINVAL;
+	}
+
+	/* 
+	 * If the header is not on the resume_dev_t, get the resume device first.
+	 */
+	if (header_dev_t != resume_dev_t) {
+		header_block_device = open_bdev(MAX_SWAPFILES + 1,
+				header_dev_t);
+
+		if (IS_ERR(header_block_device))
+			return PTR_ERR(header_block_device);
+	} else
+		header_block_device = resume_block_device;
+
+	/* 
+	 * Read swapwriter configuration.
+	 * Headerblock size taken into account already.
+	 */
+	suspend_bio_ops.bdev_page_io(READ, header_block_device,
+			headerblock << 3,
+			virt_to_page((unsigned long) suspend_writer_buffer));
+
+	memcpy(&suspend_writer_posn_save, suspend_writer_buffer, 3 * sizeof(struct extent_iterate_saved_state));
+
+	suspend_writer_buffer_posn = 3 * sizeof(struct extent_iterate_saved_state);
+	suspend_header_bytes_used += 3 * sizeof(struct extent_iterate_saved_state);
+
+	memcpy(&devinfo, suspend_writer_buffer + suspend_writer_buffer_posn, sizeof(devinfo));
+
+	suspend_writer_buffer_posn += sizeof(devinfo);
+	suspend_header_bytes_used += sizeof(devinfo);
+
+	/* Restore device info */
+	for (i = 0; i < MAX_SWAPFILES; i++) {
+		dev_t thisdevice = devinfo[i].dev_t;
+		struct block_device *result;
+
+		devinfo[i].bdev = NULL;
+
+		if (!thisdevice)
+			continue;
+
+		if (thisdevice == resume_dev_t) {
+			devinfo[i].bdev = resume_block_device;
+			bdev_info_list[i] = bdev_info_list[MAX_SWAPFILES];
+			BUG_ON(!bdev_info_list[i]);
+			bdev_info_list[MAX_SWAPFILES] = NULL;
+			continue;
+		}
+
+		if (thisdevice == header_dev_t) {
+			devinfo[i].bdev = header_block_device;
+			bdev_info_list[i] = bdev_info_list[MAX_SWAPFILES + 1];
+			BUG_ON(!bdev_info_list[i]);
+			bdev_info_list[MAX_SWAPFILES + 1] = NULL;
+			continue;
+		}
+
+		result = open_bdev(i, thisdevice);
+		if (IS_ERR(result)) {
+			close_bdevs();
+			return PTR_ERR(result);
+		}
+	}
+
+	suspend_extent_state_goto_start(&suspend_writer_posn);
+	suspend_bio_ops.set_extra_page_forward();
+
+	for (i = 0; i < MAX_SWAPFILES; i++)
+		suspend_load_extent_chain(&block_chain[i]);
+
+	return 0;
+}
+
+static int swapwriter_read_header_cleanup(void)
+{
+	free_page((unsigned long) suspend_writer_buffer);
+	return 0;
+}
+
+/* swapwriter_invalidate_image
+ * 
+ */
+static int swapwriter_invalidate_image(void)
+{
+	union p_diskpage cur;
+	int result = 0;
+	char newsig[11];
+	
+	cur.address = get_zeroed_page(GFP_ATOMIC);
+	if (!cur.address) {
+		printk("Unable to allocate a page for restoring the swap signature.\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * If nr_suspends == 0, we must be booting, so no swap pages
+	 * will be recorded as used yet.
+	 */
+
+	if (nr_suspends > 0)
+		swapwriter_release_storage();
+
+	/* 
+	 * We don't do a sanity check here: we want to restore the swap 
+	 * whatever version of kernel made the suspend image.
+	 * 
+	 * We need to write swap, but swap may not be enabled so
+	 * we write the device directly
+	 */
+	
+	suspend_bio_ops.bdev_page_io(READ, resume_block_device,
+			resume_firstblock,
+			virt_to_page(cur.pointer));
+
+	result = parse_signature(cur.pointer->swh.magic.magic, 1);
+		
+	if (result < 5)
+		goto out;
+
+	strncpy(newsig, cur.pointer->swh.magic.magic, 10);
+	newsig[10] = 0;
+
+	suspend_bio_ops.bdev_page_io(WRITE, resume_block_device,
+			resume_firstblock,
+			virt_to_page(cur.pointer));
+
+	if (!nr_suspends)
+		printk(KERN_WARNING name_suspend "Image invalidated.\n");
+out:
+	suspend_bio_ops.finish_all_io();
+	free_page(cur.address);
+	return 0;
+}
+
+/*
+ * workspace_size
+ *
+ * Description:
+ * Returns the number of bytes of RAM needed for this
+ * code to do its work. (Used when calculating whether
+ * we have enough memory to be able to suspend & resume).
+ *
+ */
+static unsigned long swapwriter_memory_needed(void)
+{
+	return 1;
+}
+
+/*
+ * Print debug info
+ *
+ * Description:
+ */
+static int swapwriter_print_debug_stats(char *buffer, int size)
+{
+	int len = 0;
+	struct sysinfo sysinfo;
+	
+	if (suspend_active_writer != &swapwriterops) {
+		len = snprintf_used(buffer, size, "- Swapwriter inactive.\n");
+		return len;
+	}
+
+	len = snprintf_used(buffer, size, "- Swapwriter active.\n");
+	if (swapfilename[0])
+		len+= snprintf_used(buffer+len, size-len,
+			"  Attempting to automatically swapon: %s.\n", swapfilename);
+
+	si_swapinfo(&sysinfo);
+	
+	len+= snprintf_used(buffer+len, size-len, "  Swap available for image: %ld pages.\n",
+			sysinfo.freeswap + swapwriter_storage_allocated());
+
+	return len;
+}
+
+/*
+ * Storage needed
+ *
+ * Returns amount of space in the swap header required
+ * for the swapwriter's data. This ignores the links between
+ * pages, which we factor in when allocating the space.
+ *
+ * We ensure the space is allocated, but actually save the
+ * data from write_header_init and therefore don't also define a
+ * save_config_info routine.
+ */
+static unsigned long swapwriter_storage_needed(void)
+{
+	int i, result;
+	result = sizeof(suspend_writer_posn_save) + sizeof(devinfo);
+
+	for (i = 0; i < MAX_SWAPFILES; i++) {
+		result += 3 * sizeof(int);
+		result += (2 * sizeof(unsigned long) * 
+			(block_chain[i].allocs - block_chain[i].frees));
+	}
+
+	return result;
+}
+
+/*
+ * Image_exists
+ */
+static int swapwriter_image_exists(void)
+{
+	int signature_found;
+	union p_diskpage diskpage;
+	
+	if (!resume_dev_t) {
+		printk("Not even trying to read header "
+				"because resume_dev_t is not set.\n");
+		return 0;
+	}
+	
+	if (!resume_block_device &&
+	    IS_ERR(resume_block_device = open_bdev(MAX_SWAPFILES, resume_dev_t))) {
+		printk("Failed to open resume dev_t (%x).\n", resume_dev_t);
+		return 0;
+	}
+
+	diskpage.address = get_zeroed_page(GFP_ATOMIC);
+
+	suspend_bio_ops.bdev_page_io(READ, resume_block_device,
+			resume_firstblock,
+			virt_to_page(diskpage.ptr));
+	suspend_bio_ops.finish_all_io();
+
+	signature_found = parse_signature(diskpage.pointer->swh.magic.magic, 0);
+	free_page(diskpage.address);
+
+	if (signature_found < 2) {
+		printk(name_suspend "Normal swapspace found.\n");
+		return 0;	/* Normal swap space */
+	} else if (signature_found == -1) {
+		printk(KERN_ERR name_suspend
+			"Unable to find a signature. Could you have moved "
+			"a swap file?\n");
+		return 0;
+	} else if (signature_found < 6) {
+		printk(name_suspend "Dectected another implementation's signature.\n");
+		return 0;
+	} else if ((signature_found >> 1) != SIGNATURE_VER) {
+		if ((!(test_suspend_state(SUSPEND_NORESUME_SPECIFIED))) &&
+			suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ,
+			 "Found a different style suspend image signature.")) {
+			set_suspend_state(SUSPEND_NORESUME_SPECIFIED);
+			printk(name_suspend "Dectected another implementation's signature.\n");
+		}
+	}
+
+	return 1;
+}
+
+/*
+ * Mark resume attempted.
+ *
+ * Record that we tried to resume from this image.
+ */
+static void swapwriter_mark_resume_attempted(void)
+{
+	union p_diskpage diskpage;
+	int signature_found;
+	
+	if (!resume_dev_t) {
+		printk("Not even trying to record attempt at resuming"
+				" because resume_dev_t is not set.\n");
+		return;
+	}
+	
+	diskpage.address = get_zeroed_page(GFP_ATOMIC);
+
+	suspend_bio_ops.bdev_page_io(READ, resume_block_device,
+			resume_firstblock,
+			virt_to_page(diskpage.ptr));
+	signature_found = parse_signature(diskpage.pointer->swh.magic.magic, 0);
+
+	switch (signature_found) {
+		case 12:
+		case 13:
+			diskpage.pointer->swh.magic.magic[5] |= 0x80;
+			break;
+	}
+	
+	suspend_bio_ops.bdev_page_io(WRITE, resume_block_device,
+			resume_firstblock,
+			virt_to_page(diskpage.ptr));
+	suspend_bio_ops.finish_all_io();
+	free_page(diskpage.address);
+	
+	close_bdevs();
+	return;
+}
+
+/*
+ * Parse Image Location
+ *
+ * Attempt to parse a resume2= parameter.
+ * Swap Writer accepts:
+ * resume2=swap:DEVNAME[:FIRSTBLOCK][@BLOCKSIZE]
+ *
+ * Where:
+ * DEVNAME is convertable to a dev_t by name_to_dev_t
+ * FIRSTBLOCK is the location of the first block in the swap file
+ * (specifying for a swap partition is nonsensical but not prohibited).
+ * Data is validated by attempting to read a swap header from the
+ * location given. Failure will result in swapwriter refusing to
+ * save an image, and a reboot with correct parameters will be
+ * necessary.
+ */
+static int swapwriter_parse_sig_location(char *commandline, int only_writer)
+{
+	char *thischar, *devstart, *colon = NULL, *at_symbol = NULL;
+	union p_diskpage diskpage;
+	int signature_found, result = -EINVAL, temp_result;
+
+	if (strncmp(commandline, "swap:", 5)) {
+		/* 
+		 * Failing swap:, we'll take a simple
+		 * resume2=/dev/hda2, but fall through to
+		 * other writers if /dev/ isn't matched.
+		 */
+		if (strncmp(commandline, "/dev/", 5))
+			return 1;
+	} else
+		commandline += 5;
+
+	devstart = thischar = commandline;
+	while ((*thischar != ':') && (*thischar != '@') &&
+		((thischar - commandline) < 250) && (*thischar))
+		thischar++;
+
+	if (*thischar == ':') {
+		colon = thischar;
+		*colon = 0;
+		thischar++;
+	}
+
+	while ((*thischar != '@') && ((thischar - commandline) < 250) && (*thischar))
+		thischar++;
+
+	if (*thischar == '@') {
+		at_symbol = thischar;
+		*at_symbol = 0;
+	}
+	
+	if (colon)
+		resume_firstblock = (int) simple_strtoul(colon + 1, NULL, 0);
+	else
+		resume_firstblock = 0;
+
+	clear_suspend_state(SUSPEND_CAN_SUSPEND);
+	clear_suspend_state(SUSPEND_CAN_RESUME);
+	
+	/* Legacy */
+	if (at_symbol) {
+		resume_blocksize = (int) simple_strtoul(at_symbol + 1, NULL, 0);
+		if (resume_blocksize & (SECTOR_SIZE - 1)) {
+			printk("Swapwriter: Blocksizes are multiples of %d!\n", SECTOR_SIZE);
+			return -EINVAL;
+		}
+		resume_firstblock = resume_firstblock * (resume_blocksize / SECTOR_SIZE);
+	}
+	
+	temp_result = try_to_parse_resume_device(devstart);
+
+	if (colon)
+		*colon = ':';
+	if (at_symbol)
+		*at_symbol = '@';
+
+	if (temp_result)
+		return -EINVAL;
+
+	diskpage.address = get_zeroed_page(GFP_ATOMIC);
+	if (!diskpage.address) {
+		printk(KERN_ERR name_suspend "Swapwriter: Failed to allocate a diskpage for I/O.\n");
+		return -ENOMEM;
+	}
+
+	temp_result = suspend_bio_ops.bdev_page_io(READ,
+			resume_block_device,
+			resume_firstblock,
+			virt_to_page(diskpage.ptr));
+
+	suspend_bio_ops.finish_all_io();
+	
+	if (temp_result) {
+		printk(KERN_ERR name_suspend "Swapwriter: Failed to submit I/O.\n");
+		goto invalid;
+	}
+	
+	signature_found = parse_signature(diskpage.pointer->swh.magic.magic, 0);
+
+	if (signature_found != -1) {
+		printk(name_suspend "Swapwriter: Signature found.\n");
+		result = 0;
+
+		suspend_bio_ops.set_devinfo(devinfo);
+		suspend_writer_posn.chains = &block_chain[0];
+		suspend_writer_posn.num_chains = MAX_SWAPFILES;
+		set_suspend_state(SUSPEND_CAN_SUSPEND);
+		set_suspend_state(SUSPEND_CAN_RESUME);
+	} else
+		printk(KERN_ERR name_suspend "Swapwriter: No swap signature found at specified location.\n");
+invalid:
+	free_page((unsigned long) diskpage.address);
+	return result;
+
+}
+
+static int header_locations_read_sysfs(const char *page, int count)
+{
+	int i, printedpartitionsmessage = 0, len = 0, haveswap = 0;
+	struct inode *swapf = 0;
+	int zone;
+	char *path_page = (char *) __get_free_page(GFP_KERNEL);
+	char *path, *output = (char *) page;
+	int path_len;
+	
+	if (!page)
+		return 0;
+
+	for (i = 0; i < MAX_SWAPFILES; i++) {
+		struct swap_info_struct *si =  get_swap_info_struct(i);
+
+		if (!si->swap_file)
+			continue;
+		
+		if (S_ISBLK(si->swap_file->f_mapping->host->i_mode)) {
+			haveswap = 1;
+			if (!printedpartitionsmessage) {
+				len += sprintf(output + len, 
+					"For swap partitions, simply use the format: resume2=swap:/dev/hda1.\n");
+				printedpartitionsmessage = 1;
+			}
+		} else {
+			path_len = 0;
+			
+			path = d_path(si->swap_file->f_dentry,
+				si->swap_file->f_vfsmnt,
+				path_page,
+				PAGE_SIZE);
+			path_len = snprintf(path_page, 31, "%s", path);
+			
+			haveswap = 1;
+			swapf = si->swap_file->f_mapping->host;
+			if (!(zone = bmap(swapf,0))) {
+				len+= sprintf(output + len, 
+					"Swapfile %s has been corrupted. Reuse mkswap on it and try again.\n",
+					path_page);
+			} else {
+				char name_buffer[255];
+				len+= sprintf(output + len, "For swapfile `%s`, use resume2=swap:/dev/%s:0x%x.\n",
+						path_page,
+						bdevname(si->bdev, name_buffer),
+						zone << (swapf->i_blkbits - 9));
+			}
+
+		}
+	}
+	
+	if (!haveswap)
+		len = sprintf(output, "You need to turn on swap partitions before examining this file.\n");
+
+	free_page((unsigned long) path_page);
+	return len;
+}
+
+static struct suspend_sysfs_data swapwriter_sysfs_data[] = {
+	{
+	 SUSPEND2_ATTR("swapfilename", SYSFS_RW),
+	 SYSFS_STRING(swapfilename, 255, SYSFS_SM_NOT_NEEDED)
+	},
+
+	{
+	 SUSPEND2_ATTR("headerlocations", SYSFS_READONLY),
+	 SYSFS_CUSTOM(header_locations_read_sysfs, NULL, SYSFS_SM_NOT_NEEDED)
+	},
+
+	{ SUSPEND2_ATTR("enabled", SYSFS_RW),
+	  SYSFS_INT(&swapwriterops.enabled, 0, 1),
+	  .write_side_effect		= attempt_to_parse_resume_device2,
+	}
+};
+
+static struct suspend_module_ops swapwriterops = {
+	.type					= WRITER_MODULE,
+	.name					= "Swap Writer",
+	.module					= THIS_MODULE,
+	.memory_needed				= swapwriter_memory_needed,
+	.print_debug_info			= swapwriter_print_debug_stats,
+	.storage_needed				= swapwriter_storage_needed,
+	.initialise				= swapwriter_initialise,
+	.cleanup				= swapwriter_cleanup,
+
+	.noresume_reset	= swapwriter_noresume_reset,
+	.storage_available 	= swapwriter_storage_available,
+	.storage_allocated	= swapwriter_storage_allocated,
+	.release_storage	= swapwriter_release_storage,
+	.allocate_header_space	= swapwriter_allocate_header_space,
+	.allocate_storage	= swapwriter_allocate_storage,
+	.image_exists		= swapwriter_image_exists,
+	.mark_resume_attempted	= swapwriter_mark_resume_attempted,
+	.write_header_init	= swapwriter_write_header_init,
+	.write_header_cleanup	= swapwriter_write_header_cleanup,
+	.read_header_init	= swapwriter_read_header_init,
+	.read_header_cleanup	= swapwriter_read_header_cleanup,
+	.invalidate_image	= swapwriter_invalidate_image,
+	.parse_sig_location	= swapwriter_parse_sig_location,
+};
+
+/* ---- Registration ---- */
+static __init int swapwriter_load(void)
+{
+	int result;
+	int i, numfiles = sizeof(swapwriter_sysfs_data) / sizeof(struct suspend_sysfs_data);
+	
+	printk("Suspend2 Swap Writer loading.\n");
+
+	swapwriterops.rw_init = suspend_bio_ops.rw_init;
+	swapwriterops.rw_cleanup = suspend_bio_ops.rw_cleanup;
+	swapwriterops.read_chunk = suspend_bio_ops.read_chunk;
+	swapwriterops.write_chunk = suspend_bio_ops.write_chunk;
+	swapwriterops.rw_header_chunk = suspend_bio_ops.rw_header_chunk;
+
+	if (!(result = suspend_register_module(&swapwriterops))) {
+		struct kobject *kobj = make_suspend2_sysdir("swapwriter");
+		for (i=0; i< numfiles; i++)
+			suspend_register_sysfs_file(kobj,
+					&swapwriter_sysfs_data[i]);
+	} else
+		printk("Suspend2 Swap Writer unable to register!\n");
+	return result;
+}
+
+#ifdef MODULE
+static __exit void swapwriter_unload(void)
+{
+	int i, numfiles = sizeof(swapwriter_sysfs_data) /
+		    sizeof(struct suspend_sysfs_data);
+
+	printk("Suspend2 Swap Writer unloading.\n");
+
+	for (i=0; i< numfiles; i++)
+		suspend_unregister_sysfs_file(&swapwriter_sysfs_data[i]);
+	suspend_unregister_module(&swapwriterops);
+}
+
+module_init(swapwriter_load);
+module_exit(swapwriter_unload);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nigel Cunningham");
+MODULE_DESCRIPTION("Suspend2 swap writer");
+#else
+late_initcall(swapwriter_load);
+#endif
diff -urN oldtree/kernel/power/swsusp.c newtree/kernel/power/swsusp.c
--- oldtree/kernel/power/swsusp.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/power/swsusp.c	2006-10-06 17:28:20.000000000 -0400
@@ -49,6 +49,7 @@
 #include <linux/bootmem.h>
 #include <linux/syscalls.h>
 #include <linux/highmem.h>
+#include <linux/freezer.h>
 
 #include "power.h"
 
@@ -190,6 +191,8 @@
 	unsigned int i = 0;
 	char *p = "-\\|/";
 
+	thaw_processes(FREEZER_KERNEL_THREADS);
+
 	printk("Shrinking memory...  ");
 	do {
 		size = 2 * count_highmem_pages();
@@ -214,6 +217,8 @@
 	} while (tmp > 0);
 	printk("\bdone (%lu pages freed)\n", pages);
 
+	freeze_processes();
+
 	return 0;
 }
 
diff -urN oldtree/kernel/power/sysfs.c newtree/kernel/power/sysfs.c
--- oldtree/kernel/power/sysfs.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/sysfs.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,298 @@
+/*
+ * kernel/power/sysfs.c
+ *
+ * Copyright (C) 2002-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * This file contains support for sysfs entries for tuning Suspend2.
+ *
+ * We have a generic handler that deals with the most common cases, and
+ * hooks for special handlers to use.
+ */
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+#include "sysfs.h"
+#include "suspend.h"
+#include "storage.h"
+
+static int suspend_sysfs_initialised = 0;
+
+static struct suspend_sysfs_data sysfs_params[];
+
+extern void __suspend_try_resume(void);
+
+extern struct subsystem power_subsys;
+
+#define to_sysfs_data(_attr) container_of(_attr, struct suspend_sysfs_data, attr)
+
+static void suspend_main_wrapper(void)
+{
+	suspend_main(0);
+}
+
+ssize_t suspend2_attr_show(struct kobject *kobj, struct attribute *attr,
+			      char *page)
+{
+	struct suspend_sysfs_data *sysfs_data = to_sysfs_data(attr);
+	int len = 0;
+
+	if (suspend_start_anything(0))
+		return -EBUSY;
+
+	if (sysfs_data->needs_storage_manager & SYSFS_NEEDS_FOR_READ)
+		suspend_prepare_usm();
+	
+	switch (sysfs_data->type) {
+		case SUSPEND_SYSFS_DATA_CUSTOM:
+			len = (sysfs_data->data.special.read_sysfs) ?
+				(sysfs_data->data.special.read_sysfs)(page, PAGE_SIZE)
+				: 0;
+			break;
+		case SUSPEND_SYSFS_DATA_BIT:
+			len = sprintf(page, "%d\n", 
+				-test_bit(sysfs_data->data.bit.bit,
+					sysfs_data->data.bit.bit_vector));
+			break;
+		case SUSPEND_SYSFS_DATA_INTEGER:
+			len = sprintf(page, "%d\n",
+				*(sysfs_data->data.integer.variable));
+			break;
+		case SUSPEND_SYSFS_DATA_LONG:
+			len = sprintf(page, "%ld\n",
+				*(sysfs_data->data.a_long.variable));
+			break;
+		case SUSPEND_SYSFS_DATA_UL:
+			len = sprintf(page, "%lu\n",
+				*(sysfs_data->data.ul.variable));
+			break;
+		case SUSPEND_SYSFS_DATA_STRING:
+			len = sprintf(page, "%s\n",
+				sysfs_data->data.string.variable);
+			break;
+	}
+	/* Side effect routine? */
+	if (sysfs_data->read_side_effect)
+		sysfs_data->read_side_effect();
+
+	if (sysfs_data->needs_storage_manager & SYSFS_NEEDS_FOR_READ)
+		suspend_cleanup_usm();
+
+	suspend_finish_anything(0);
+
+	return len;
+}
+
+#define BOUND(_variable, _type) \
+	if (sysfs_data->data._type.maximum) { \
+		if (*_variable < sysfs_data->data._type.minimum) \
+			*_variable = sysfs_data->data._type.minimum; \
+		else if (*_variable > sysfs_data->data._type.maximum) \
+			*_variable = sysfs_data->data._type.maximum; \
+	}
+
+ssize_t suspend2_attr_store(struct kobject *kobj, struct attribute *attr,
+		const char *my_buf, size_t count)
+{
+	int assigned_temp_buffer = 0, result = count;
+	struct suspend_sysfs_data *sysfs_data = to_sysfs_data(attr);
+	
+	if (suspend_start_anything(sysfs_data == &sysfs_params[0]))
+		return -EBUSY;
+
+	((char *) my_buf)[count] = 0;
+
+	if (sysfs_data->needs_storage_manager & SYSFS_NEEDS_FOR_WRITE)
+		suspend_prepare_usm();
+
+	switch (sysfs_data->type) {
+		case SUSPEND_SYSFS_DATA_CUSTOM:
+			if (sysfs_data->data.special.write_sysfs)
+				result = (sysfs_data->data.special.write_sysfs)
+					(my_buf, count);
+			break;
+		case SUSPEND_SYSFS_DATA_BIT:
+			{
+			int value = simple_strtoul(my_buf, NULL, 0);
+			if (value)
+				set_bit(sysfs_data->data.bit.bit, 
+					(sysfs_data->data.bit.bit_vector));
+			else
+				clear_bit(sysfs_data->data.bit.bit,
+					(sysfs_data->data.bit.bit_vector));
+			}
+			break;
+		case SUSPEND_SYSFS_DATA_INTEGER:
+			{
+				int *variable = sysfs_data->data.integer.variable;
+				*variable = simple_strtol(my_buf, NULL, 0);
+				BOUND(variable, integer);
+				break;
+			}
+		case SUSPEND_SYSFS_DATA_LONG:
+			{
+				long *variable = sysfs_data->data.a_long.variable;
+				*variable = simple_strtol(my_buf, NULL, 0);
+				BOUND(variable, a_long);
+				break;
+			}
+		case SUSPEND_SYSFS_DATA_UL:
+			{
+				unsigned long *variable = sysfs_data->data.ul.variable;
+				*variable = simple_strtoul(my_buf, NULL, 0);
+				BOUND(variable, ul);
+				break;
+			}
+			break;
+		case SUSPEND_SYSFS_DATA_STRING:
+			{
+				int copy_len = count;
+				char *variable =
+					sysfs_data->data.string.variable;
+
+				if (sysfs_data->data.string.max_length &&
+				    (copy_len > sysfs_data->data.string.max_length))
+					copy_len = sysfs_data->data.string.max_length;
+
+				if (!variable) {
+					sysfs_data->data.string.variable =
+						variable = (char *) get_zeroed_page(GFP_ATOMIC);
+					assigned_temp_buffer = 1;
+				}
+				strncpy(variable, my_buf, copy_len);
+				if ((copy_len) &&
+					 (my_buf[copy_len - 1] == '\n'))
+					variable[count - 1] = 0;
+				variable[count] = 0;
+			}
+			break;
+	}
+
+	/* Side effect routine? */
+	if (sysfs_data->write_side_effect)
+		sysfs_data->write_side_effect();
+
+	/* Free temporary buffers */
+	if (assigned_temp_buffer) {
+		free_page((unsigned long) sysfs_data->data.string.variable);
+		sysfs_data->data.string.variable = NULL;
+	}
+
+	if (sysfs_data->needs_storage_manager & SYSFS_NEEDS_FOR_WRITE)
+		suspend_cleanup_usm();
+
+	suspend_finish_anything(sysfs_data == &sysfs_params[0]);
+
+	return result;
+}
+
+static struct sysfs_ops suspend2_sysfs_ops = {
+	.show	= &suspend2_attr_show,
+	.store	= &suspend2_attr_store,
+};
+
+static struct kobj_type suspend2_ktype = {
+	.sysfs_ops	= &suspend2_sysfs_ops,
+};
+
+decl_subsys(suspend2, &suspend2_ktype, NULL);
+
+/* Non-module sysfs entries.
+ *
+ * This array contains entries that are automatically registered at
+ * boot. Modules and the console code register their own entries separately.
+ *
+ * NB: If you move do_suspend, change suspend_write_sysfs's test so that
+ * suspend_start_anything still gets a 1 when the user echos > do_suspend!
+ */
+
+static struct suspend_sysfs_data sysfs_params[] = {
+	{ SUSPEND2_ATTR("do_suspend", SYSFS_WRITEONLY),
+	  SYSFS_CUSTOM(NULL, NULL, SYSFS_NEEDS_FOR_WRITE),
+	  .write_side_effect = suspend_main_wrapper
+	},
+
+	{ SUSPEND2_ATTR("do_resume", SYSFS_WRITEONLY),
+	  SYSFS_CUSTOM(NULL, NULL, SYSFS_NEEDS_FOR_WRITE),
+	  .write_side_effect = __suspend_try_resume
+	},
+};
+
+struct kobject *make_suspend2_sysdir(char *name)
+{
+	struct kobject *kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
+	int err;
+
+	BUG_ON(!kobj);
+
+	err = kobject_set_name(kobj, "%s", name);
+
+	if (err) {
+		kfree(kobj);
+		return NULL;
+	}
+
+	kobj->kset = &suspend2_subsys.kset;
+
+	err = kobject_register(kobj);
+
+	return err ? NULL : kobj;
+}
+
+/* suspend_initialise_sysfs
+ *
+ * Initialise the /sysfs/suspend2 directory.
+ */
+
+static void suspend_initialise_sysfs(void)
+{
+	int i, error;
+	int numfiles = sizeof(sysfs_params) / sizeof(struct suspend_sysfs_data);
+	
+	if (suspend_sysfs_initialised)
+		return;
+
+	/* Make our suspend2 directory a child of /sys/power */
+	kobj_set_kset_s(&suspend2_subsys.kset, power_subsys);
+	error = subsystem_register(&suspend2_subsys);
+
+	if (error)
+		return;
+
+	/* Make it use the .store and .show routines above */
+	kobj_set_kset_s(&suspend2_subsys.kset, suspend2_subsys);
+
+	suspend_sysfs_initialised = 1;
+
+	for (i=0; i< numfiles; i++)
+		suspend_register_sysfs_file(&suspend2_subsys.kset.kobj,
+				&sysfs_params[i]);
+}
+
+/* suspend_register_sysfs_file
+ *
+ * Helper for registering a new /sysfs/suspend2 entry.
+ */
+
+void suspend_register_sysfs_file(
+		struct kobject *kobj,
+		struct suspend_sysfs_data *suspend_sysfs_data)
+{
+	if (!suspend_sysfs_initialised)
+		suspend_initialise_sysfs();
+
+	sysfs_create_file(kobj, &suspend_sysfs_data->attr);
+}
+
+/* suspend_unregister_sysfs_file
+ *
+ * Helper for removing unwanted /sysfs/suspend2 entries.
+ *
+ */
+void suspend_unregister_sysfs_file(struct suspend_sysfs_data *suspend_sysfs_data)
+{
+	sysfs_remove_file(&suspend2_subsys.kset.kobj, &suspend_sysfs_data->attr);
+}
diff -urN oldtree/kernel/power/sysfs.h newtree/kernel/power/sysfs.h
--- oldtree/kernel/power/sysfs.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/sysfs.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,117 @@
+/*
+ * kernel/power/sysfs.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * It provides declarations for suspend to use in managing
+ * /sysfs/suspend2. When we switch to kobjects,
+ * this will become redundant.
+ *
+ */
+
+#include <linux/sysfs.h>
+#include "power.h"
+
+struct suspend_sysfs_data {
+	struct attribute attr;
+	int type;
+	int needs_storage_manager;
+	union {
+		struct {
+			unsigned long *bit_vector;
+			int bit;
+		} bit;
+		struct {
+			int *variable;
+			int minimum;
+			int maximum;
+		} integer;
+		struct {
+			long *variable;
+			long minimum;
+			long maximum;
+		} a_long;
+		struct {
+			unsigned long *variable;
+			unsigned long minimum;
+			unsigned long maximum;
+		} ul;
+		struct {
+			char *variable;
+			int max_length;
+		} string;
+		struct {
+			int (*read_sysfs) (const char *buffer, int count);
+			int (*write_sysfs) (const char *buffer, int count);
+			void *data;
+		} special;
+	} data;
+	
+	/* Side effects routines. Used, eg, for reparsing the
+	 * resume2 entry when it changes */
+	void (*read_side_effect) (void);
+	void (*write_side_effect) (void); 
+	struct list_head sysfs_data_list;
+};
+
+enum {
+	SUSPEND_SYSFS_DATA_NONE,
+	SUSPEND_SYSFS_DATA_CUSTOM,
+	SUSPEND_SYSFS_DATA_BIT,
+	SUSPEND_SYSFS_DATA_INTEGER,
+	SUSPEND_SYSFS_DATA_UL,
+	SUSPEND_SYSFS_DATA_LONG,
+	SUSPEND_SYSFS_DATA_STRING
+};
+
+#define SUSPEND2_ATTR(_name, _mode)      \
+        .attr = {.name  = _name , .mode   = _mode }
+
+#define SYSFS_BIT(_ul, _bit) \
+	.type = SUSPEND_SYSFS_DATA_BIT, \
+	.data = { .bit = { .bit_vector = _ul, .bit = _bit } }
+
+#define SYSFS_INT(_int, _min, _max) \
+	.type = SUSPEND_SYSFS_DATA_INTEGER, \
+	.data = { .integer = { .variable = _int, .minimum = _min, \
+			.maximum = _max } }
+
+#define SYSFS_UL(_ul, _min, _max) \
+	.type = SUSPEND_SYSFS_DATA_UL, \
+	.data = { .ul = { .variable = _ul, .minimum = _min, \
+			.maximum = _max } }
+
+#define SYSFS_LONG(_long, _min, _max) \
+	.type = SUSPEND_SYSFS_DATA_LONG, \
+	.data = { .a_long = { .variable = _long, .minimum = _min, \
+			.maximum = _max } }
+
+#define SYSFS_STRING(_string, _max_len, _needs_usm) \
+	.type = SUSPEND_SYSFS_DATA_STRING, \
+	.needs_storage_manager = _needs_usm, \
+	.data = { .string = { .variable = _string, .max_length = _max_len } }
+
+#define SYSFS_CUSTOM(_read, _write, _needs_storage_manager) \
+	.type = SUSPEND_SYSFS_DATA_CUSTOM, \
+	.needs_storage_manager = _needs_storage_manager, \
+	.data = { .special = { .read_sysfs = _read, .write_sysfs = _write } }
+
+#define SYSFS_WRITEONLY 0200
+#define SYSFS_READONLY 0444
+#define SYSFS_RW 0644
+
+/* Storage manager needed? */
+#define SYSFS_SM_NOT_NEEDED 0
+#define SYSFS_NEEDS_FOR_READ 1
+#define SYSFS_NEEDS_FOR_WRITE 2
+#define SYSFS_NEEDS_FOR_BOTH 3
+
+void suspend_register_sysfs_file(struct kobject *kobj,
+		struct suspend_sysfs_data *suspend_sysfs_data);
+void suspend_unregister_sysfs_file(struct suspend_sysfs_data *suspend_sysfs_data);
+
+extern struct subsystem suspend2_subsys;
+
+struct kobject *make_suspend2_sysdir(char *name);
diff -urN oldtree/kernel/power/ui.c newtree/kernel/power/ui.c
--- oldtree/kernel/power/ui.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/ui.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,756 @@
+/*
+ * kernel/power/ui.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-2006 Nigel Cunningham <nigel@suspend2.net>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Routines for Suspend2's user interface.
+ *
+ * The user interface code talks to a userspace program via a
+ * netlink socket.
+ *
+ * The kernel side:
+ * - starts the userui program;
+ * - sends text messages and progress bar status;
+ *
+ * The user space side:
+ * - passes messages regarding user requests (abort, toggle reboot etc)
+ *
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/suspend.h>
+#include <linux/freezer.h>
+#include <linux/console.h>
+#include <linux/ctype.h>
+#include <linux/tty.h>
+#include <linux/vt_kern.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/kmod.h>
+#include <linux/security.h>
+#include <linux/syscalls.h>
+ 
+#include "sysfs.h"
+#include "modules.h"
+#include "suspend.h"
+#include "ui.h"
+#include "netlink.h"
+#include "power_off.h"
+
+static char local_printf_buf[1024];	/* Same as printk - should be safe */
+
+/*! The console log level we default to. */
+int suspend_default_console_level = 0;
+
+#ifdef CONFIG_NET
+static struct user_helper_data ui_helper_data;
+static struct suspend_module_ops userui_ops;
+static int orig_loglevel;
+static int orig_default_message_loglevel;
+static int orig_kmsg;
+
+static char lastheader[512];
+static int lastheader_message_len = 0;
+extern int console_suspended;
+
+/* Number of distinct progress amounts that userspace can display */
+static int progress_granularity = 30;
+
+DECLARE_WAIT_QUEUE_HEAD(userui_wait_for_key);
+
+static void ui_nl_set_state(int n)
+{
+	/* Only let them change certain settings */
+	static const int suspend_action_mask =
+		(1 << SUSPEND_REBOOT) | (1 << SUSPEND_PAUSE) | (1 << SUSPEND_SLOW) |
+		(1 << SUSPEND_LOGALL) | (1 << SUSPEND_SINGLESTEP) |
+		(1 << SUSPEND_PAUSE_NEAR_PAGESET_END);
+
+	suspend_action = (suspend_action & (~suspend_action_mask)) |
+		(n & suspend_action_mask);
+
+	if (!test_action_state(SUSPEND_PAUSE) &&
+			!test_action_state(SUSPEND_SINGLESTEP))
+		wake_up_interruptible(&userui_wait_for_key);
+}
+
+void userui_redraw(void)
+{
+	suspend_send_netlink_message(&ui_helper_data,
+			USERUI_MSG_REDRAW, NULL, 0);
+}
+
+/* request_abort_suspend
+ *
+ * Description:	Handle the user requesting the cancellation of a suspend by
+ * 		pressing escape.
+ * Callers:	Invoked from a netlink packet from userspace when the user presses
+ * 	 	escape.
+ */
+void request_abort_suspend(void)
+{
+	if (test_result_state(SUSPEND_ABORT_REQUESTED))
+		return;
+
+	if (test_suspend_state(SUSPEND_NOW_RESUMING)) {
+		suspend_prepare_status(CLEAR_BAR, "Escape pressed. "
+					"Powering down again.");
+		suspend_power_down();
+	} else {
+		suspend_prepare_status(CLEAR_BAR, "--- ESCAPE PRESSED :"
+					" ABORTING SUSPEND ---");
+		set_result_state(SUSPEND_ABORTED);
+		set_result_state(SUSPEND_ABORT_REQUESTED);
+	
+		wake_up_interruptible(&userui_wait_for_key);
+	}
+}
+
+static int userui_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+	int type;
+	int *data;
+
+	type = nlh->nlmsg_type;
+
+	/* A control message: ignore them */
+	if (type < NETLINK_MSG_BASE)
+		return 0;
+
+	/* Unknown message: reply with EINVAL */
+	if (type >= USERUI_MSG_MAX)
+		return -EINVAL;
+
+	/* All operations require privileges, even GET */
+	if (security_netlink_recv(skb, CAP_NET_ADMIN))
+		return -EPERM;
+
+	/* Only allow one task to receive NOFREEZE privileges */
+	if (type == NETLINK_MSG_NOFREEZE_ME && ui_helper_data.pid != -1)
+		return -EBUSY;
+
+	data = (int*)NLMSG_DATA(nlh);
+
+	switch (type) {
+		case USERUI_MSG_ABORT:
+			request_abort_suspend();
+			break;
+		case USERUI_MSG_GET_STATE:
+			suspend_send_netlink_message(&ui_helper_data, 
+					USERUI_MSG_GET_STATE, &suspend_action,
+					sizeof(suspend_action));
+			break;
+		case USERUI_MSG_GET_DEBUG_STATE:
+			suspend_send_netlink_message(&ui_helper_data,
+					USERUI_MSG_GET_DEBUG_STATE,
+					&suspend_debug_state,
+					sizeof(suspend_debug_state));
+			break;
+		case USERUI_MSG_SET_STATE:
+			if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int)))
+				return -EINVAL;
+			ui_nl_set_state(*data);
+			break;
+		case USERUI_MSG_SET_DEBUG_STATE:
+			if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int)))
+				return -EINVAL;
+			suspend_debug_state = (*data);
+			break;
+		case USERUI_MSG_SPACE:
+			wake_up_interruptible(&userui_wait_for_key);
+			break;
+	}
+
+	return 1;
+}
+
+static unsigned long userui_storage_needed(void)
+{
+	return sizeof(ui_helper_data.program) + 1 + sizeof(int);
+}
+
+static int userui_save_config_info(char *buf)
+{
+	*((int *) buf) = progress_granularity;
+	memcpy(buf + sizeof(int), ui_helper_data.program, sizeof(ui_helper_data.program));
+	return sizeof(ui_helper_data.program) + sizeof(int) + 1;
+}
+
+static void userui_load_config_info(char *buf, int size)
+{
+	progress_granularity = *((int *) buf);
+	size -= sizeof(int);
+
+	/* Don't load the saved path if one has already been set */
+	if (ui_helper_data.program[0])
+		return;
+
+	if (size > sizeof(ui_helper_data.program))
+		size = sizeof(ui_helper_data.program);
+
+	memcpy(ui_helper_data.program, buf + sizeof(int), size);
+	ui_helper_data.program[sizeof(ui_helper_data.program)-1] = '\0';
+}
+
+static unsigned long userui_memory_needed(void)
+{
+	/* ball park figure of 128 pages */
+	return (128 * PAGE_SIZE);
+}
+
+/* suspend_update_status
+ *
+ * Description: Update the progress bar and (if on) in-bar message.
+ * Arguments:	UL value, maximum: Current progress percentage (value/max).
+ * 		const char *fmt, ...: Message to be displayed in the middle
+ * 		of the progress bar.
+ * 		Note that a NULL message does not mean that any previous
+ * 		message is erased! For that, you need suspend_prepare_status with
+ * 		clearbar on.
+ * Returns:	Unsigned long: The next value where status needs to be updated.
+ * 		This is to reduce unnecessary calls to update_status.
+ */
+unsigned long suspend_update_status(unsigned long value, unsigned long maximum,
+		const char *fmt, ...)
+{
+	static int last_step = -1;
+	struct userui_msg_params msg;
+	int bitshift;
+	int this_step;
+	unsigned long next_update;
+
+	if (ui_helper_data.pid == -1)
+		return 0;
+
+	if ((!maximum) || (!progress_granularity))
+		return maximum;
+
+	if (value < 0)
+		value = 0;
+
+	if (value > maximum)
+		value = maximum;
+
+	/* Try to avoid math problems - we can't do 64 bit math here
+	 * (and shouldn't need it - anyone got screen resolution
+	 * of 65536 pixels or more?) */
+	bitshift = fls(maximum) - 16;
+	if (bitshift > 0) {
+		unsigned long temp_maximum = maximum >> bitshift;
+		unsigned long temp_value = value >> bitshift;
+		this_step = (int)
+			(temp_value * progress_granularity / temp_maximum);
+		next_update = (((this_step + 1) * temp_maximum /
+					progress_granularity) + 1) << bitshift;
+	} else {
+		this_step = (int) (value * progress_granularity / maximum);
+		next_update = ((this_step + 1) * maximum /
+				progress_granularity) + 1;
+	}
+
+	if (this_step == last_step)
+		return next_update;
+
+	memset(&msg, 0, sizeof(msg));
+
+	msg.a = this_step;
+	msg.b = progress_granularity;
+
+	if (fmt) {
+		va_list args;
+		va_start(args, fmt);
+		vsnprintf(msg.text, sizeof(msg.text), fmt, args);
+		va_end(args);
+		msg.text[sizeof(msg.text)-1] = '\0';
+	}
+
+	suspend_send_netlink_message(&ui_helper_data, USERUI_MSG_PROGRESS,
+			&msg, sizeof(msg));
+	last_step = this_step;
+
+	return next_update;
+}
+
+/* __suspend_message.
+ *
+ * Description:	This function is intended to do the same job as printk, but
+ * 		without normally 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"
+ *
+ * 		It may be called from an interrupt context - can't sleep!
+ *
+ * Arguments:	int mask: The debugging section(s) this message belongs to.
+ * 		int level: The level of verbosity of this message.
+ * 		int restartline: Whether to output a \r or \n with this line
+ * 			(\n if we're logging all output).
+ * 		const char *fmt, ...: Message to be displayed a la printk.
+ */
+void __suspend_message(unsigned long section, unsigned long level,
+		int normally_logged,
+		const char *fmt, ...)
+{
+	struct userui_msg_params msg;
+
+	if ((level) && (level > console_loglevel))
+		return;
+
+	memset(&msg, 0, sizeof(msg));
+
+	msg.a = section;
+	msg.b = level;
+	msg.c = normally_logged;
+
+	if (fmt) {
+		va_list args;
+		va_start(args, fmt);
+		vsnprintf(msg.text, sizeof(msg.text), fmt, args);
+		va_end(args);
+		msg.text[sizeof(msg.text)-1] = '\0';
+	}
+
+	if (test_action_state(SUSPEND_LOGALL))
+		printk("%s\n", msg.text);
+
+	suspend_send_netlink_message(&ui_helper_data, USERUI_MSG_MESSAGE,
+			&msg, sizeof(msg));
+}
+
+static void wait_for_key_via_userui(void)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&userui_wait_for_key, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	interruptible_sleep_on(&userui_wait_for_key);
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&userui_wait_for_key, &wait);
+}
+
+static char suspend_wait_for_keypress(int timeout)
+{
+	int fd;
+	char key = '\0';
+	struct termios t, t_backup;
+
+	if (ui_helper_data.pid != -1) {
+		wait_for_key_via_userui();
+		key = ' ';
+		goto out;
+	}
+	
+	/* We should be guaranteed /dev/console exists after populate_rootfs() in
+	 * init/main.c
+	 */
+	if ((fd = sys_open("/dev/console", O_RDONLY, 0)) < 0) {
+		printk("Couldn't open /dev/console.\n");
+		goto out;
+	}
+
+	if (sys_ioctl(fd, TCGETS, (long)&t) < 0)
+		goto out_close;
+
+	memcpy(&t_backup, &t, sizeof(t));
+
+	t.c_lflag &= ~(ISIG|ICANON|ECHO);
+	t.c_cc[VMIN] = 0;
+	if (timeout)
+		t.c_cc[VTIME] = timeout*10;
+
+	if (sys_ioctl(fd, TCSETS, (long)&t) < 0)
+		goto out_restore;
+
+	while (1) {
+		if (sys_read(fd, &key, 1) <= 0) {
+			key = '\0';
+			break;
+		}
+		key = tolower(key);
+		if (test_suspend_state(SUSPEND_SANITY_CHECK_PROMPT)) {
+			if (key == 'c') {
+				set_suspend_state(SUSPEND_CONTINUE_REQ);
+				break;
+			} else if (key == ' ')
+				break;
+		} else
+			break;
+	}
+
+out_restore:
+	sys_ioctl(fd, TCSETS, (long)&t_backup);
+out_close:
+	sys_close(fd);
+out:
+	return key;
+}
+
+/* abort_suspend
+ *
+ * Description: Begin to abort a cycle. If this wasn't at the user's request
+ * 		(and we're displaying output), tell the user why and wait for
+ * 		them to acknowledge the message.
+ * Arguments:	A parameterised string (imagine this is printk) to display,
+ *	 	telling the user why we're aborting.
+ */
+
+void abort_suspend(const char *fmt, ...)
+{
+	va_list args;
+	int printed_len = 0;
+
+	if (!test_result_state(SUSPEND_ABORTED)) {
+		if (!test_result_state(SUSPEND_ABORT_REQUESTED)) {
+			va_start(args, fmt);
+			printed_len = vsnprintf(local_printf_buf, 
+					sizeof(local_printf_buf), fmt, args);
+			va_end(args);
+			if (ui_helper_data.pid != -1)
+				printed_len = sprintf(local_printf_buf + printed_len,
+					" (Press SPACE to continue)");
+			suspend_prepare_status(CLEAR_BAR, local_printf_buf);
+
+			/* 
+			 * Make sure message seen - wait for shift to be
+			 * released if being pressed 
+			 */
+			if (ui_helper_data.pid != -1)
+				suspend_wait_for_keypress(0);
+		}
+		/* Turn on aborting flag */
+		set_result_state(SUSPEND_ABORTED);
+	}
+}
+
+/* suspend_prepare_status
+ * Description:	Prepare the 'nice display', drawing the header and version,
+ * 		along with the current action and perhaps also resetting the
+ * 		progress bar.
+ * Arguments:	
+ * 		int clearbar: Whether to reset the progress bar.
+ * 		const char *fmt, ...: The action to be displayed.
+ */
+void suspend_prepare_status(int clearbar, const char *fmt, ...)
+{
+	va_list args;
+
+	if (fmt) {
+		va_start(args, fmt);
+		lastheader_message_len = vsnprintf(lastheader, 512, fmt, args);
+		va_end(args);
+	}
+
+	if (clearbar)
+		suspend_update_status(0, 1, NULL);
+
+	suspend_message(0, SUSPEND_STATUS, 1, lastheader, NULL);
+
+	if (ui_helper_data.pid == -1)
+		printk(KERN_EMERG "%s\n", lastheader);
+}
+
+/* suspend_cond_pause
+ * 
+ * Description:	Potentially pause and wait for the user to tell us to continue.
+ * 		We normally only pause when @pause is set.
+ * Arguments:	int pause: Whether we normally pause.
+ * 		char *message: The message to display. Not parameterised
+ * 		 because it's normally a constant.
+ */
+
+void suspend_cond_pause(int pause, char *message)
+{
+#ifdef CONFIG_PM_DEBUG
+	int displayed_message = 0, last_key = 0;
+	
+	while (last_key != 32 &&
+		ui_helper_data.pid != -1 &&
+		(!test_result_state(SUSPEND_ABORTED)) &&
+		((test_action_state(SUSPEND_PAUSE) && pause) || 
+		 (test_action_state(SUSPEND_SINGLESTEP)))) {
+		if (!displayed_message) {
+			suspend_prepare_status(DONT_CLEAR_BAR, 
+			   "%s Press SPACE to continue.%s",
+			   message ? message : "",
+			   (test_action_state(SUSPEND_SINGLESTEP)) ? 
+			   " Single step on." : "");
+			displayed_message = 1;
+		}
+		last_key = suspend_wait_for_keypress(0);
+	}
+#endif
+	schedule();
+}
+
+extern asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, 
+		unsigned long arg);
+
+/* suspend_prepare_console
+ *
+ * Description:	Prepare a console for use, save current settings.
+ * Returns:	Boolean: Whether an error occured. Errors aren't
+ * 		treated as fatal, but a warning is printed.
+ */
+void suspend_prepare_console(void)
+{
+	orig_loglevel = console_loglevel;
+	orig_default_message_loglevel = default_message_loglevel;
+	orig_kmsg = kmsg_redirect;
+	kmsg_redirect = fg_console + 1;
+	default_message_loglevel = 1;
+	console_loglevel = suspend_default_console_level;
+
+	ui_helper_data.pid = -1;
+
+	if (!userui_ops.enabled)
+		return;
+
+	if (!*ui_helper_data.program) {
+		printk("suspend_userui: program not configured. suspend_userui disabled.\n");
+		return;
+	}
+
+	suspend_netlink_setup(&ui_helper_data);
+
+	return;
+}
+
+/* suspend_cleanup_console
+ *
+ * Description: Restore the settings we saved above.
+ */
+
+void suspend_cleanup_console(void)
+{
+	suspend_default_console_level = console_loglevel;
+
+	if (ui_helper_data.pid > -1) {
+		struct task_struct *t;
+
+		suspend_send_netlink_message(&ui_helper_data,
+				NETLINK_MSG_CLEANUP, NULL, 0);
+
+		read_lock(&tasklist_lock);
+		if ((t = find_task_by_pid(ui_helper_data.pid)))
+			t->flags &= ~PF_NOFREEZE;
+		read_unlock(&tasklist_lock);
+
+		suspend_netlink_close(&ui_helper_data);
+
+		ui_helper_data.pid = -1;
+	}
+
+	console_loglevel = orig_loglevel;
+	kmsg_redirect = orig_kmsg;
+	default_message_loglevel = orig_default_message_loglevel;
+}
+#endif
+
+/* suspend_early_boot_message()
+ * Description:	Handle errors early in the process of booting.
+ * 		The user may press C to continue booting, perhaps
+ * 		invalidating the image,  or space to reboot. 
+ * 		This works from either the serial console or normally 
+ * 		attached keyboard.
+ *
+ * 		Note that we come in here from init, while the kernel is
+ * 		locked. If we want to get events from the serial console,
+ * 		we need to temporarily unlock the kernel.
+ *
+ * 		suspend_early_boot_message may also be called post-boot.
+ * 		In this case, it simply printks the message and returns.
+ *
+ * Arguments:	int	Whether we are able to erase the image.
+ * 		int	default_answer. What to do when we timeout. This
+ * 			will normally be continue, but the user might
+ * 			provide command line options (__setup) to override
+ * 			particular cases.
+ * 		Char *. Pointer to a string explaining why we're moaning.
+ */
+
+#define say(message, a...) printk(KERN_EMERG message, ##a)
+#define message_timeout 25 /* message_timeout * 10 must fit in 8 bits */
+
+int suspend_early_boot_message(int message_detail, int default_answer, char *warning_reason, ...)
+{
+	unsigned long orig_state = get_suspend_state(), continue_req = 0;
+	va_list args;
+	int printed_len;
+
+	if (warning_reason) {
+		va_start(args, warning_reason);
+		printed_len = vsnprintf(local_printf_buf, 
+				sizeof(local_printf_buf), 
+				warning_reason,
+				args);
+		va_end(args);
+	}
+
+	if (!test_suspend_state(SUSPEND_BOOT_TIME)) {
+		printk(name_suspend "%s\n", local_printf_buf);
+		return default_answer;
+	}
+
+	/* We might be called directly from do_mounts_initrd if the
+	 * user fails to set up their initrd properly. We need to
+	 * enable the keyboard handler by setting the running flag */
+	set_suspend_state(SUSPEND_RUNNING);
+
+#if defined(CONFIG_VT) || defined(CONFIG_SERIAL_CONSOLE)
+	console_loglevel = 7;
+
+	say("=== Suspend2 ===\n\n");
+	if (warning_reason) {
+		say("BIG FAT WARNING!! %s\n\n", local_printf_buf);
+		switch (message_detail) {
+		 case 0:
+			say("If you continue booting, note that any image WILL NOT BE REMOVED.\n");
+			say("Suspend is unable to do so because the appropriate modules aren't\n");
+			say("loaded. You should manually remove the image to avoid any\n");
+			say("possibility of corrupting your filesystem(s) later.\n");
+			break;
+		 case 1:
+			say("If you want to use the current suspend image, reboot and try\n");
+			say("again with the same kernel that you suspended from. If you want\n");
+			say("to forget that image, continue and the image will be erased.\n");
+			break;
+		}
+		say("Press SPACE to reboot or C to continue booting with this kernel\n\n");
+		say("Default action if you don't select one in %d seconds is: %s.\n",
+			message_timeout,
+			default_answer == SUSPEND_CONTINUE_REQ ?
+			"continue booting" : "reboot");
+	} else {
+		say("BIG FAT WARNING!!\n\n");
+		say("You have tried to resume from this image before.\n");
+		say("If it failed once, it may well fail again.\n");
+		say("Would you like to remove the image and boot normally?\n");
+		say("This will be equivalent to entering noresume2 on the\n");
+		say("kernel command line.\n\n");
+		say("Press SPACE to remove the image or C to continue resuming.\n\n");
+		say("Default action if you don't select one in %d seconds is: %s.\n",
+			message_timeout,
+			!!default_answer ?
+			"continue resuming" : "remove the image");
+	}
+	
+	set_suspend_state(SUSPEND_SANITY_CHECK_PROMPT);
+	clear_suspend_state(SUSPEND_CONTINUE_REQ);
+
+	if (suspend_wait_for_keypress(message_timeout) == 0) /* We timed out */
+		continue_req = !!default_answer;
+	else
+		continue_req = test_suspend_state(SUSPEND_CONTINUE_REQ);
+
+	if ((warning_reason) && (!continue_req))
+		machine_restart(NULL);
+	
+	restore_suspend_state(orig_state);
+	if (continue_req)
+		set_suspend_state(SUSPEND_CONTINUE_REQ);
+
+#endif /* CONFIG_VT or CONFIG_SERIAL_CONSOLE */
+	return -EPERM;
+}
+#undef say
+
+/*
+ * User interface specific /sys/power/suspend2 entries.
+ */
+
+static struct suspend_sysfs_data sysfs_params[] = {
+#if defined(CONFIG_NET) && defined(CONFIG_SYSFS)
+	{ SUSPEND2_ATTR("default_console_level", SYSFS_RW),
+#ifdef CONFIG_PM_DEBUG
+	  SYSFS_INT(&suspend_default_console_level, 0, 7)
+#else
+	  SYSFS_INT(&suspend_default_console_level, 0, 1)
+#endif
+	},
+
+	{ SUSPEND2_ATTR("enable_escape", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_CAN_CANCEL)
+	},
+
+#ifdef CONFIG_PM_DEBUG
+	{ SUSPEND2_ATTR("debug_sections", SYSFS_RW),
+	  SYSFS_UL(&suspend_debug_state, 0, 2 << 30)
+	},
+
+	{ SUSPEND2_ATTR("log_everything", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_LOGALL)
+	},
+	  
+	{ SUSPEND2_ATTR("pause_between_steps", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_PAUSE)
+	},
+#endif
+	{ SUSPEND2_ATTR("enabled", SYSFS_RW),
+	  SYSFS_INT(&userui_ops.enabled, 0, 1)
+	},
+
+	{ SUSPEND2_ATTR("progress_granularity", SYSFS_RW),
+	  SYSFS_INT(&progress_granularity, 1, 2048)
+	},
+
+	{ SUSPEND2_ATTR("program", SYSFS_RW),
+	  SYSFS_STRING(ui_helper_data.program, 255, SYSFS_SM_NOT_NEEDED)
+	},
+#endif
+	{ SUSPEND2_ATTR("pm_prepare_console", SYSFS_RW),
+	  SYSFS_BIT(&suspend_action, SUSPEND_PM_PREPARE_CONSOLE)
+	}
+};
+
+static struct suspend_module_ops userui_ops = {
+	.type				= MISC_MODULE,
+	.name				= "Userspace UI Support",
+	.module				= THIS_MODULE,
+#ifdef CONFIG_NET
+	.storage_needed			= userui_storage_needed,
+	.save_config_info		= userui_save_config_info,
+	.load_config_info		= userui_load_config_info,
+	.memory_needed			= userui_memory_needed,
+#endif
+};
+
+/* suspend_console_sysfs_init
+ * Description: Boot time initialisation for user interface.
+ */
+static __init int suspend_console_sysfs_init(void)
+{
+	int result, i,
+	    numfiles = sizeof(sysfs_params) / sizeof(struct suspend_sysfs_data);
+
+	if (!(result = suspend_register_module(&userui_ops))) {
+		struct kobject *kobj = make_suspend2_sysdir("user_interface");
+		for (i=0; i< numfiles; i++)
+			suspend_register_sysfs_file(kobj, &sysfs_params[i]);
+	}
+
+#ifdef CONFIG_NET
+	ui_helper_data.nl = NULL;
+	ui_helper_data.program[0] = '\0';
+	ui_helper_data.pid = -1;
+	ui_helper_data.skb_size = sizeof(struct userui_msg_params);
+	ui_helper_data.pool_limit = 6;
+	ui_helper_data.netlink_id = NETLINK_SUSPEND2_USERUI;
+	ui_helper_data.name = "userspace ui";
+	ui_helper_data.rcv_msg = userui_user_rcv_msg;
+	ui_helper_data.interface_version = 6;
+	ui_helper_data.must_init = 0;
+	ui_helper_data.not_ready = suspend_cleanup_console;
+	init_completion(&ui_helper_data.wait_for_process);
+#endif
+
+	return result;
+}
+
+late_initcall(suspend_console_sysfs_init);
diff -urN oldtree/kernel/power/ui.h newtree/kernel/power/ui.h
--- oldtree/kernel/power/ui.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/ui.h	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,71 @@
+/*
+ * kernel/power/ui.h
+ *
+ * Copyright 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ */
+
+extern int suspend_default_console_level;
+
+enum {
+	DONT_CLEAR_BAR,
+	CLEAR_BAR
+};
+
+enum {
+	/* Userspace -> Kernel */
+	USERUI_MSG_ABORT = 0x11,
+	USERUI_MSG_SET_STATE = 0x12,
+	USERUI_MSG_GET_STATE = 0x13,
+	USERUI_MSG_GET_DEBUG_STATE = 0x14,
+	USERUI_MSG_SET_DEBUG_STATE = 0x15,
+	USERUI_MSG_SET_PROGRESS_GRANULARITY = 0x17,
+	USERUI_MSG_SPACE = 0x18,
+
+	/* Kernel -> Userspace */
+	USERUI_MSG_MESSAGE = 0x21,
+	USERUI_MSG_PROGRESS = 0x22,
+	USERUI_MSG_REDRAW = 0x25,
+	USERUI_MSG_KEYPRESS = 0x26,
+	USERUI_MSG_DEBUG_STATE = 0x29,
+
+	USERUI_MSG_MAX,
+};
+
+struct userui_msg_params {
+	unsigned long a, b, c, d;
+	char text[255];
+};
+
+#if defined(CONFIG_NET)
+
+extern unsigned long suspend_update_status(unsigned long value, unsigned long maximum,
+		const char *fmt, ...);
+extern void suspend_prepare_status (int clearbar, const char *fmt, ...);
+extern void suspend_cond_pause(int pause, char *message);
+extern void abort_suspend(const char *fmt, ...);
+extern void suspend_prepare_console(void);
+extern void suspend_cleanup_console(void);
+extern void userui_redraw(void);
+
+#else
+static inline char suspend_wait_for_keypress(int timeout)
+{
+	return 0;
+} 
+
+static inline unsigned long suspend_update_status(unsigned long value, unsigned long maximum,
+		const char *fmt, ...)
+{
+	return maximum;
+} 
+
+static inline void __suspend_message(unsigned long section, unsigned long level,
+		int normally_logged,
+		const char *fmt, ...)  { }
+static inline void suspend_prepare_status(int clearbar, const char *fmt, ...) { }
+static inline void suspend_cond_pause(int pause, char *message) { }
+static inline void abort_suspend(const char *fmt, ...) { }
+static inline void suspend_prepare_console(void) { }
+static inline void suspend_cleanup_console(void) { }
+static inline void userui_redraw(void) { }
+#endif
diff -urN oldtree/kernel/power/user.c newtree/kernel/power/user.c
--- oldtree/kernel/power/user.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/power/user.c	2006-10-06 17:28:20.000000000 -0400
@@ -22,6 +22,7 @@
 #include <linux/fs.h>
 #include <linux/console.h>
 #include <linux/cpu.h>
+#include <linux/freezer.h>
 
 #include <asm/uaccess.h>
 
@@ -79,7 +80,7 @@
 	free_bitmap(data->bitmap);
 	if (data->frozen) {
 		down(&pm_sem);
-		thaw_processes();
+		thaw_processes(FREEZER_ALL_THREADS);
 		enable_nonboot_cpus();
 		up(&pm_sem);
 	}
@@ -148,7 +149,7 @@
 		if (!error) {
 			error = freeze_processes();
 			if (error) {
-				thaw_processes();
+				thaw_processes(FREEZER_ALL_THREADS);
 				error = -EBUSY;
 			}
 		}
@@ -162,7 +163,7 @@
 		if (!data->frozen)
 			break;
 		down(&pm_sem);
-		thaw_processes();
+		thaw_processes(FREEZER_ALL_THREADS);
 		enable_nonboot_cpus();
 		up(&pm_sem);
 		data->frozen = 0;
diff -urN oldtree/kernel/timer.c newtree/kernel/timer.c
--- oldtree/kernel/timer.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/kernel/timer.c	2006-10-06 17:28:20.000000000 -0400
@@ -34,6 +34,7 @@
 #include <linux/cpu.h>
 #include <linux/syscalls.h>
 #include <linux/delay.h>
+#include <linux/freezer.h>
 
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -1010,6 +1011,16 @@
 
 	active_tasks = count_active_tasks();
 	for (count -= ticks; count < 0; count += LOAD_FREQ) {
+
+		/* Suspend2 does a lot of work (pagecache I/O) before
+		 * and after the atomic copy. If we let the load average
+		 * be updated while suspending, it will be very high post
+		 * resume. Processes such as some MTAs that stop work
+		 * while the average is high will be unnecessarily disrupted.
+		 */
+		if (freezer_is_on())
+			continue;
+
 		CALC_LOAD(avenrun[0], EXP_1, active_tasks);
 		CALC_LOAD(avenrun[1], EXP_5, active_tasks);
 		CALC_LOAD(avenrun[2], EXP_15, active_tasks);
diff -urN oldtree/lib/Kconfig newtree/lib/Kconfig
--- oldtree/lib/Kconfig	2006-10-05 15:26:55.000000000 -0400
+++ newtree/lib/Kconfig	2006-10-06 17:28:20.000000000 -0400
@@ -43,6 +43,9 @@
 	depends on AUDIT && !AUDIT_ARCH
 	default y
 
+config DYN_PAGEFLAGS
+	bool
+
 #
 # compression support is select'ed if needed
 #
diff -urN oldtree/lib/Makefile newtree/lib/Makefile
--- oldtree/lib/Makefile	2006-10-05 15:26:55.000000000 -0400
+++ newtree/lib/Makefile	2006-10-06 17:28:20.000000000 -0400
@@ -35,6 +35,8 @@
   lib-y += dec_and_lock.o
 endif
 
+obj-$(CONFIG_DYN_PAGEFLAGS) += dyn_pageflags.o
+
 obj-$(CONFIG_CRC_CCITT)	+= crc-ccitt.o
 obj-$(CONFIG_CRC16)	+= crc16.o
 obj-$(CONFIG_CRC32)	+= crc32.o
diff -urN oldtree/lib/dyn_pageflags.c newtree/lib/dyn_pageflags.c
--- oldtree/lib/dyn_pageflags.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/lib/dyn_pageflags.c	2006-10-06 17:28:20.000000000 -0400
@@ -0,0 +1,257 @@
+/*
+ * lib/dyn_pageflags.c
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham <nigel@suspend2.net>
+ * 
+ * This file is released under the GPLv2.
+ *
+ * Routines for dynamically allocating and releasing bitmaps
+ * used as pseudo-pageflags.
+ */
+
+#include <linux/module.h>
+#include <linux/dyn_pageflags.h>
+#include <linux/bootmem.h>
+#include <linux/mm.h>
+
+#define page_to_zone_offset(pg) (page_to_pfn(pg) - page_zone(pg)->zone_start_pfn)
+
+/* 
+ * pages_for_zone(struct zone *zone)
+ * 
+ * How many pages do we need for a bitmap for this zone?
+ *
+ */
+
+static int pages_for_zone(struct zone *zone)
+{
+	return (zone->spanned_pages + (PAGE_SIZE << 3) - 1) >>
+			(PAGE_SHIFT + 3);
+}
+
+/* 
+ * clear_dyn_pageflags(dyn_pageflags_t pagemap)
+ *
+ * Clear an array used to store local page flags.
+ *
+ */
+
+void clear_dyn_pageflags(dyn_pageflags_t pagemap)
+{
+	int i = 0, zone_num = 0;
+	struct zone *zone;
+	
+	BUG_ON(!pagemap);
+
+	for_each_zone(zone) {
+		if (!populated_zone(zone))
+			continue;
+
+		zone_num = page_zone_id(pfn_to_page(zone->zone_start_pfn));
+
+		for (i = 0; i < pages_for_zone(zone); i++)
+			memset((pagemap[zone_num][i]), 0, PAGE_SIZE);
+	}
+}
+
+/* 
+ * allocate_dyn_pageflags(dyn_pageflags_t *pagemap)
+ *
+ * Allocate a bitmap for dynamic page flags.
+ *
+ */
+int allocate_dyn_pageflags(dyn_pageflags_t *pagemap)
+{
+	int i, zone_num, zone_pages;
+	struct zone *zone;
+
+	BUG_ON(*pagemap);
+
+	*pagemap = kmalloc(sizeof(void *) * (1 << ZONEID_SHIFT), GFP_ATOMIC);
+
+	if (!*pagemap)
+		return -ENOMEM;
+
+	for_each_zone(zone) {
+		if (!populated_zone(zone))
+			continue;
+
+		zone_pages = pages_for_zone(zone);
+		zone_num = page_zone_id(pfn_to_page(zone->zone_start_pfn));
+
+		(*pagemap)[zone_num] = kmalloc(sizeof(void *) * zone_pages,
+					       GFP_ATOMIC);
+
+		if (!(*pagemap)[zone_num]) {
+			free_dyn_pageflags(pagemap);
+			return -ENOMEM;
+		}
+
+		for (i = 0; i < zone_pages; i++) {
+			unsigned long address = get_zeroed_page(GFP_ATOMIC);
+			(*pagemap)[zone_num][i] = (unsigned long *) address;
+			if (!(*pagemap)[zone_num][i]) {
+				printk("Error. Unable to allocate memory for "
+					"dynamic pageflags.");
+				free_dyn_pageflags(pagemap);
+				return -ENOMEM;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* 
+ * free_dyn_pageflags(dyn_pageflags_t *pagemap)
+ *
+ * Free a dynamically allocated pageflags bitmap. For Suspend2 usage, we
+ * support data being relocated from slab to pages that don't conflict
+ * with the image that will be copied back. This is the reason for the
+ * PageSlab tests below.
+ *
+ */
+void free_dyn_pageflags(dyn_pageflags_t *pagemap)
+{
+	int i = 0, zone_num, zone_pages;
+	struct zone *zone;
+
+	if (!*pagemap)
+		return;
+	
+	for_each_zone(zone) {
+		if (!populated_zone(zone))
+			continue;
+
+		zone_pages = pages_for_zone(zone);
+		zone_num = page_zone_id(pfn_to_page(zone->zone_start_pfn));
+
+		/* 
+		 * May be called on an error path in allocating, so this
+		 * isn't redundant.
+		 */
+		if (!((*pagemap)[zone_num]))
+			continue;
+
+		for (i = 0; i < zone_pages; i++)
+			if ((*pagemap)[zone_num][i])
+				free_page((unsigned long) (*pagemap)[zone_num][i]);
+	
+		if (PageSlab(virt_to_page((*pagemap)[zone_num])))
+			kfree((*pagemap)[zone_num]);
+		else
+			free_page((unsigned long) (*pagemap)[zone_num]);
+	}
+
+	if (PageSlab(virt_to_page((*pagemap))))
+		kfree(*pagemap);
+	else
+		free_page((unsigned long) (*pagemap));
+
+	*pagemap = NULL;
+	return;
+}
+
+#define GET_BIT_AND_UL(bitmap, page) \
+	unsigned long zone_pfn = page_to_zone_offset(page); \
+	int zone_num = page_zone_id(page); \
+	int pagenum = PAGENUMBER(zone_pfn); \
+	int page_offset = PAGEINDEX(zone_pfn); \
+	unsigned long *ul = ((*bitmap)[zone_num][pagenum]) + page_offset; \
+	int bit = PAGEBIT(zone_pfn);
+
+/*
+ * test_dynpageflag(dyn_pageflags_t *bitmap, struct page *page)
+ *
+ * Is the page flagged in the given bitmap?
+ *
+ */
+
+int test_dynpageflag(dyn_pageflags_t *bitmap, struct page *page)
+{
+	GET_BIT_AND_UL(bitmap, page);
+	return test_bit(bit, ul);
+}
+
+/*
+ * set_dynpageflag(dyn_pageflags_t *bitmap, struct page *page)
+ *
+ * Set the flag for the page in the given bitmap.
+ *
+ */
+
+void set_dynpageflag(dyn_pageflags_t *bitmap, struct page *page)
+{
+	GET_BIT_AND_UL(bitmap, page);
+	set_bit(bit, ul);
+}
+
+/*
+ * clear_dynpageflags(dyn_pageflags_t *bitmap, struct page *page)
+ *
+ * Clear the flag for the page in the given bitmap.
+ *
+ */
+
+void clear_dynpageflag(dyn_pageflags_t *bitmap, struct page *page)
+{
+	GET_BIT_AND_UL(bitmap, page);
+	clear_bit(bit, ul);
+}
+
+/*
+ * get_next_bit_on(dyn_pageflags_t bitmap, int counter)
+ *
+ * Given a pfn (possibly -1), find the next pfn in the bitmap that
+ * is set. If there are no more flags set, return -1.
+ *
+ */
+
+unsigned long get_next_bit_on(dyn_pageflags_t bitmap, unsigned long counter)
+{
+	struct page *page;
+	struct zone *zone;
+	unsigned long *ul = NULL;
+	unsigned long zone_offset;
+	int pagebit, zone_num, first = (counter == max_pfn);
+
+	if (first)
+		counter = first_online_pgdat()->node_zones->zone_start_pfn;
+
+	page = pfn_to_page(counter);
+	zone = page_zone(page);
+	zone_num = page_zone_id(page);
+	zone_offset = counter - zone->zone_start_pfn;
+
+	if (first)
+		goto test;
+
+	do {
+		zone_offset++;
+	
+		if (zone_offset >= zone->spanned_pages) {
+			do {
+				zone = next_zone(zone);
+				if (!zone)
+					return max_pfn;
+			} while(!zone->spanned_pages);
+			
+			zone_num = page_zone_id(pfn_to_page(zone->zone_start_pfn));
+			zone_offset = 0;
+		}
+test:
+		pagebit = PAGEBIT(zone_offset);
+
+		if (!pagebit || !ul)
+			ul = (bitmap[zone_num][PAGENUMBER(zone_offset)]) + PAGEINDEX(zone_offset);
+
+		if (!(*ul & ~((1 << pagebit) - 1))) {
+			zone_offset += BITS_PER_LONG - pagebit - 1;
+			continue;
+		}
+
+	} while(!test_bit(pagebit, ul));
+
+	return zone->zone_start_pfn + zone_offset;
+}
+
diff -urN oldtree/lib/vsprintf.c newtree/lib/vsprintf.c
--- oldtree/lib/vsprintf.c	2006-10-05 15:26:55.000000000 -0400
+++ newtree/lib/vsprintf.c	2006-10-06 17:28:20.000000000 -0400
@@ -236,6 +236,29 @@
 	return buf;
 }
 
+/*
+ * vsnprintf_used
+ *
+ * Functionality    : Print a string with parameters to a buffer of a 
+ *                    limited size. Unlike vsnprintf, we return the number
+ *                    of bytes actually put in the buffer, not the number
+ *                    that would have been put in if it was big enough.
+ */
+int snprintf_used(char *buffer, int buffer_size, const char *fmt, ...)
+{
+	int result;
+	va_list args;
+
+	if (!buffer_size)
+		return 0;
+
+	va_start(args, fmt);
+	result = vsnprintf(buffer, buffer_size, fmt, args);
+	va_end(args);
+
+	return result > buffer_size ? buffer_size : result;
+}
+
 /**
  * vsnprintf - Format a string and place it in a buffer
  * @buf: The buffer to place the result into
diff -urN oldtree/mm/memory.c newtree/mm/memory.c
--- oldtree/mm/memory.c	2006-10-06 16:28:30.000000000 -0400
+++ newtree/mm/memory.c	2006-10-06 17:28:20.000000000 -0400
@@ -976,6 +976,15 @@
 	return page;
 }
 
+/*
+ * We want the address of the page for Suspend2 to mark as being in pageset1. 
+ */
+
+struct page *suspend2_follow_page(struct mm_struct *mm, unsigned long address)
+{
+	return follow_page(mm->mmap, address, 0);
+}
+
 int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
 		unsigned long start, int len, int write, int force,
 		struct page **pages, struct vm_area_struct **vmas)
diff -urN oldtree/mm/vmscan.c newtree/mm/vmscan.c
--- oldtree/mm/vmscan.c	2006-10-06 16:52:04.000000000 -0400
+++ newtree/mm/vmscan.c	2006-10-06 17:28:20.000000000 -0400
@@ -1151,6 +1151,77 @@
 	return ret;
 }
 
+struct lru_save {
+	struct zone 		*zone;
+	struct list_head	active_list;
+	struct list_head	inactive_list;
+	unsigned long		nr_active;
+	unsigned long		nr_inactive;
+	struct lru_save		*next;
+};
+
+struct lru_save *lru_save_list;
+
+void unlink_lru_lists(void)
+{
+	struct zone *zone;
+
+	for_each_zone(zone) {
+		struct lru_save *this;
+
+		if (!zone->spanned_pages)
+			continue;
+
+		this = (struct lru_save *)
+			kzalloc(sizeof(struct lru_save), GFP_ATOMIC);
+
+		BUG_ON(!this);
+
+		this->next = lru_save_list;
+		lru_save_list = this;
+		
+		this->zone = zone;
+
+		spin_lock_irq(&zone->lru_lock);
+		this->nr_active = zone->nr_active;
+		this->nr_inactive = zone->nr_inactive;
+		memcpy((char *) &this->active_list, (char *) &zone->active_list,
+				sizeof(struct list_head));
+		memcpy((char *) &this->inactive_list,
+				(char *) &zone->inactive_list,
+				sizeof(struct list_head));
+
+		zone->nr_active = zone->nr_inactive = 0;
+		INIT_LIST_HEAD(&zone->active_list);
+		INIT_LIST_HEAD(&zone->inactive_list);
+
+		spin_unlock_irq(&zone->lru_lock);
+	}
+}
+
+void relink_lru_lists(void)
+{
+	while(lru_save_list) {
+		struct lru_save *this = lru_save_list;
+		struct zone *zone = this->zone;
+
+		lru_save_list = this->next;
+
+		spin_lock_irq(&zone->lru_lock);
+		zone->nr_active = this->nr_active;
+		zone->nr_inactive = this->nr_inactive;
+		memcpy((char *) &zone->active_list, (char *) &this->active_list,
+				sizeof(struct list_head));
+		memcpy((char *) &zone->inactive_list,
+				(char *) &this->inactive_list,
+				sizeof(struct list_head));
+
+		spin_unlock_irq(&zone->lru_lock);
+
+		kfree(this);
+	}
+}
+
 /*
  * For kswapd, balance_pgdat() will work across all this node's zones until
  * they are all at pages_high.
