diff -urN oldtree/Documentation/DocBook/libata.tmpl newtree/Documentation/DocBook/libata.tmpl
--- oldtree/Documentation/DocBook/libata.tmpl 2006-03-27 13:28:15.000000000 -0500
+++ newtree/Documentation/DocBook/libata.tmpl 2006-03-27 15:57:04.181986500 -0500
@@ -787,6 +787,12 @@
!Idrivers/scsi/libata-scsi.c
+
+ libata ACPI interfaces/methods
+!Edrivers/scsi/ata_acpi.c
+!Idrivers/scsi/ata_acpi.c
+
+
ATA errors & exceptions
diff -urN oldtree/Documentation/kernel-parameters.txt newtree/Documentation/kernel-parameters.txt
--- oldtree/Documentation/kernel-parameters.txt 2006-03-27 13:28:15.000000000 -0500
+++ newtree/Documentation/kernel-parameters.txt 2006-03-27 16:04:08.268490250 -0500
@@ -41,6 +41,7 @@
ISAPNP ISA PnP code is enabled.
ISDN Appropriate ISDN support is enabled.
JOY Appropriate joystick support is enabled.
+ LIBATA libata driver is enabled.
LP Printer support is enabled.
LOOP Loopback device support is enabled.
M68k M68k architecture is enabled.
@@ -73,6 +74,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.
@@ -247,6 +249,9 @@
ataflop= [HW,M68k]
+ atapi_enabled= [LIBATA] Enable discovery & support of ATAPI devices
+ Format: (0=off, 1=on)
+
atarimouse= [HW,MOUSE] Atari Mouse
atascsi= [HW,SCSI] Atari SCSI
@@ -998,6 +1003,10 @@
emulation library even if a 387 maths coprocessor
is present.
+ noacpi= [LIBATA] Disables use of ACPI in libata suspend/resume
+ when set.
+ Format:
+
noalign [KNL,ARM]
noapic [SMP,APIC] Tells the kernel to not make use of any
@@ -1060,6 +1069,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).
@@ -1263,6 +1274,11 @@
autoconfiguration.
Ranges are in pairs (memory base and size).
+ printk= [LIBATA] Set libata printk level (mask).
+ The values are defined in include/linux/libata.h.
+ The default value is 1 (ATA_MSG_DRV).
+ Format:
+
profile= [KNL] Enable kernel profiling via /proc/profile
Format: [schedule,]
Param: "schedule" - profile schedule points.
@@ -1345,6 +1361,11 @@
resume= [SWSUSP]
Specify the partition device for software suspend
+ resume2= [SUSPEND2] Specify the storage device for Suspend2.
+ Format: :.
+ 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/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-03-27 16:04:08.272490500 -0500
@@ -0,0 +1,362 @@
+ 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 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. Pages added to the LRU
+ lists are immediately shot down, and careful accounting for available
+ memory aids debugging. 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. To avoid the possibility of remaining
+ activity corrupting the image, we allocate a special memory pool. Calls
+ to __alloc_pages and __free_pages_ok are then diverted to use our memory
+ pool. Pages in the memory pool are saved as part of pageset1 regardless of
+ whether or not they are used.
+
+ 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. 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.
+
+ In addition to the above routines for writing the data, all modules have a
+ number of other routines:
+
+ TYPE indicates whether the module is a page transformer or a writer.
+ #define TRANSFORMER_MODULE 1
+ #define WRITER_MODULE 2
+
+ NAME is the name of the module, used in generic messages.
+
+ MODULE_LIST is used to link the module into the list of all modules.
+
+ MEMORY_NEEDED returns the number of pages of memory required by the module
+ to do its work.
+
+ STORAGE_NEEDED returns the number of pages in the suspend header required
+ to store the module's configuration data.
+
+ PRINT_DEBUG_INFO fills a buffer with information to be displayed about the
+ operation or settings of the module.
+
+ SAVE_CONFIG_INFO returns a buffer of PAGE_SIZE or smaller (the size is the
+ return code), containing the module's configuration info. This information
+ will be written in the image header and restored at resume time. Since this
+ buffer is allocated after the atomic copy of the kernel is made, you don't
+ need to worry about the buffer being freed.
+
+ LOAD_CONFIG_INFO gives the module a pointer to the the configuration info
+ which was saved during suspending. Once again, the module doesn't need to
+ worry about freeing the buffer. The kernel will be overwritten with the
+ original kernel, so no memory leak will occur.
+
+ OPS contains the operations specific to transformers and writers. These are
+ described below.
+
+ The complete definition of struct suspend_module_ops is:
+
+ struct suspend_module_ops {
+ /* Functions common to all modules */
+ int type;
+ char *name;
+ struct module *module;
+ int disabled;
+ 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);
+
+ /*
+ * 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, 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);
+ };
+
+
+ Expected compression returns the expected ratio between the amount of
+ data sent to this module and the amount of data it passes to the next
+ module. The value is used by the core code to calculate the amount of
+ space required to write the image. If the ratio is not achieved, the
+ writer will complain when it runs out of space with data still to
+ write, and the core code will abort the suspend.
+
+ transformer_list links together page transformers, in the order in
+ which they register, which is in turn determined by order in the
+ Makefile.
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-03-27 16:04:08.284491250 -0500
@@ -0,0 +1,641 @@
+ --- 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 /proc/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.
+
+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 General Setup), 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. Also, because
+ the filewriter is comparatively new, it's not as well tested as the
+ swapwriter. Be aware that there may be bugs that could cause damage to your
+ data even if you are careful! You have been warned!
+
+ 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 > /proc/suspend2/filewriter_target
+
+ Then
+
+ cat /proc/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 /proc/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.
+
+ 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 /proc/suspend2/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@4096.
+
+ 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
+ /proc/suspend2/headerlocations for a new resume_block value.
+
+ Once you've compiled and installed the kernel, adjusted your lilo.conf
+ and rerun lilo, 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@4096 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.
+
+5. How do you use it?
+
+ Once your script is properly set up, you should just be able to start it
+ and everything should go like clockwork. Of course things aren't always
+ that easy out of the box.
+
+ Check out (in the kernel source tree) include/linux/suspend2.h for
+ settings you can use to get detailed information about what suspend is doing.
+ The kernel parameters suspend_act, suspend_dbg and suspend_lvl allow you to
+ set the action and debugging parameters prior to starting a suspend and/or
+ at the lilo prompt before resuming. There is also a nice little program that
+ should be available from suspend2.net which makes it easier to turn these
+ debugging settings on and off. Note that to get any debugging output, you
+ need to enable CONFIG_PM_DEBUG when compiling the kernel.
+
+ A neat feature of Suspend2 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 /proc/suspend2 do?
+
+ /proc/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!
+
+ - debug_info:
+
+ This file returns information about your configuration that may be helpful
+ in diagnosing problems with suspending.
+
+ - 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.
+
+ - 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.
+
+ This value can be overridden using the kernel command line option
+ suspend_lvl.
+
+ - disable_*
+
+ This option can be used to temporarily disable various parts of suspend.
+ Note that these flags can be set by restoring all_settings: If the saved
+ settings don't include any information about how a part of suspend should
+ be configured, that section will be disabled.
+
+ - 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.
+
+ - 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).
+
+ - 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.
+
+ - 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.
+
+ - 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.
+
+ - 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 /proc/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: Normal power down.
+
+ Note that these options are highly dependant upon your hardware & software.
+
+ - 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.
+
+ - swapfile:
+
+ 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 > /proc/suspend2/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.
+
+ - 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.
+
+ - 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/arch/arm/mm/init.c newtree/arch/arm/mm/init.c
--- oldtree/arch/arm/mm/init.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/arch/arm/mm/init.c 2006-03-27 16:04:08.304492500 -0500
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include
#include
@@ -85,6 +86,11 @@
printk("%d pages swap cached\n", cached);
}
+int page_is_ram(int pfn)
+{
+ return pfn_valid(pfn);
+}
+
static inline pmd_t *pmd_off(pgd_t *pgd, unsigned long virt)
{
return pmd_offset(pgd, virt);
@@ -659,6 +665,15 @@
*/
sysctl_overcommit_memory = OVERCOMMIT_ALWAYS;
}
+#ifdef CONFIG_SUSPEND2
+ {
+ unsigned long addr;
+ for (addr = &__nosave_begin; addr < &__nosave_end;
+ addr += PAGE_SIZE) {
+ SetPageNosave(virt_to_page(addr));
+ }
+ }
+#endif
}
void free_initmem(void)
diff -urN oldtree/arch/i386/mm/init.c newtree/arch/i386/mm/init.c
--- oldtree/arch/i386/mm/init.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/arch/i386/mm/init.c 2006-03-27 16:06:06.191860000 -0500
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include
#include
@@ -48,6 +49,7 @@
unsigned long highstart_pfn, highend_pfn;
static int noinline do_test_wp_bit(void);
+int bad_ppro;
/*
* Creates a middle page table and puts a pointer to it in the
@@ -279,9 +281,12 @@
{
if (page_is_ram(pfn) && !(bad_ppro && page_kills_ppro(pfn))) {
ClearPageReserved(page);
+ ClearPageNosave(page);
free_new_highpage(page);
- } else
+ } else {
SetPageReserved(page);
+ SetPageNosave(page);
+ }
}
static int add_one_highpage_hotplug(struct page *page, unsigned long pfn)
@@ -384,7 +389,7 @@
#endif
}
-#ifdef CONFIG_SOFTWARE_SUSPEND
+#ifdef CONFIG_PM
/*
* Swap suspend & friends need this for resume because things like the intel-agp
* driver might have split up a kernel 4MB mapping.
@@ -570,7 +575,7 @@
extern int ppro_with_ram_bug(void);
int codesize, reservedpages, datasize, initsize;
int tmp;
- int bad_ppro;
+ struct page *tmp_page;
#ifdef CONFIG_FLATMEM
if (!mem_map)
@@ -601,12 +606,23 @@
totalram_pages += free_all_bootmem();
reservedpages = 0;
- for (tmp = 0; tmp < max_low_pfn; tmp++)
- /*
- * Only count reserved RAM pages
- */
- if (page_is_ram(tmp) && PageReserved(pfn_to_page(tmp)))
- reservedpages++;
+ for (tmp = 0; tmp < max_low_pfn; tmp++) {
+ if (page_is_ram(tmp)) {
+ /*
+ * Only count reserved RAM pages
+ */
+ if (PageReserved(pfn_to_page(tmp)))
+ reservedpages++;
+ } else
+ /*
+ * Non-RAM pages are always nosave
+ */
+ SetPageNosave(pfn_to_page(tmp));
+ }
+
+ for (tmp_page = virt_to_page(&__nosave_begin);
+ tmp_page < virt_to_page(&__nosave_end); tmp_page++)
+ SetPageNosave(tmp_page);
set_highmem_pages_init(bad_ppro);
@@ -749,7 +765,8 @@
for (addr = begin; addr < end; addr += PAGE_SIZE) {
ClearPageReserved(virt_to_page(addr));
- init_page_count(virt_to_page(addr));
+ ClearPageNosave(virt_to_page(addr));
+init_page_count(virt_to_page(addr));
#ifdef CONFIG_DEBUG_INITDATA
/*
* Unmap the page, and leak it. So any further accesses will
diff -urN oldtree/arch/ppc/mm/init.c newtree/arch/ppc/mm/init.c
--- oldtree/arch/ppc/mm/init.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/arch/ppc/mm/init.c 2006-03-27 16:04:08.336494500 -0500
@@ -32,6 +32,7 @@
#include
#include
#include
+#include
#include
#include
@@ -407,8 +408,10 @@
/* if we are booted from BootX with an initial ramdisk,
make sure the ramdisk pages aren't reserved. */
if (initrd_start) {
- for (addr = initrd_start; addr < initrd_end; addr += PAGE_SIZE)
+ for (addr = initrd_start; addr < initrd_end; addr += PAGE_SIZE) {
ClearPageReserved(virt_to_page(addr));
+ ClearPageNosave(virt_to_page(addr));
+ }
}
#endif /* CONFIG_BLK_DEV_INITRD */
@@ -417,13 +420,21 @@
if ( rtas_data )
for (addr = (ulong)__va(rtas_data);
addr < PAGE_ALIGN((ulong)__va(rtas_data)+rtas_size) ;
- addr += PAGE_SIZE)
+ addr += PAGE_SIZE) {
SetPageReserved(virt_to_page(addr));
+ SetPageNosave(virt_to_page(addr));
+ }
#endif
for (addr = PAGE_OFFSET; addr < (unsigned long)high_memory;
addr += PAGE_SIZE) {
if (!PageReserved(virt_to_page(addr)))
continue;
+ /*
+ * Mark nosave pages
+ */
+ if (addr >= (void *)&__nosave_begin && addr < (void *)&__nosave_end)
+ SetPageNosave(virt_to_page(addr));
+
if (addr < (ulong) etext)
codepages++;
else if (addr >= (unsigned long)&__init_begin
diff -urN oldtree/arch/x86_64/kernel/e820.c newtree/arch/x86_64/kernel/e820.c
--- oldtree/arch/x86_64/kernel/e820.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/arch/x86_64/kernel/e820.c 2006-03-27 16:04:08.340494750 -0500
@@ -186,6 +186,23 @@
return end_pfn;
}
+int page_is_ram(unsigned long pagenr)
+{
+ unsigned long start = pagenr << PAGE_SHIFT;
+ int i;
+ for (i = 0; i < e820.nr_map; i++) {
+ struct e820entry *ei = &e820.map[i];
+
+ if (ei->addr+ei->size <= start ||
+ ei->addr >= (start + PAGE_SIZE))
+ continue;
+
+ return (ei->type == E820_RAM);
+ }
+
+ return 0;
+}
+
/*
* Compute how much memory is missing in a range.
* Unlike the other functions in this file the arguments are in page numbers.
diff -urN oldtree/arch/x86_64/mm/init.c newtree/arch/x86_64/mm/init.c
--- oldtree/arch/x86_64/mm/init.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/arch/x86_64/mm/init.c 2006-03-27 16:07:34.369370750 -0500
@@ -639,7 +639,8 @@
addr = (unsigned long)(&__init_begin);
for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
ClearPageReserved(virt_to_page(addr));
- init_page_count(virt_to_page(addr));
+ ClearPageNosave(virt_to_page(addr));
+ init_page_count(virt_to_page(addr));
memset((void *)(addr & ~(PAGE_SIZE-1)), 0xcc, PAGE_SIZE);
free_page(addr);
totalram_pages++;
@@ -679,7 +680,8 @@
printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
for (; start < end; start += PAGE_SIZE) {
ClearPageReserved(virt_to_page(start));
- init_page_count(virt_to_page(start));
+ ClearPageNosave(virt_to_page(start));
+ init_page_count(virt_to_page(start));
free_page(start);
totalram_pages++;
}
@@ -790,3 +792,22 @@
{
return (addr >= VSYSCALL_START) && (addr < VSYSCALL_END);
}
+
+#if defined(CONFIG_SOFTWARE_SUSPEND) || defined(CONFIG_SUSPEND2)
+/*
+ * Software suspend & friends need this for resume because things like the intel-agp
+ * driver might have split up a kernel 4MB mapping.
+ */
+char __nosavedata swsusp_pg_dir[PAGE_SIZE]
+ __attribute__ ((aligned (PAGE_SIZE)));
+
+static inline void save_pg_dir(void)
+{
+ memcpy(swsusp_pg_dir, swapper_pg_dir, PAGE_SIZE);
+}
+#else
+static inline void save_pg_dir(void)
+{
+}
+#endif
+
diff -urN oldtree/block/ll_rw_blk.c newtree/block/ll_rw_blk.c
--- oldtree/block/ll_rw_blk.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/block/ll_rw_blk.c 2006-03-27 16:08:11.199672500 -0500
@@ -28,6 +28,9 @@
#include
#include
#include
+#include
+#include
+#include
#include
/*
@@ -3157,12 +3160,26 @@
else
mod_page_state(pgpgin, count);
+ if (unlikely(( bio->bi_flags & (1 << BIO_SUSPEND2)) &&
+ test_action_state(SUSPEND_TEST_BIO) &&
+ (rw & WRITE))) {
+ char b[BDEVNAME_SIZE];
+ printk("FAKEDWRITE: %s(%d): %s block %Lu on %s\n",
+ current->comm, current->pid,
+ (rw & WRITE) ? "WRITE" : "READ",
+ (unsigned long long)bio->bi_sector,
+ bdevname(bio->bi_bdev,b));
+ bio_endio(bio, PAGE_SIZE, 0);
+ return;
+ }
+
if (unlikely(block_dump)) {
char b[BDEVNAME_SIZE];
- printk(KERN_DEBUG "%s(%d): %s block %Lu on %s\n",
+ printk(KERN_DEBUG "%s(%d): %s block %Lu size %d on %s\n",
current->comm, current->pid,
(rw & WRITE) ? "WRITE" : "READ",
(unsigned long long)bio->bi_sector,
+ bio->bi_size,
bdevname(bio->bi_bdev,b));
}
@@ -3556,7 +3573,7 @@
{
int i;
- kblockd_workqueue = create_workqueue("kblockd");
+ kblockd_workqueue = create_nofreeze_workqueue("kblockd");
if (!kblockd_workqueue)
panic("Failed to create kblockd\n");
diff -urN oldtree/crypto/Kconfig newtree/crypto/Kconfig
--- oldtree/crypto/Kconfig 2006-03-27 13:28:15.000000000 -0500
+++ newtree/crypto/Kconfig 2006-03-27 15:56:21.011288500 -0500
@@ -316,6 +316,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"
depends on CRYPTO
diff -urN oldtree/crypto/Makefile newtree/crypto/Makefile
--- oldtree/crypto/Makefile 2006-03-27 13:28:15.000000000 -0500
+++ newtree/crypto/Makefile 2006-03-27 15:56:21.019289000 -0500
@@ -30,5 +30,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/deflate.c newtree/crypto/deflate.c
--- oldtree/crypto/deflate.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/crypto/deflate.c 2006-03-27 15:56:33.516070000 -0500
@@ -142,8 +142,15 @@
ret = zlib_deflate(stream, Z_FINISH);
if (ret != Z_STREAM_END) {
- ret = -EINVAL;
- goto out;
+ if (!(ret == Z_OK && !stream->avail_in && !stream->avail_out)) {
+ ret = -EINVAL;
+ goto out;
+ } else {
+ u8 zerostuff = 0;
+ stream->next_out = &zerostuff;
+ stream->avail_out = 1;
+ ret = zlib_deflate(stream, Z_FINISH);
+ }
}
ret = 0;
*dlen = stream->total_out;
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-03-27 15:56:21.015288750 -0500
@@ -0,0 +1,335 @@
+/*
+ * Cryptoapi LZF compression module.
+ *
+ * Copyright (c) 2004-2005 Nigel Cunningham
+ *
+ * based on the deflate.c file:
+ *
+ * Copyright (c) 2003 James Morris
+ *
+ * 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
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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 ; literal
+ * LLLOOOOO oooooooo ; backref L
+ * 111OOOOO LLLLLLLL oooooooo ; backref L+7
+ *
+ */
+
+static void lzf_compress_exit(void *context)
+{
+ struct lzf_ctx *ctx = (struct lzf_ctx *)context;
+
+ if (ctx->hbuf) {
+ vfree(ctx->hbuf);
+ ctx->hbuf = NULL;
+ }
+}
+
+static int lzf_compress_init(void *context)
+{
+ struct lzf_ctx *ctx = (struct lzf_ctx *)context;
+
+ /* 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(void *context, const u8 *in_data, unsigned int in_len,
+ u8 *out_data, unsigned int *out_len)
+{
+ struct lzf_ctx *ctx = (struct lzf_ctx *)context;
+ 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(void *context, 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_u = {.compress = {
+ .coa_init = lzf_compress_init,
+ .coa_exit = lzf_compress_exit,
+ .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/acpi/osl.c newtree/drivers/acpi/osl.c
--- oldtree/drivers/acpi/osl.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/acpi/osl.c 2006-03-27 15:30:38.222870250 -0500
@@ -91,7 +91,7 @@
"Access to PCI configuration space unavailable\n");
return AE_NULL_ENTRY;
}
- kacpid_wq = create_singlethread_workqueue("kacpid");
+ kacpid_wq = create_nofreeze_singlethread_workqueue("kacpid");
BUG_ON(!kacpid_wq);
return AE_OK;
diff -urN oldtree/drivers/acpi/sleep/proc.c newtree/drivers/acpi/sleep/proc.c
--- oldtree/drivers/acpi/sleep/proc.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/acpi/sleep/proc.c 2006-03-27 15:56:41.732583500 -0500
@@ -58,6 +58,15 @@
goto Done;
}
state = simple_strtoul(str, NULL, 0);
+
+ /*
+ * I used to put this after the CONFIG_SOFTWARE_SUSPEND
+ * test, but people who compile in suspend2 usually want
+ * to use it instead of swsusp. --NC
+ */
+ if (may_try_suspend2(state))
+ goto Done;
+
#ifdef CONFIG_SOFTWARE_SUSPEND
if (state == 4) {
error = software_suspend();
diff -urN oldtree/drivers/char/hvc_console.c newtree/drivers/char/hvc_console.c
--- oldtree/drivers/char/hvc_console.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/char/hvc_console.c 2006-03-27 15:30:38.230870750 -0500
@@ -841,7 +841,7 @@
/* Always start the kthread because there can be hotplug vty adapters
* added later. */
- hvc_task = kthread_run(khvcd, NULL, "khvcd");
+ hvc_task = kthread_nofreeze_run(khvcd, NULL, "khvcd");
if (IS_ERR(hvc_task)) {
panic("Couldn't create kthread for console.\n");
put_tty_driver(hvc_driver);
diff -urN oldtree/drivers/char/hvcs.c newtree/drivers/char/hvcs.c
--- oldtree/drivers/char/hvcs.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/char/hvcs.c 2006-03-27 15:30:38.242871500 -0500
@@ -1405,7 +1405,7 @@
return -ENOMEM;
}
- hvcs_task = kthread_run(khvcsd, NULL, "khvcsd");
+ hvcs_task = kthread_nofreeze_run(khvcsd, NULL, "khvcsd");
if (IS_ERR(hvcs_task)) {
printk(KERN_ERR "HVCS: khvcsd creation failed. Driver not loaded.\n");
kfree(hvcs_pi_buff);
diff -urN oldtree/drivers/input/serio/serio.c newtree/drivers/input/serio/serio.c
--- oldtree/drivers/input/serio/serio.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/input/serio/serio.c 2006-03-27 15:30:38.250872000 -0500
@@ -903,7 +903,7 @@
static int __init serio_init(void)
{
- serio_task = kthread_run(serio_thread, NULL, "kseriod");
+ serio_task = kthread_nofreeze_run(serio_thread, NULL, "kseriod");
if (IS_ERR(serio_task)) {
printk(KERN_ERR "serio: Failed to start kseriod\n");
return PTR_ERR(serio_task);
diff -urN oldtree/drivers/macintosh/Kconfig newtree/drivers/macintosh/Kconfig
--- oldtree/drivers/macintosh/Kconfig 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/macintosh/Kconfig 2006-03-27 16:04:08.376497000 -0500
@@ -200,4 +200,8 @@
tristate "Support for ANS LCD display"
depends on ADB_CUDA && PPC_PMAC
+config SOFTWARE_REPLACE_SLEEP
+ bool "Using Software suspend replace broken sleep function"
+ depends on SUSPEND2
+
endmenu
diff -urN oldtree/drivers/macintosh/via-pmu.c newtree/drivers/macintosh/via-pmu.c
--- oldtree/drivers/macintosh/via-pmu.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/macintosh/via-pmu.c 2006-03-27 16:04:08.384497500 -0500
@@ -2654,6 +2654,13 @@
return -EACCES;
if (sleep_in_progress)
return -EBUSY;
+#ifdef CONFIG_SOFTWARE_REPLACE_SLEEP
+ {
+ extern void software_suspend_pending(void);
+ software_suspend_pending();
+ return (0);
+ }
+#endif
sleep_in_progress = 1;
switch (pmu_kind) {
case PMU_OHARE_BASED:
diff -urN oldtree/drivers/md/dm-crypt.c newtree/drivers/md/dm-crypt.c
--- oldtree/drivers/md/dm-crypt.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/md/dm-crypt.c 2006-03-27 15:30:38.258872500 -0500
@@ -915,7 +915,7 @@
if (!_crypt_io_pool)
return -ENOMEM;
- _kcryptd_workqueue = create_workqueue("kcryptd");
+ _kcryptd_workqueue = create_nofreeze_workqueue("kcryptd");
if (!_kcryptd_workqueue) {
r = -ENOMEM;
DMERR(PFX "couldn't create kcryptd");
diff -urN oldtree/drivers/md/md.c newtree/drivers/md/md.c
--- oldtree/drivers/md/md.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/md/md.c 2006-03-27 15:30:38.274873500 -0500
@@ -41,7 +41,6 @@
#include
#include
#include /* for invalidate_bdev */
-#include
#include
#include
@@ -4112,7 +4111,8 @@
thread->run = run;
thread->mddev = mddev;
thread->timeout = MAX_SCHEDULE_TIMEOUT;
- thread->tsk = kthread_run(md_thread, thread, name, mdname(thread->mddev));
+ thread->tsk = kthread_nofreeze_run(md_thread, thread,
+ name, mdname(thread->mddev));
if (IS_ERR(thread->tsk)) {
kfree(thread);
return NULL;
diff -urN oldtree/drivers/scsi/Kconfig newtree/drivers/scsi/Kconfig
--- oldtree/drivers/scsi/Kconfig 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/scsi/Kconfig 2006-03-27 15:57:04.237990000 -0500
@@ -699,6 +699,19 @@
depends on IDE=y && !BLK_DEV_IDE_SATA && (SCSI_SATA_AHCI || SCSI_ATA_PIIX)
default y
+config SCSI_SATA_ACPI
+ bool
+ depends on SCSI_SATA && ACPI && PCI
+ default y
+ help
+ This option adds support for SATA-related ACPI objects.
+ These ACPI objects add the ability to retrieve taskfiles
+ from the ACPI BIOS and write them to the disk controller.
+ These objects may be related to performance, security,
+ power management, or other areas.
+ You can disable this at kernel boot time by using the
+ option 'libata.noacpi'.
+
config SCSI_BUSLOGIC
tristate "BusLogic SCSI support"
depends on (PCI || ISA || MCA) && SCSI && ISA_DMA_API
diff -urN oldtree/drivers/scsi/Makefile newtree/drivers/scsi/Makefile
--- oldtree/drivers/scsi/Makefile 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/scsi/Makefile 2006-03-27 15:59:20.430501500 -0500
@@ -177,7 +177,8 @@
CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m)
zalon7xx-objs := zalon.o ncr53c8xx.o
NCR_Q720_mod-objs := NCR_Q720.o ncr53c8xx.o
-libata-objs := libata-core.o libata-scsi.o libata-bmdma.o
+libata-y := libata-core.o libata-scsi.o libata-bmdma.o
+libata-$(CONFIG_SCSI_SATA_ACPI) += libata-acpi.o
oktagon_esp_mod-objs := oktagon_esp.o oktagon_io.o
# Files generated that shall be removed upon make clean
diff -urN oldtree/drivers/scsi/ahci.c newtree/drivers/scsi/ahci.c
--- oldtree/drivers/scsi/ahci.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/scsi/ahci.c 2006-03-27 15:57:04.217988750 -0500
@@ -218,6 +218,7 @@
.dma_boundary = AHCI_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .shutdown = ata_scsi_device_shutdown,
};
static const struct ata_port_operations ahci_ops = {
diff -urN oldtree/drivers/scsi/ata_piix.c newtree/drivers/scsi/ata_piix.c
--- oldtree/drivers/scsi/ata_piix.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/scsi/ata_piix.c 2006-03-27 15:57:04.225989250 -0500
@@ -222,6 +222,7 @@
.bios_param = ata_std_bios_param,
.resume = ata_scsi_device_resume,
.suspend = ata_scsi_device_suspend,
+ .shutdown = ata_scsi_device_shutdown,
};
static const struct ata_port_operations piix_pata_ops = {
diff -urN oldtree/drivers/scsi/hosts.c newtree/drivers/scsi/hosts.c
--- oldtree/drivers/scsi/hosts.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/scsi/hosts.c 2006-03-27 15:30:38.278873750 -0500
@@ -227,7 +227,7 @@
if (shost->transportt->create_work_queue) {
snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d",
shost->host_no);
- shost->work_q = create_singlethread_workqueue(
+ shost->work_q = create_nofreeze_singlethread_workqueue(
shost->work_q_name);
if (!shost->work_q)
goto out_free_shost_data;
diff -urN oldtree/drivers/scsi/libata-acpi.c newtree/drivers/scsi/libata-acpi.c
--- oldtree/drivers/scsi/libata-acpi.c 1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/libata-acpi.c 2006-03-27 15:57:04.241990250 -0500
@@ -0,0 +1,974 @@
+/*
+ * libata-acpi.c
+ * Provides ACPI support for PATA/SATA.
+ *
+ * Copyright (C) 2005 Intel Corp.
+ * Copyright (C) 2005 Randy Dunlap
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "scsi.h"
+#include
+#include
+#include "libata.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define SATA_ROOT_PORT(x) (((x) >> 16) & 0xffff)
+#define SATA_PORT_NUMBER(x) ((x) & 0xffff) /* or NO_PORT_MULT */
+#define NO_PORT_MULT 0xffff
+#define SATA_ADR_RSVD 0xffffffff
+
+#define REGS_PER_GTF 7
+struct taskfile_array {
+ u8 tfa[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */
+};
+
+struct GTM_buffer {
+ __u32 PIO_speed0;
+ __u32 DMA_speed0;
+ __u32 PIO_speed1;
+ __u32 DMA_speed1;
+ __u32 GTM_flags;
+};
+
+#define DEBUGGING 1
+/* note: adds function name and KERN_DEBUG */
+#ifdef DEBUGGING
+#define DEBPRINT(fmt, args...) \
+ printk(KERN_DEBUG "%s: " fmt, __FUNCTION__, ## args)
+#else
+#define DEBPRINT(fmt, args...) do {} while (0)
+#endif /* DEBUGGING */
+
+/**
+ * sata_get_dev_handle - finds acpi_handle and PCI device.function
+ * @dev: device to locate
+ * @handle: returned acpi_handle for @dev
+ * @pcidevfn: return PCI device.func for @dev
+ *
+ * This function is somewhat SATA-specific. Or at least the
+ * IDE and SCSI versions of this function are different,
+ * so it's not entirely generic code.
+ *
+ * Returns 0 on success, <0 on error.
+ */
+static int sata_get_dev_handle(struct device *dev, acpi_handle *handle,
+ acpi_integer *pcidevfn)
+{
+ struct pci_dev *pci_dev;
+ acpi_integer addr;
+
+ pci_dev = to_pci_dev(dev); /* NOTE: PCI-specific */
+ /* Please refer to the ACPI spec for the syntax of _ADR. */
+ addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
+ *pcidevfn = addr;
+ *handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
+ printk(KERN_DEBUG "%s: SATA dev addr=0x%llx, handle=0x%p\n",
+ __FUNCTION__, (unsigned long long)addr, *handle);
+ if (!*handle)
+ return -ENODEV;
+ return 0;
+}
+
+/**
+ * pata_get_dev_handle - finds acpi_handle and PCI device.function
+ * @dev: device to locate
+ * @handle: returned acpi_handle for @dev
+ * @pcidevfn: return PCI device.func for @dev
+ *
+ * The PATA and SATA versions of this function are different.
+ *
+ * Returns 0 on success, <0 on error.
+ */
+static int pata_get_dev_handle(struct device *dev, acpi_handle *handle,
+ acpi_integer *pcidevfn)
+{
+ unsigned int domain, bus, devnum, func;
+ acpi_integer addr;
+ acpi_handle dev_handle, parent_handle;
+ int scanned;
+ struct acpi_buffer buffer = {.length = ACPI_ALLOCATE_BUFFER,
+ .pointer = NULL};
+ acpi_status status;
+ struct acpi_device_info *dinfo = NULL;
+ int ret = -ENODEV;
+
+ printk(KERN_DEBUG "%s: ENTER: dev->bus_id='%s'\n",
+ __FUNCTION__, dev->bus_id);
+ if ((scanned = sscanf(dev->bus_id, "%x:%x:%x.%x",
+ &domain, &bus, &devnum, &func)) != 4) {
+ printk(KERN_DEBUG "%s: sscanf ret. %d\n",
+ __FUNCTION__, scanned);
+ goto err;
+ }
+
+ dev_handle = DEVICE_ACPI_HANDLE(dev);
+ parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
+
+ status = acpi_get_object_info(parent_handle, &buffer);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_DEBUG "%s: get_object_info for parent failed\n",
+ __FUNCTION__);
+ goto err;
+ }
+ dinfo = buffer.pointer;
+ if (dinfo && (dinfo->valid & ACPI_VALID_ADR) &&
+ dinfo->address == bus) {
+ /* ACPI spec for _ADR for PCI bus: */
+ addr = (acpi_integer)(devnum << 16 | func);
+ *pcidevfn = addr;
+ *handle = dev_handle;
+ } else {
+ printk(KERN_DEBUG "%s: get_object_info for parent has wrong "
+ " bus: %llu, should be %d\n",
+ __FUNCTION__,
+ dinfo ? (unsigned long long)dinfo->address : -1ULL,
+ bus);
+ goto err;
+ }
+
+ printk(KERN_DEBUG "%s: dev_handle: 0x%p, parent_handle: 0x%p\n",
+ __FUNCTION__, dev_handle, parent_handle);
+ printk(KERN_DEBUG
+ "%s: for dev=0x%x.%x, addr=0x%llx, parent=0x%p, *handle=0x%p\n",
+ __FUNCTION__, devnum, func, (unsigned long long)addr,
+ dev->parent, *handle);
+ if (!*handle)
+ goto err;
+ ret = 0;
+err:
+ acpi_os_free(dinfo);
+ return ret;
+}
+
+struct walk_info { /* can be trimmed some */
+ struct device *dev;
+ struct acpi_device *adev;
+ acpi_handle handle;
+ acpi_integer pcidevfn;
+ unsigned int drivenum;
+ acpi_handle obj_handle;
+ struct ata_port *ataport;
+ struct ata_device *atadev;
+ u32 sata_adr;
+ int status;
+ char basepath[ACPI_PATHNAME_MAX];
+ int basepath_len;
+};
+
+static acpi_status get_devices(acpi_handle handle,
+ u32 level, void *context, void **return_value)
+{
+ acpi_status status;
+ struct walk_info *winfo = context;
+ struct acpi_buffer namebuf = {ACPI_ALLOCATE_BUFFER, NULL};
+ char *pathname;
+ struct acpi_buffer buffer;
+ struct acpi_device_info *dinfo;
+
+ status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &namebuf);
+ if (status)
+ goto ret;
+ pathname = namebuf.pointer;
+
+ buffer.length = ACPI_ALLOCATE_BUFFER;
+ buffer.pointer = NULL;
+ status = acpi_get_object_info(handle, &buffer);
+
+ if (ACPI_SUCCESS(status)) {
+ dinfo = buffer.pointer;
+
+ /* find full device path name for pcidevfn */
+ if (dinfo && (dinfo->valid & ACPI_VALID_ADR) &&
+ dinfo->address == winfo->pcidevfn) {
+ if (ata_msg_probe(winfo->ataport))
+ printk(KERN_DEBUG
+ ":%s: matches pcidevfn (0x%llx)\n",
+ pathname, winfo->pcidevfn);
+ strlcpy(winfo->basepath, pathname,
+ sizeof(winfo->basepath));
+ winfo->basepath_len = strlen(pathname);
+ goto out;
+ }
+
+ /* if basepath is not yet known, ignore this object */
+ if (!winfo->basepath_len)
+ goto out;
+
+ /* if this object is in scope of basepath, maybe use it */
+ if (strncmp(pathname, winfo->basepath,
+ winfo->basepath_len) == 0) {
+ if (!(dinfo->valid & ACPI_VALID_ADR))
+ goto out;
+ if (ata_msg_probe(winfo->ataport))
+ printk(KERN_DEBUG "GOT ONE: (%s) "
+ "root_port = 0x%llx, port_num = 0x%llx\n",
+ pathname,
+ SATA_ROOT_PORT(dinfo->address),
+ SATA_PORT_NUMBER(dinfo->address));
+ /* heuristics: */
+ if (SATA_PORT_NUMBER(dinfo->address) != NO_PORT_MULT)
+ if (ata_msg_probe(winfo->ataport))
+ printk(KERN_DEBUG
+ "warning: don't know how to handle SATA port multiplier\n");
+ if (SATA_ROOT_PORT(dinfo->address) ==
+ winfo->ataport->port_no &&
+ SATA_PORT_NUMBER(dinfo->address) == NO_PORT_MULT) {
+ if (ata_msg_probe(winfo->ataport))
+ printk(KERN_DEBUG
+ "THIS ^^^^^ is the requested SATA drive (handle = 0x%p)\n",
+ handle);
+ winfo->sata_adr = dinfo->address;
+ winfo->obj_handle = handle;
+ }
+ }
+out:
+ acpi_os_free(dinfo);
+ }
+ acpi_os_free(pathname);
+
+ret:
+ return status;
+}
+
+/* Get the SATA drive _ADR object. */
+static int get_sata_adr(struct device *dev, acpi_handle handle,
+ acpi_integer pcidevfn, unsigned int drive,
+ struct ata_port *ap,
+ struct ata_device *atadev, u32 *dev_adr)
+{
+ acpi_status status;
+ struct walk_info *winfo;
+ int err = -ENOMEM;
+
+ winfo = kzalloc(sizeof(struct walk_info), GFP_KERNEL);
+ if (!winfo)
+ goto out;
+
+ winfo->dev = dev;
+ winfo->atadev = atadev;
+ winfo->ataport = ap;
+ if (acpi_bus_get_device(handle, &winfo->adev) < 0)
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "acpi_bus_get_device failed\n");
+ winfo->handle = handle;
+ winfo->pcidevfn = pcidevfn;
+ winfo->drivenum = drive;
+
+ status = acpi_get_devices(NULL, get_devices, winfo, NULL);
+ if (ACPI_FAILURE(status)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: acpi_get_devices failed\n",
+ __FUNCTION__);
+ err = -ENODEV;
+ } else {
+ *dev_adr = winfo->sata_adr;
+ atadev->obj_handle = winfo->obj_handle;
+ err = 0;
+ }
+ kfree(winfo);
+out:
+ return err;
+}
+
+/**
+ * ata_acpi_push_id - send Identify data to a drive
+ * @ap: the ata_port for the drive
+ * @ix: drive index
+ *
+ * _SDD ACPI object: for SATA mode only.
+ * Must be after Identify (Packet) Device -- uses its data.
+ */
+int ata_acpi_push_id(struct ata_port *ap, unsigned int ix)
+{
+ acpi_handle handle;
+ acpi_integer pcidevfn;
+ int err = -ENODEV;
+ struct device *dev = ap->host_set->dev;
+ struct ata_device *atadev = &ap->device[ix];
+ u32 dev_adr;
+ acpi_status status;
+ struct acpi_object_list input;
+ union acpi_object in_params[1];
+
+ if (ap->legacy_mode) {
+ printk(KERN_DEBUG "%s: skipping for PATA mode\n",
+ __FUNCTION__);
+ return 0;
+ }
+ if (noacpi)
+ return 0;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: ap->id: %d, ix = %d, port#: %d, hard_port#: %d\n",
+ __FUNCTION__, ap->id, ix,
+ ap->port_no, ap->hard_port_no);
+
+ /* Don't continue if not a SATA device. */
+ if (!ata_id_is_sata(atadev->id)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ata_id_is_sata is False\n",
+ __FUNCTION__);
+ goto out;
+ }
+
+ /* Don't continue if device has no _ADR method.
+ * _SDD is intended for known motherboard devices. */
+ err = sata_get_dev_handle(dev, &handle, &pcidevfn);
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: sata_get_dev_handle failed (%d\n",
+ __FUNCTION__, err);
+ goto out;
+ }
+
+ /* Get this drive's _ADR info. if not already known. */
+ if (!atadev->obj_handle) {
+ dev_adr = SATA_ADR_RSVD;
+ err = get_sata_adr(dev, handle, pcidevfn, ix, ap, atadev,
+ &dev_adr);
+ if (err < 0 || dev_adr == SATA_ADR_RSVD ||
+ !atadev->obj_handle) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: get_sata_adr failed: "
+ "err=%d, dev_adr=%u, obj_handle=0x%p\n",
+ __FUNCTION__, err, dev_adr,
+ atadev->obj_handle);
+ goto out;
+ }
+ }
+
+ /* Give the drive Identify data to the drive via the _SDD method */
+ /* _SDD: set up input parameters */
+ input.count = 1;
+ input.pointer = in_params;
+ in_params[0].type = ACPI_TYPE_BUFFER;
+ in_params[0].buffer.length = sizeof(atadev->id);
+ in_params[0].buffer.pointer = (u8 *)atadev->id;
+ /* Output buffer: _SDD has no output */
+
+ /* It's OK for _SDD to be missing too. */
+ swap_buf_le16(atadev->id, ATA_ID_WORDS);
+ status = acpi_evaluate_object(atadev->obj_handle, "_SDD", &input, NULL);
+ swap_buf_le16(atadev->id, ATA_ID_WORDS);
+
+ err = ACPI_FAILURE(status) ? -EIO : 0;
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "ata%u(%u): %s _SDD error: status = 0x%x\n",
+ ap->id, ap->device->devno,
+ __FUNCTION__, status);
+ }
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(ata_acpi_push_id);
+
+/**
+ * do_drive_get_GTF - get the drive bootup default taskfile settings
+ * @ap: the ata_port for the drive
+ * @ix: target ata_device (drive) index
+ * @gtf_length: number of bytes of _GTF data returned at @gtf_address
+ * @gtf_address: buffer containing _GTF taskfile arrays
+ *
+ * This applies to both PATA and SATA drives.
+ *
+ * The _GTF method has no input parameters.
+ * It returns a variable number of register set values (registers
+ * hex 1F1..1F7, taskfiles).
+ * The is not known in advance, so have ACPI-CA
+ * allocate the buffer as needed and return it, then free it later.
+ *
+ * The returned @gtf_length and @gtf_address are only valid if the
+ * function return value is 0.
+ */
+int do_drive_get_GTF(struct ata_port *ap, int ix,
+ unsigned int *gtf_length, unsigned long *gtf_address,
+ unsigned long *obj_loc)
+{
+ acpi_status status;
+ acpi_handle dev_handle;
+ acpi_handle chan_handle, drive_handle;
+ acpi_integer pcidevfn;
+ u32 dev_adr;
+ struct acpi_buffer output;
+ union acpi_object *out_obj;
+ struct device *dev = ap->host_set->dev;
+ struct ata_device *atadev = &ap->device[ix];
+ int err = -ENODEV;
+
+ *gtf_length = 0;
+ *gtf_address = 0UL;
+ *obj_loc = 0UL;
+
+ if (noacpi)
+ return 0;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: ENTER: ap->id: %d, port#: %d, hard_port#: %d\n",
+ __FUNCTION__, ap->id,
+ ap->port_no, ap->hard_port_no);
+
+ if (!ata_dev_present(atadev) ||
+ (ap->flags & ATA_FLAG_PORT_DISABLED)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ERR: "
+ "ata_dev_present: %d, PORT_DISABLED: %lu\n",
+ __FUNCTION__, ata_dev_present(atadev),
+ ap->flags & ATA_FLAG_PORT_DISABLED);
+ goto out;
+ }
+
+ /* Don't continue if device has no _ADR method.
+ * _GTF is intended for known motherboard devices. */
+ if (!ata_id_is_sata(atadev->id)) {
+ err = pata_get_dev_handle(dev, &dev_handle, &pcidevfn);
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: pata_get_dev_handle failed (%d)\n",
+ __FUNCTION__, err);
+ goto out;
+ }
+ } else {
+ err = sata_get_dev_handle(dev, &dev_handle, &pcidevfn);
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: sata_get_dev_handle failed (%d\n",
+ __FUNCTION__, err);
+ goto out;
+ }
+ }
+
+ /* Get this drive's _ADR info. if not already known. */
+ if (!atadev->obj_handle) {
+ if (!ata_id_is_sata(atadev->id)) {
+ /* get child objects of dev_handle == channel objects,
+ * + _their_ children == drive objects */
+ /* channel is ap->hard_port_no */
+ chan_handle = acpi_get_child(dev_handle,
+ ap->hard_port_no);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: chan adr=%d: chan_handle=0x%p\n",
+ __FUNCTION__, ap->hard_port_no,
+ chan_handle);
+ if (!chan_handle) {
+ err = -ENODEV;
+ goto out;
+ }
+ /* TBD: could also check ACPI object VALID bits */
+ drive_handle = acpi_get_child(chan_handle, ix);
+ printk(KERN_DEBUG "%s: drive w/ adr=%d: %c: 0x%p\n",
+ __FUNCTION__, ix,
+ ap->device[0].class == ATA_DEV_NONE ? 'n' : 'v',
+ drive_handle);
+ if (!drive_handle) {
+ err = -ENODEV;
+ goto out;
+ }
+ dev_adr = ix;
+ atadev->obj_handle = drive_handle;
+ } else { /* for SATA mode */
+ dev_adr = SATA_ADR_RSVD;
+ err = get_sata_adr(dev, dev_handle, pcidevfn, 0,
+ ap, atadev, &dev_adr);
+ }
+ if (err < 0 || dev_adr == SATA_ADR_RSVD ||
+ !atadev->obj_handle) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: get_sata/pata_adr failed: "
+ "err=%d, dev_adr=%u, obj_handle=0x%p\n",
+ __FUNCTION__, err, dev_adr,
+ atadev->obj_handle);
+ goto out;
+ }
+ }
+
+ /* Setting up output buffer */
+ output.length = ACPI_ALLOCATE_BUFFER;
+ output.pointer = NULL; /* ACPI-CA sets this; save/free it later */
+
+ /* _GTF has no input parameters */
+ err = -EIO;
+ status = acpi_evaluate_object(atadev->obj_handle, "_GTF",
+ NULL, &output);
+ if (ACPI_FAILURE(status)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: Run _GTF error: status = 0x%x\n",
+ __FUNCTION__, status);
+ goto out;
+ }
+
+ if (!output.length || !output.pointer) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: Run _GTF: "
+ "length or ptr is NULL (0x%llx, 0x%p)\n",
+ __FUNCTION__,
+ (unsigned long long)output.length,
+ output.pointer);
+ acpi_os_free(output.pointer);
+ goto out;
+ }
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
+ acpi_os_free(output.pointer);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: Run _GTF: error: "
+ "expected object type of ACPI_TYPE_BUFFER, "
+ "got 0x%x\n",
+ __FUNCTION__, out_obj->type);
+ err = -ENOENT;
+ goto out;
+ }
+
+ if (!out_obj->buffer.length || !out_obj->buffer.pointer ||
+ out_obj->buffer.length % REGS_PER_GTF) {
+ if (ata_msg_drv(ap))
+ printk(KERN_ERR
+ "%s: unexpected GTF length (%d) or addr (0x%p)\n",
+ __FUNCTION__, out_obj->buffer.length,
+ out_obj->buffer.pointer);
+ err = -ENOENT;
+ goto out;
+ }
+
+ *gtf_length = out_obj->buffer.length;
+ *gtf_address = (unsigned long)out_obj->buffer.pointer;
+ *obj_loc = (unsigned long)out_obj;
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: returning "
+ "gtf_length=%d, gtf_address=0x%lx, obj_loc=0x%lx\n",
+ __FUNCTION__, *gtf_length, *gtf_address, *obj_loc);
+ err = 0;
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(do_drive_get_GTF);
+
+/**
+ * taskfile_load_raw - send taskfile registers to host controller
+ * @ap: Port to which output is sent
+ * @gtf: raw ATA taskfile register set (0x1f1 - 0x1f7)
+ *
+ * Outputs ATA taskfile to standard ATA host controller using MMIO
+ * or PIO as indicated by the ATA_FLAG_MMIO flag.
+ * Writes the control, feature, nsect, lbal, lbam, and lbah registers.
+ * Optionally (ATA_TFLAG_LBA48) writes hob_feature, hob_nsect,
+ * hob_lbal, hob_lbam, and hob_lbah.
+ *
+ * This function waits for idle (!BUSY and !DRQ) after writing
+ * registers. If the control register has a new value, this
+ * function also waits for idle after writing control and before
+ * writing the remaining registers.
+ *
+ * LOCKING: TBD:
+ * Inherited from caller.
+ */
+static void taskfile_load_raw(struct ata_port *ap,
+ struct ata_device *atadev,
+ const struct taskfile_array *gtf)
+{
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: (0x1f1-1f7): hex: "
+ "%02x %02x %02x %02x %02x %02x %02x\n",
+ __FUNCTION__,
+ gtf->tfa[0], gtf->tfa[1], gtf->tfa[2],
+ gtf->tfa[3], gtf->tfa[4], gtf->tfa[5], gtf->tfa[6]);
+
+ if (ap->ops->qc_issue) {
+ struct ata_taskfile tf;
+ unsigned int err;
+
+ ata_tf_init(ap, &tf, atadev->devno);
+
+ /* convert gtf to tf */
+ tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; /* TBD */
+ tf.protocol = atadev->class == ATA_DEV_ATAPI ?
+ ATA_PROT_ATAPI_NODATA : ATA_PROT_NODATA;
+ tf.feature = gtf->tfa[0]; /* 0x1f1 */
+ tf.nsect = gtf->tfa[1]; /* 0x1f2 */
+ tf.lbal = gtf->tfa[2]; /* 0x1f3 */
+ tf.lbam = gtf->tfa[3]; /* 0x1f4 */
+ tf.lbah = gtf->tfa[4]; /* 0x1f5 */
+ tf.device = gtf->tfa[5]; /* 0x1f6 */
+ tf.command = gtf->tfa[6]; /* 0x1f7 */
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "call ata_exec_internal:\n");
+ err = ata_exec_internal(ap, atadev, &tf, DMA_NONE, NULL, 0);
+ if (err && ata_msg_probe(ap))
+ printk(KERN_ERR "%s: ata_exec_internal failed: %u\n",
+ __FUNCTION__, err);
+ } else
+ if (ata_msg_warn(ap))
+ printk(KERN_WARNING
+ "%s: SATA driver is missing qc_issue function entry points\n",
+ __FUNCTION__);
+}
+
+/**
+ * do_drive_set_taskfiles - write the drive taskfile settings from _GTF
+ * @ap: the ata_port for the drive
+ * @atadev: target ata_device
+ * @gtf_length: total number of bytes of _GTF taskfiles
+ * @gtf_address: location of _GTF taskfile arrays
+ *
+ * This applies to both PATA and SATA drives.
+ *
+ * Write {gtf_address, length gtf_length} in groups of
+ * REGS_PER_GTF bytes.
+ */
+int do_drive_set_taskfiles(struct ata_port *ap, struct ata_device *atadev,
+ unsigned int gtf_length, unsigned long gtf_address)
+{
+ int err = -ENODEV;
+ int gtf_count = gtf_length / REGS_PER_GTF;
+ int ix;
+ struct taskfile_array *gtf;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: ENTER: ap->id: %d, port#: %d, hard_port#: %d\n",
+ __FUNCTION__, ap->id,
+ ap->port_no, ap->hard_port_no);
+
+ if (noacpi)
+ return 0;
+ if (!ata_id_is_sata(atadev->id)) {
+ printk(KERN_DEBUG "%s: skipping non-SATA drive\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ if (!ata_dev_present(atadev) ||
+ (ap->flags & ATA_FLAG_PORT_DISABLED))
+ goto out;
+ if (!gtf_count) /* shouldn't be here */
+ goto out;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: total GTF bytes=%u (0x%x), gtf_count=%d, addr=0x%lx\n",
+ __FUNCTION__, gtf_length, gtf_length, gtf_count,
+ gtf_address);
+ if (gtf_length % REGS_PER_GTF) {
+ if (ata_msg_drv(ap))
+ printk(KERN_ERR "%s: unexpected GTF length (%d)\n",
+ __FUNCTION__, gtf_length);
+ goto out;
+ }
+
+ for (ix = 0; ix < gtf_count; ix++) {
+ gtf = (struct taskfile_array *)
+ (gtf_address + ix * REGS_PER_GTF);
+
+ /* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */
+ taskfile_load_raw(ap, atadev, gtf);
+ }
+
+ err = 0;
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(do_drive_set_taskfiles);
+
+/**
+ * ata_acpi_exec_tfs - get then write drive taskfile settings
+ * @ap: the ata_port for the drive
+ *
+ * This applies to both PATA and SATA drives.
+ */
+int ata_acpi_exec_tfs(struct ata_port *ap)
+{
+ int ix;
+ int ret;
+ unsigned int gtf_length;
+ unsigned long gtf_address;
+ unsigned long obj_loc;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ENTER:\n", __FUNCTION__);
+
+ if (noacpi)
+ return 0;
+
+ for (ix = 0; ix < ATA_MAX_DEVICES; ix++) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: call get_GTF, ix=%d\n",
+ __FUNCTION__, ix);
+ ret = do_drive_get_GTF(ap, ix,
+ >f_length, >f_address, &obj_loc);
+ if (ret < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: get_GTF error (%d)\n",
+ __FUNCTION__, ret);
+ break;
+ }
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: call set_taskfiles, ix=%d\n",
+ __FUNCTION__, ix);
+ ret = do_drive_set_taskfiles(ap, &ap->device[ix],
+ gtf_length, gtf_address);
+ acpi_os_free((void *)obj_loc);
+ if (ret < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: set_taskfiles error (%d)\n",
+ __FUNCTION__, ret);
+ break;
+ }
+ }
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ret=%d\n", __FUNCTION__, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ata_acpi_exec_tfs);
+
+/**
+ * ata_acpi_get_timing - get the channel (controller) timings
+ * @ap: target ata_port (channel)
+ *
+ * For PATA ACPI, this function executes the _GTM ACPI method for the
+ * target channel.
+ *
+ * _GTM only applies to ATA controllers in PATA (legacy) mode, not to SATA.
+ * In legacy mode, ap->hard_port_no is channel (controller) number.
+ */
+void ata_acpi_get_timing(struct ata_port *ap)
+{
+ struct device *dev = ap->dev;
+ int err;
+ acpi_handle dev_handle;
+ acpi_integer pcidevfn;
+ acpi_handle chan_handle;
+ acpi_status status;
+ struct acpi_buffer output;
+ union acpi_object *out_obj;
+ struct GTM_buffer *gtm;
+
+ if (noacpi)
+ goto out;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ENTER:\n", __FUNCTION__);
+
+ if (!ap->legacy_mode) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: channel/controller not in legacy mode (%s)\n",
+ __FUNCTION__, dev->bus_id);
+ goto out;
+ }
+
+ err = pata_get_dev_handle(dev, &dev_handle, &pcidevfn);
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: pata_get_dev_handle failed (%d)\n",
+ __FUNCTION__, err);
+ goto out;
+ }
+
+ /* get child objects of dev_handle == channel objects,
+ * + _their_ children == drive objects */
+ /* channel is ap->hard_port_no */
+ chan_handle = acpi_get_child(dev_handle, ap->hard_port_no);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: chan adr=%d: handle=0x%p\n",
+ __FUNCTION__, ap->hard_port_no, chan_handle);
+ if (!chan_handle)
+ goto out;
+
+ /* Setting up output buffer for _GTM */
+ output.length = ACPI_ALLOCATE_BUFFER;
+ output.pointer = NULL; /* ACPI-CA sets this; save/free it later */
+
+ /* _GTM has no input parameters */
+ status = acpi_evaluate_object(chan_handle, "_GTM",
+ NULL, &output);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: _GTM status: %d, outptr: 0x%p, outlen: 0x%llx\n",
+ __FUNCTION__, status, output.pointer,
+ (unsigned long long)output.length);
+ if (ACPI_FAILURE(status)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: Run _GTM error: status = 0x%x\n",
+ __FUNCTION__, status);
+ goto out;
+ }
+
+ if (!output.length || !output.pointer) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: Run _GTM: "
+ "length or ptr is NULL (0x%llx, 0x%p)\n",
+ __FUNCTION__,
+ (unsigned long long)output.length,
+ output.pointer);
+ acpi_os_free(output.pointer);
+ goto out;
+ }
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
+ acpi_os_free(output.pointer);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: Run _GTM: error: "
+ "expected object type of ACPI_TYPE_BUFFER, "
+ "got 0x%x\n",
+ __FUNCTION__, out_obj->type);
+ goto out;
+ }
+
+ if (!out_obj->buffer.length || !out_obj->buffer.pointer ||
+ out_obj->buffer.length != sizeof(struct GTM_buffer)) {
+ acpi_os_free(output.pointer);
+ if (ata_msg_drv(ap))
+ printk(KERN_ERR
+ "%s: unexpected _GTM length (0x%x)[should be 0x%x] or addr (0x%p)\n",
+ __FUNCTION__, out_obj->buffer.length,
+ sizeof(struct GTM_buffer), out_obj->buffer.pointer);
+ goto out;
+ }
+
+ gtm = (struct GTM_buffer *)out_obj->buffer.pointer;
+ if (ata_msg_probe(ap)) {
+ printk(KERN_DEBUG "%s: _GTM info: ptr: 0x%p, len: 0x%x, exp.len: 0x%Zx\n",
+ __FUNCTION__, out_obj->buffer.pointer,
+ out_obj->buffer.length, sizeof(struct GTM_buffer));
+ printk(KERN_DEBUG "%s: _GTM fields: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+ __FUNCTION__, gtm->PIO_speed0, gtm->DMA_speed0,
+ gtm->PIO_speed1, gtm->DMA_speed1, gtm->GTM_flags);
+ }
+
+ /* TBD: when to free gtm */
+ ap->gtm = gtm;
+ kfree(ap->gtm_object_area); /* free previous then store new one */
+ ap->gtm_object_area = out_obj;
+out:;
+}
+EXPORT_SYMBOL_GPL(ata_acpi_get_timing);
+
+/**
+ * platform_set_timing - set the channel (controller) timings
+ * @ap: target ata_port (channel)
+ *
+ * For PATA ACPI, this function executes the _STM ACPI method for the
+ * target channel.
+ *
+ * _STM only applies to ATA controllers in PATA (legacy) mode, not to SATA.
+ * In legacy mode, ap->hard_port_no is channel (controller) number.
+ *
+ * _STM requires Identify Drive data, which must already be present in
+ * ata_device->id[] (i.e., it's not fetched here).
+ */
+void ata_acpi_push_timing(struct ata_port *ap)
+{
+ struct device *dev = ap->dev;
+ int err;
+ acpi_handle dev_handle;
+ acpi_integer pcidevfn;
+ acpi_handle chan_handle;
+ acpi_status status;
+ struct acpi_object_list input;
+ union acpi_object in_params[1];
+
+ if (noacpi)
+ goto out;
+
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: ENTER:\n", __FUNCTION__);
+
+ if (!ap->legacy_mode) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: channel/controller not in legacy mode (%s)\n",
+ __FUNCTION__, dev->bus_id);
+ goto out;
+ }
+
+ if (ap->device[0].id[49] || ap->device[1].id[49]) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: drive(s) on channel %d: missing Identify data\n",
+ __FUNCTION__, ap->hard_port_no);
+ goto out;
+ }
+
+ err = pata_get_dev_handle(dev, &dev_handle, &pcidevfn);
+ if (err < 0) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: pata_get_dev_handle failed (%d)\n",
+ __FUNCTION__, err);
+ goto out;
+ }
+
+ /* get child objects of dev_handle == channel objects,
+ * + _their_ children == drive objects */
+ /* channel is ap->hard_port_no */
+ chan_handle = acpi_get_child(dev_handle, ap->hard_port_no);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: chan adr=%d: handle=0x%p\n",
+ __FUNCTION__, ap->hard_port_no, chan_handle);
+ if (!chan_handle)
+ goto out;
+
+ /* Give the GTM buffer + drive Identify data to the channel via the
+ * _STM method: */
+ /* setup input parameters buffer for _STM */
+ input.count = 3;
+ input.pointer = in_params;
+ in_params[0].type = ACPI_TYPE_BUFFER;
+ in_params[0].buffer.length = sizeof(struct GTM_buffer);
+ in_params[0].buffer.pointer = (u8 *)ap->gtm;
+ in_params[1].type = ACPI_TYPE_BUFFER;
+ in_params[1].buffer.length = sizeof(ap->device[0].id);
+ in_params[1].buffer.pointer = (u8 *)ap->device[0].id;
+ in_params[2].type = ACPI_TYPE_BUFFER;
+ in_params[2].buffer.length = sizeof(ap->device[1].id);
+ in_params[2].buffer.pointer = (u8 *)ap->device[1].id;
+ /* Output buffer: _STM has no output */
+
+ swap_buf_le16(ap->device[0].id, ATA_ID_WORDS);
+ swap_buf_le16(ap->device[1].id, ATA_ID_WORDS);
+ status = acpi_evaluate_object(chan_handle, "_STM", &input, NULL);
+ swap_buf_le16(ap->device[0].id, ATA_ID_WORDS);
+ swap_buf_le16(ap->device[1].id, ATA_ID_WORDS);
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG "%s: _STM status: %d\n",
+ __FUNCTION__, status);
+ if (ACPI_FAILURE(status)) {
+ if (ata_msg_probe(ap))
+ printk(KERN_DEBUG
+ "%s: Run _STM error: status = 0x%x\n",
+ __FUNCTION__, status);
+ goto out;
+ }
+
+out:;
+}
+EXPORT_SYMBOL_GPL(ata_acpi_push_timing);
diff -urN oldtree/drivers/scsi/libata-core.c newtree/drivers/scsi/libata-core.c
--- oldtree/drivers/scsi/libata-core.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/scsi/libata-core.c 2006-03-27 15:57:04.293993500 -0500
@@ -79,6 +79,14 @@
module_param_named(fua, libata_fua, int, 0444);
MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)");
+int noacpi = 0;
+module_param(noacpi, int, 0444);
+MODULE_PARM_DESC(noacpi, "Disables use of ACPI in suspend/resume when set");
+
+int libata_printk = ATA_MSG_DRV;
+module_param_named(printk, libata_printk, int, 0644);
+MODULE_PARM_DESC(printk, "Set libata printk flags"); /* in linux/libata.h */
+
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Library module for ATA devices");
MODULE_LICENSE("GPL");
@@ -929,7 +937,7 @@
* None. Should be called with kernel context, might sleep.
*/
-static unsigned
+unsigned int
ata_exec_internal(struct ata_port *ap, struct ata_device *dev,
struct ata_taskfile *tf,
int dma_dir, void *buf, unsigned int buflen)
@@ -1405,6 +1413,8 @@
if (ap->flags & ATA_FLAG_PORT_DISABLED)
goto err_out_disable;
+ ata_acpi_exec_tfs(ap);
+
return 0;
err_out_disable:
@@ -4465,6 +4475,7 @@
}
if (!ata_dev_present(dev))
return 0;
+ ata_acpi_exec_tfs(ap);
if (dev->class == ATA_DEV_ATA)
ata_start_drive(ap, dev);
@@ -4504,6 +4515,27 @@
* Inherited from caller.
*/
+/**
+ * ata_device_shutdown - send Standby Immediate command to drive
+ * @ap: target ata_port
+ * @dev: target device on the ata_port
+ *
+ * This command makes it safe to power-off a drive.
+ * Otherwise the heads may be flying at the wrong place
+ * when the power is removed.
+ */
+int ata_device_shutdown(struct ata_port *ap, struct ata_device *dev)
+{
+
+ if (!ata_dev_present(dev))
+ return 0;
+
+ ata_standby_drive(ap, dev);
+ ap->flags |= ATA_FLAG_SUSPENDED;
+
+ return 0;
+}
+
int ata_port_start (struct ata_port *ap)
{
struct device *dev = ap->host_set->dev;
@@ -4608,6 +4640,7 @@
ap->port_no = port_no;
ap->hard_port_no =
ent->legacy_mode ? ent->hard_port_no : port_no;
+ ap->legacy_mode = ent->legacy_mode;
ap->pio_mask = ent->pio_mask;
ap->mwdma_mask = ent->mwdma_mask;
ap->udma_mask = ent->udma_mask;
@@ -4617,6 +4650,7 @@
ap->cbl = ATA_CBL_NONE;
ap->active_tag = ATA_TAG_POISON;
ap->last_ctl = 0xFF;
+ ap->dev = ent->dev;
INIT_WORK(&ap->port_task, NULL, NULL);
INIT_LIST_HEAD(&ap->eh_done_q);
@@ -4731,10 +4765,13 @@
(ap->mwdma_mask << ATA_SHIFT_MWDMA) |
(ap->pio_mask << ATA_SHIFT_PIO);
+ ap->msg_enable = libata_printk;
+
/* print per-port info to dmesg */
printk(KERN_INFO "ata%u: %cATA max %s cmd 0x%lX ctl 0x%lX "
"bmdma 0x%lX irq %lu\n",
ap->id,
+ ap->flags & ATA_FLAG_PATA_MODE ? 'P' :
ap->flags & ATA_FLAG_SATA ? 'S' : 'P',
ata_mode_string(xfer_mode_mask),
ap->ioaddr.cmd_addr,
@@ -4796,6 +4833,12 @@
ata_scsi_scan_host(ap);
}
+ for (i = 0; i < ent->n_ports; i++) {
+ struct ata_port *ap = host_set->ports[i];
+
+ ata_acpi_get_timing(ap);
+ }
+
dev_set_drvdata(dev, host_set);
VPRINTK("EXIT, returning %u\n", ent->n_ports);
@@ -5121,5 +5164,7 @@
EXPORT_SYMBOL_GPL(ata_device_suspend);
EXPORT_SYMBOL_GPL(ata_device_resume);
+EXPORT_SYMBOL_GPL(ata_device_shutdown);
EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
+EXPORT_SYMBOL_GPL(ata_scsi_device_shutdown);
diff -urN oldtree/drivers/scsi/libata-scsi.c newtree/drivers/scsi/libata-scsi.c
--- oldtree/drivers/scsi/libata-scsi.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/scsi/libata-scsi.c 2006-03-27 15:57:04.313994750 -0500
@@ -422,6 +422,14 @@
return ata_device_suspend(ap, dev);
}
+int ata_scsi_device_shutdown(struct scsi_device *sdev)
+{
+ struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+ struct ata_device *dev = &ap->device[sdev->id];
+
+ return ata_device_shutdown(ap, dev);
+}
+
/**
* ata_to_sense_error - convert ATA error to SCSI error
* @id: ATA device number
diff -urN oldtree/drivers/scsi/libata.h newtree/drivers/scsi/libata.h
--- oldtree/drivers/scsi/libata.h 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/scsi/libata.h 2006-03-27 15:57:04.305994250 -0500
@@ -41,6 +41,8 @@
/* libata-core.c */
extern int atapi_enabled;
+extern int noacpi;
+extern int libata_printk;
extern int libata_fua;
extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
struct ata_device *dev);
@@ -54,6 +56,51 @@
extern void swap_buf_le16(u16 *buf, unsigned int buf_words);
extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
+extern unsigned int ata_exec_internal(struct ata_port *ap,
+ struct ata_device *dev,
+ struct ata_taskfile *tf,
+ int dma_dir, void *buf, unsigned int buflen);
+
+
+/* libata-acpi.c */
+#ifdef CONFIG_SCSI_SATA_ACPI
+extern int ata_acpi_push_id(struct ata_port *ap, unsigned int ix);
+extern int do_drive_get_GTF(struct ata_port *ap, int ix,
+ unsigned int *gtf_length, unsigned long *gtf_address,
+ unsigned long *obj_loc);
+extern int do_drive_set_taskfiles(struct ata_port *ap, struct ata_device *atadev,
+ unsigned int gtf_length, unsigned long gtf_address);
+extern int ata_acpi_exec_tfs(struct ata_port *ap);
+extern void ata_acpi_get_timing(struct ata_port *ap);
+extern void ata_acpi_push_timing(struct ata_port *ap);
+#else
+static inline int ata_acpi_push_id(struct ata_port *ap, unsigned int ix)
+{
+ return 0;
+}
+static inline int do_drive_get_GTF(struct ata_port *ap, int ix,
+ unsigned int *gtf_length, unsigned long *gtf_address,
+ unsigned long *obj_loc)
+{
+ return 0;
+}
+static inline int do_drive_set_taskfiles(struct ata_port *ap,
+ struct ata_device *atadev,
+ unsigned int gtf_length, unsigned long gtf_address)
+{
+ return 0;
+}
+static inline int ata_acpi_exec_tfs(struct ata_port *ap)
+{
+ return 0;
+}
+static void ata_acpi_get_timing(struct ata_port *ap)
+{
+}
+static void ata_acpi_push_timing(struct ata_port *ap)
+{
+}
+#endif
/* libata-scsi.c */
diff -urN oldtree/drivers/scsi/lpfc/lpfc_init.c newtree/drivers/scsi/lpfc/lpfc_init.c
--- oldtree/drivers/scsi/lpfc/lpfc_init.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/scsi/lpfc/lpfc_init.c 2006-03-27 15:30:38.286874250 -0500
@@ -1602,7 +1602,7 @@
phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING * 4));
/* Startup the kernel thread for this host adapter. */
- phba->worker_thread = kthread_run(lpfc_do_work, phba,
+ phba->worker_thread = kthread_nofreeze_run(lpfc_do_work, phba,
"lpfc_worker_%d", phba->brd_no);
if (IS_ERR(phba->worker_thread)) {
error = PTR_ERR(phba->worker_thread);
diff -urN oldtree/drivers/scsi/scsi_sysfs.c newtree/drivers/scsi/scsi_sysfs.c
--- oldtree/drivers/scsi/scsi_sysfs.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/scsi/scsi_sysfs.c 2006-03-27 15:57:04.329995750 -0500
@@ -305,11 +305,27 @@
return err;
}
+static void scsi_bus_shutdown(struct device * dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+ int err;
+
+ err = scsi_device_quiesce(sdev);
+ if (err)
+ printk(KERN_DEBUG "%s: error (0x%x) during shutdown\n",
+ __FUNCTION__, err);
+
+ if (sht->shutdown)
+ sht->shutdown(sdev);
+}
+
struct bus_type scsi_bus_type = {
.name = "scsi",
.match = scsi_bus_match,
.suspend = scsi_bus_suspend,
.resume = scsi_bus_resume,
+ .shutdown = scsi_bus_shutdown,
};
int scsi_sysfs_register(void)
diff -urN oldtree/drivers/usb/net/pegasus.c newtree/drivers/usb/net/pegasus.c
--- oldtree/drivers/usb/net/pegasus.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/drivers/usb/net/pegasus.c 2006-03-27 15:30:38.294874750 -0500
@@ -1452,7 +1452,7 @@
pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION);
if (devid)
parse_id(devid);
- pegasus_workqueue = create_singlethread_workqueue("pegasus");
+ pegasus_workqueue = create_nofreeze_singlethread_workqueue("pegasus");
if (!pegasus_workqueue)
return -ENOMEM;
return usb_register(&pegasus_driver);
diff -urN oldtree/include/asm-arm/hw_irq.h newtree/include/asm-arm/hw_irq.h
--- oldtree/include/asm-arm/hw_irq.h 1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/asm-arm/hw_irq.h 2006-03-27 16:04:08.388497750 -0500
@@ -0,0 +1,4 @@
+#ifndef __ASM_HARDIRQ_H
+#define __ASM_HARDIRQ_H
+#include
+#endif
diff -urN oldtree/include/asm-arm/suspend2.h newtree/include/asm-arm/suspend2.h
--- oldtree/include/asm-arm/suspend2.h 1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/asm-arm/suspend2.h 2006-03-27 16:04:08.392498000 -0500
@@ -0,0 +1,136 @@
+#ifndef _ASMARM_SUSPEND_H
+#define _ASMARM_SUSPEND_H
+/*
+ * Based on code
+ * Copyright 2005 Sony Corporation
+ * Copyright 2003-2004 Nigel Cunningham
+ * Copyright 2001-2002 Pavel Machek
+ * Copyright 2001 Patrick Mochel
+ */
+
+/* image of the saved processor state */
+struct suspend2_saved_context {
+ /* general registers */
+ __u32 r[15];
+
+ /* coprocessor 15 registers */
+/* __u32 ID_code; read only reg */
+/* __u32 cache_type; read only reg */
+/* __u32 TCM_stat; read only reg */
+ __u32 CR;
+ __u32 TTBR;
+ __u32 DACR;
+ __u32 D_FSR;
+ __u32 I_FSR;
+ __u32 FAR;
+/* __u32 COR; write only reg */
+/* __u32 TLBOR; write only reg */
+ __u32 D_CLR;
+ __u32 I_CLR;
+ __u32 D_TCMRR;
+ __u32 I_TCMRR;
+ __u32 TLBLR;
+ __u32 FCSE;
+ __u32 CID;
+} __attribute__((packed));
+typedef struct suspend2_saved_context suspend2_saved_context_t;
+
+/* temporary storage */
+extern struct suspend2_saved_context suspend2_saved_context;
+
+static inline void suspend2_arch_save_processor_context(void)
+{
+ /* save general registers */
+ asm volatile ("stmia %0, {r4-r14}"
+ :: "r" (suspend2_saved_context.r));
+ /* save coprocessor 15 registers */
+ asm volatile ("mrc p15, 0, %0, c1, c0, 0"
+ : "=r" (suspend2_saved_context.CR));
+ asm volatile ("mrc p15, 0, %0, c3, c0, 0"
+ : "=r" (suspend2_saved_context.DACR));
+ asm volatile ("mrc p15, 0, %0, c5, c0, 0"
+ : "=r" (suspend2_saved_context.D_FSR));
+ asm volatile ("mrc p15, 0, %0, c5, c0, 1"
+ : "=r" (suspend2_saved_context.I_FSR));
+ asm volatile ("mrc p15, 0, %0, c6, c0, 0"
+ : "=r" (suspend2_saved_context.FAR));
+ asm volatile ("mrc p15, 0, %0, c9, c0, 0"
+ : "=r" (suspend2_saved_context.D_CLR));
+ asm volatile ("mrc p15, 0, %0, c9, c0, 1"
+ : "=r" (suspend2_saved_context.I_CLR));
+ asm volatile ("mrc p15, 0, %0, c9, c1, 0"
+ : "=r" (suspend2_saved_context.D_TCMRR));
+ asm volatile ("mrc p15, 0, %0, c9, c1, 1"
+ : "=r" (suspend2_saved_context.I_TCMRR));
+ asm volatile ("mrc p15, 0, %0, c10, c0, 0"
+ : "=r" (suspend2_saved_context.TLBLR));
+ asm volatile ("mrc p15, 0, %0, c13, c0, 0"
+ : "=r" (suspend2_saved_context.FCSE));
+ asm volatile ("mrc p15, 0, %0, c13, c0, 1"
+ : "=r" (suspend2_saved_context.CID));
+ asm volatile ("mrc p15, 0, %0, c2, c0, 0"
+ : "=r" (suspend2_saved_context.TTBR));
+}
+
+static inline void suspend2_arch_restore_processor_context(void)
+{
+ /* restore coprocessor 15 registers */
+ asm volatile ("mcr p15, 0, %0, c2, c0, 0"
+ :: "r" (suspend2_saved_context.TTBR));
+ asm volatile ("mcr p15, 0, %0, c13, c0, 1"
+ :: "r" (suspend2_saved_context.CID));
+ asm volatile ("mcr p15, 0, %0, c13, c0, 0"
+ :: "r" (suspend2_saved_context.FCSE));
+ asm volatile ("mcr p15, 0, %0, c10, c0, 0"
+ :: "r" (suspend2_saved_context.TLBLR));
+ asm volatile ("mcr p15, 0, %0, c9, c1, 1"
+ :: "r" (suspend2_saved_context.I_TCMRR));
+ asm volatile ("mcr p15, 0, %0, c9, c1, 0"
+ :: "r" (suspend2_saved_context.D_TCMRR));
+ asm volatile ("mcr p15, 0, %0, c9, c0, 1"
+ :: "r" (suspend2_saved_context.I_CLR));
+ asm volatile ("mcr p15, 0, %0, c9, c0, 0"
+ :: "r" (suspend2_saved_context.D_CLR));
+ asm volatile ("mcr p15, 0, %0, c6, c0, 0"
+ :: "r" (suspend2_saved_context.FAR));
+ asm volatile ("mcr p15, 0, %0, c5, c0, 1"
+ :: "r" (suspend2_saved_context.I_FSR));
+ asm volatile ("mcr p15, 0, %0, c5, c0, 0"
+ :: "r" (suspend2_saved_context.D_FSR));
+ asm volatile ("mcr p15, 0, %0, c3, c0, 0"
+ :: "r" (suspend2_saved_context.DACR));
+ asm volatile ("mcr p15, 0, %0, c1, c0, 0"
+ :: "r" (suspend2_saved_context.CR));
+
+ /* restore general registers */
+ asm volatile ("ldmia r3, {r4-r14}" : "=m" (suspend2_saved_context.r));
+}
+
+static inline void save_context(void)
+{
+}
+
+static inline void restore_context(void)
+{
+}
+
+static inline void suspend2_arch_pre_copy(void)
+{
+}
+
+static inline void suspend2_arch_post_copy(void)
+{
+}
+
+static inline void suspend2_arch_pre_copyback(void)
+{
+}
+
+static inline void suspend2_arch_post_copyback(void)
+{
+}
+
+static inline void suspend2_arch_flush_caches(void)
+{
+}
+#endif
diff -urN oldtree/include/asm-i386/suspend2.h newtree/include/asm-i386/suspend2.h
--- oldtree/include/asm-i386/suspend2.h 1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/asm-i386/suspend2.h 2006-03-27 16:04:08.396498250 -0500
@@ -0,0 +1,288 @@
+ /*
+ * Copyright 2003-2005 Nigel Cunningham
+ * Based on code
+ * Copyright 2001-2002 Pavel Machek
+ * Based on code
+ * Copyright 2001 Patrick Mochel
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* image of the saved processor states */
+struct suspend2_saved_context {
+ u32 eax, ebx, ecx, edx;
+ u32 esp, ebp, esi, edi;
+ u16 es, fs, gs, ss;
+ u32 cr0, cr2, cr3, cr4;
+ u16 gdt_pad;
+ u16 gdt_limit;
+ u32 gdt_base;
+ u16 idt_pad;
+ u16 idt_limit;
+ u32 idt_base;
+ u16 ldt;
+ u16 tss;
+ u32 tr;
+ u32 safety;
+ u32 return_address;
+ u32 eflags;
+} __attribute__((packed));
+typedef struct suspend2_saved_context suspend2_saved_context_t;
+
+/* temporary storage */
+extern struct suspend2_saved_context suspend2_saved_context;
+
+/*
+ * save_processor_context
+ *
+ * Save the state of the processor before we go to sleep.
+ *
+ * return_stack is the value of the stack pointer (%esp) as the caller sees it.
+ * A good way could not be found to obtain it from here (don't want to make
+ * _too_ many assumptions about the layout of the stack this far down.) Also,
+ * the handy little __builtin_frame_pointer(level) where level > 0, is blatantly
+ * buggy - it returns the value of the stack at the proper location, not the
+ * location, like it should (as of gcc 2.91.66)
+ *
+ * Note that the context and timing of this function is pretty critical.
+ * With a minimal amount of things going on in the caller and in here, gcc
+ * does a good job of being just a dumb compiler. Watch the assembly output
+ * if anything changes, though, and make sure everything is going in the right
+ * place.
+ */
+static inline void suspend2_arch_save_processor_context(void)
+{
+ kernel_fpu_begin();
+
+ /*
+ * descriptor tables
+ */
+ asm volatile ("sgdt (%0)" : "=m" (suspend2_saved_context.gdt_limit));
+ asm volatile ("sidt (%0)" : "=m" (suspend2_saved_context.idt_limit));
+ asm volatile ("sldt (%0)" : "=m" (suspend2_saved_context.ldt));
+ asm volatile ("str (%0)" : "=m" (suspend2_saved_context.tr));
+
+ /*
+ * save the general registers.
+ * note that gcc has constructs to specify output of certain registers,
+ * but they're not used here, because it assumes that you want to modify
+ * those registers, so it tries to be smart and save them beforehand.
+ * It's really not necessary, and kinda fishy (check the assembly output),
+ * so it's avoided.
+ */
+ asm volatile ("movl %%esp, (%0)" : "=m" (suspend2_saved_context.esp));
+ asm volatile ("movl %%eax, (%0)" : "=m" (suspend2_saved_context.eax));
+ asm volatile ("movl %%ebx, (%0)" : "=m" (suspend2_saved_context.ebx));
+ asm volatile ("movl %%ecx, (%0)" : "=m" (suspend2_saved_context.ecx));
+ asm volatile ("movl %%edx, (%0)" : "=m" (suspend2_saved_context.edx));
+ asm volatile ("movl %%ebp, (%0)" : "=m" (suspend2_saved_context.ebp));
+ asm volatile ("movl %%esi, (%0)" : "=m" (suspend2_saved_context.esi));
+ asm volatile ("movl %%edi, (%0)" : "=m" (suspend2_saved_context.edi));
+
+ /*
+ * segment registers
+ */
+ asm volatile ("movw %%es, %0" : "=r" (suspend2_saved_context.es));
+ asm volatile ("movw %%fs, %0" : "=r" (suspend2_saved_context.fs));
+ asm volatile ("movw %%gs, %0" : "=r" (suspend2_saved_context.gs));
+ asm volatile ("movw %%ss, %0" : "=r" (suspend2_saved_context.ss));
+
+ /*
+ * control registers
+ */
+ asm volatile ("movl %%cr0, %0" : "=r" (suspend2_saved_context.cr0));
+ asm volatile ("movl %%cr2, %0" : "=r" (suspend2_saved_context.cr2));
+ asm volatile ("movl %%cr3, %0" : "=r" (suspend2_saved_context.cr3));
+ asm volatile ("movl %%cr4, %0" : "=r" (suspend2_saved_context.cr4));
+
+ /*
+ * eflags
+ */
+ asm volatile ("pushfl ; popl (%0)" : "=m" (suspend2_saved_context.eflags));
+}
+
+static void fix_processor_context(void)
+{
+ struct tss_struct *t = &per_cpu(init_tss,0);
+
+ /* This just modifies memory; should not be neccessary. But... This is
+ * neccessary, because 386 hardware has concept of busy tsc or some
+ * similar stupidity. */
+ set_tss_desc(0,t);
+ get_cpu_gdt_table(0)[GDT_ENTRY_TSS].b &= 0xfffffdff;
+
+ load_TR_desc();
+
+ load_LDT(¤t->active_mm->context); /* This does lldt */
+
+ /*
+ * Now maybe reload the debug registers
+ */
+ if (current->thread.debugreg[7]){
+ set_debugreg(¤t->thread.debugreg[0], 0);
+ set_debugreg(¤t->thread.debugreg[1], 1);
+ set_debugreg(¤t->thread.debugreg[2], 2);
+ set_debugreg(¤t->thread.debugreg[3], 3);
+ /* no 4 and 5 */
+ set_debugreg(¤t->thread.debugreg[6], 6);
+ set_debugreg(¤t->thread.debugreg[7], 7);
+ }
+
+}
+
+static void do_fpu_end(void)
+{
+ /* restore FPU regs if necessary */
+ /* Do it out of line so that gcc does not move cr0 load to some stupid
+ * place */
+ kernel_fpu_end();
+}
+
+#if defined(CONFIG_SUSPEND2) || defined(CONFIG_SMP)
+static unsigned long c_loops_per_jiffy_ref __nosavedata;
+#endif
+
+#ifdef CONFIG_SUSPEND2
+#ifndef CONFIG_SMP
+extern unsigned long loops_per_jiffy;
+volatile static unsigned long cpu_khz_ref __nosavedata = 0;
+#endif
+
+static inline void suspend2_arch_pre_copy(void) { }
+static inline void suspend2_arch_post_copy(void) { }
+
+static inline void suspend2_arch_pre_copyback(void)
+{
+ /* We want to run from swsusp_pg_dir, since swsusp_pg_dir is stored in
+ * constant place in memory.
+ */
+
+ __asm__( "movl %%ecx,%%cr3\n" ::"c"(__pa(swsusp_pg_dir)));
+
+ c_loops_per_jiffy_ref =
+ current_cpu_data.loops_per_jiffy;
+#ifndef CONFIG_SMP
+ cpu_khz_ref = cpu_khz;
+ c_loops_per_jiffy_ref = loops_per_jiffy;
+#endif
+
+}
+
+/*
+ * restore_processor_context
+ *
+ * Restore the processor context as it was before we went to sleep
+ * - descriptor tables
+ * - control registers
+ * - segment registers
+ * - flags
+ *
+ * Note that it is critical that this function is declared inline.
+ * It was separated out from restore_state to make that function
+ * a little clearer, but it needs to be inlined because we won't have a
+ * stack when we get here (so we can't push a return address).
+ */
+static inline void suspend2_arch_restore_processor_context(void)
+{
+ /*
+ * first restore %ds, so we can access our data properly
+ */
+ asm volatile (".align 4");
+ asm volatile ("movw %0, %%ds" :: "r" ((u16)__KERNEL_DS));
+
+
+ /*
+ * control registers
+ */
+ asm volatile ("movl %0, %%cr4" :: "r" (suspend2_saved_context.cr4));
+ asm volatile ("movl %0, %%cr3" :: "r" (suspend2_saved_context.cr3));
+ asm volatile ("movl %0, %%cr2" :: "r" (suspend2_saved_context.cr2));
+ asm volatile ("movl %0, %%cr0" :: "r" (suspend2_saved_context.cr0));
+
+ /*
+ * segment registers
+ */
+ asm volatile ("movw %0, %%es" :: "r" (suspend2_saved_context.es));
+ asm volatile ("movw %0, %%fs" :: "r" (suspend2_saved_context.fs));
+ asm volatile ("movw %0, %%gs" :: "r" (suspend2_saved_context.gs));
+ asm volatile ("movw %0, %%ss" :: "r" (suspend2_saved_context.ss));
+
+ /*
+ * the other general registers
+ *
+ * note that even though gcc has constructs to specify memory
+ * input into certain registers, it will try to be too smart
+ * and save them at the beginning of the function. This is esp.
+ * bad since we don't have a stack set up when we enter, and we
+ * want to preserve the values on exit. So, we set them manually.
+ */
+ asm volatile ("movl %0, %%esp" :: "m" (suspend2_saved_context.esp));
+ asm volatile ("movl %0, %%ebp" :: "m" (suspend2_saved_context.ebp));
+ asm volatile ("movl %0, %%eax" :: "m" (suspend2_saved_context.eax));
+ asm volatile ("movl %0, %%ebx" :: "m" (suspend2_saved_context.ebx));
+ asm volatile ("movl %0, %%ecx" :: "m" (suspend2_saved_context.ecx));
+ asm volatile ("movl %0, %%edx" :: "m" (suspend2_saved_context.edx));
+ asm volatile ("movl %0, %%esi" :: "m" (suspend2_saved_context.esi));
+ asm volatile ("movl %0, %%edi" :: "m" (suspend2_saved_context.edi));
+
+ /*
+ * now restore the descriptor tables to their proper values
+ * ltr is done in fix_processor_context().
+ */
+
+ asm volatile ("lgdt (%0)" :: "m" (suspend2_saved_context.gdt_limit));
+ asm volatile ("lidt (%0)" :: "m" (suspend2_saved_context.idt_limit));
+ asm volatile ("lldt (%0)" :: "m" (suspend2_saved_context.ldt));
+
+ /* tell gcc that we clobbered all the registers...
+ * otherwise it might keep some addresses there.
+ * Unfortunately gcc 4 thinks it's smart and will
+ * error out if we tell it we're clobbering ebp as
+ * well. So we have to lie.
+ */
+ asm volatile ("" : : : "esp", "eax", "ebx", "ecx", "edx", "esi", "edi");
+
+ if (boot_cpu_has(X86_FEATURE_SEP))
+ enable_sep_cpu();
+
+ fix_processor_context();
+
+ /*
+ * the flags
+ */
+ asm volatile ("pushl %0 ; popfl" :: "m" (suspend2_saved_context.eflags));
+
+ do_fpu_end();
+
+ mtrr_ap_init();
+ mcheck_init(&boot_cpu_data);
+}
+
+static inline void suspend2_arch_flush_caches(void)
+{
+#ifdef CONFIG_SMP
+ cpu_clear(0, per_cpu(cpu_tlbstate,
+ 0).active_mm->cpu_vm_mask);
+#endif
+ wbinvd();
+ __flush_tlb_all();
+
+}
+
+static inline void suspend2_arch_post_copyback(void)
+{
+ BUG_ON(!irqs_disabled());
+
+ current_cpu_data.loops_per_jiffy =
+ c_loops_per_jiffy_ref;
+#ifndef CONFIG_SMP
+ loops_per_jiffy = c_loops_per_jiffy_ref;
+ cpu_khz = cpu_khz_ref;
+#endif
+}
+
+#endif
diff -urN oldtree/include/asm-ppc/cpu_context.h newtree/include/asm-ppc/cpu_context.h
--- oldtree/include/asm-ppc/cpu_context.h 1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/asm-ppc/cpu_context.h 2006-03-27 16:04:08.400498500 -0500
@@ -0,0 +1,110 @@
+/*
+ * Written by Hu Gang (hugang@soulinfo.com)
+ *
+ * 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.
+ */
+
+#include
+#include
+#include
+
+/* image of the saved processor states */
+struct saved_context {
+ u32 lr, cr, sp, r2;
+ u32 r[20]; /* r12 - r31 */
+ u32 sprg[4];
+ u32 msr, sdr1, tb1, tb2;
+} __attribute__((packed));
+
+inline static void __save_processor_state(struct saved_context *s)
+{
+ /*asm volatile ("mflr 0; stw 0,%0" : "=m" (s->lr));*/
+ asm volatile ("mfcr 0; stw 0,%0" : "=m" (s->cr));
+ asm volatile ("stw 1,%0" : "=m" (s->sp));
+ asm volatile ("stw 2,%0" : "=m" (s->r2));
+ asm volatile ("stmw 12,%0" : "=m" (s->r));
+
+ /* Save MSR & SDR1 */
+ asm volatile ("mfmsr 4; stw 4,%0" : "=m" (s->msr));
+ asm volatile ("mfsdr1 4; stw 4,%0": "=m" (s->sdr1));
+
+ /* Get a stable timebase and save it */
+ asm volatile ("1:\n"
+ "mftbu 4;stw 4,%0\n"
+ "mftb 5;stw 5,%1\n"
+ "mftbu 3\n"
+ "cmpw 3,4;\n"
+ "bne 1b" :
+ "=m" (s->tb1),
+ "=m" (s->tb2));
+
+ /* Save SPRGs */
+ asm volatile ("mfsprg 4,0; stw 4,%0 " : "=m" (s->sprg[0]));
+ asm volatile ("mfsprg 4,1; stw 4,%0 " : "=m" (s->sprg[1]));
+ asm volatile ("mfsprg 4,2; stw 4,%0 " : "=m" (s->sprg[2]));
+ asm volatile ("mfsprg 4,3; stw 4,%0 " : "=m" (s->sprg[3]));
+}
+
+inline static void __restore_processor_state(struct saved_context *s)
+{
+ /* Restore the BATs, and SDR1 */
+ asm volatile ("lwz 4,%0; mtsdr1 4" : "=m" (s->sdr1));
+ /* asm volatile ("lwz 3,%0" : "=m" (saved_context.msr)); */
+
+ asm volatile ("lwz 4,%0; mtsprg 0,4": "=m" (s->sprg[0]));
+ asm volatile ("lwz 4,%0; mtsprg 1,4": "=m" (s->sprg[1]));
+ asm volatile ("lwz 4,%0; mtsprg 2,4": "=m" (s->sprg[2]));
+ asm volatile ("lwz 4,%0; mtsprg 3,4": "=m" (s->sprg[3]));
+
+ /* Restore TB */
+ asm volatile ("li 3,0; mttbl 3; \n"
+ "lwz 3,%0\n; lwz 4,%1\n"
+ "mttbu 3; mttbl 4" :
+ "=m" (s->tb1),
+ "=m" (s->tb2));
+
+ /* Restore the callee-saved registers and return */
+ asm volatile ("lmw 12,%0" : "=m" (s->r));
+ asm volatile ("lwz 2,%0" : "=m" (s->r2));
+ asm volatile ("lwz 1,%0" : "=m" (s->sp));
+ asm volatile ("lwz 0,%0; mtcr 0" : "=m" (s->cr));
+
+ /* tell gcc that we clobbered all the registers...
+ * otherwise it might keep some addresses there. */
+ asm volatile ("" : : : "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31");
+ /*asm volatile ("lwz 0,%0; mtlr 0" : "=m" (s->lr));*/
+}
+
+static inline void save_context(void)
+{
+#ifdef CONFIG_ADB_PMU
+ printk("pmu suspend\n");
+ pmu_suspend();
+#endif
+}
+
+extern void enable_kernel_altivec(void);
+
+static inline void restore_context(void)
+{
+ printk("set context: <%p>\n", current);
+ set_context(current->active_mm->context,
+ current->active_mm->pgd);
+
+#ifdef CONFIG_ADB_PMU
+ printk("pmu_resume\n");
+ pmu_resume();
+#endif
+
+#ifdef CONFIG_ALTIVEC
+ if (cur_cpu_spec->cpu_features & CPU_FTR_ALTIVEC) {
+ printk("enable altivec\n");
+ enable_kernel_altivec();
+ }
+#endif
+ printk("enable fp\n");
+ enable_kernel_fp();
+}
diff -urN oldtree/include/asm-ppc/suspend2.h newtree/include/asm-ppc/suspend2.h
--- oldtree/include/asm-ppc/suspend2.h 1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/asm-ppc/suspend2.h 2006-03-27 16:04:08.400498500 -0500
@@ -0,0 +1,47 @@
+/*
+ * Written by Hu Gang (hugang@soulinfo.com)
+ *
+ * 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.
+ */
+
+#include "asm/cpu_context.h"
+
+typedef struct saved_context suspend2_saved_context_t;
+
+extern struct saved_context suspend2_saved_context;
+
+static inline void suspend2_arch_save_processor_context(void)
+{
+ __save_processor_state(&suspend2_saved_context);
+}
+
+static inline void suspend2_arch_restore_processor_context(void)
+{
+ __restore_processor_state(&suspend2_saved_context);
+
+ restore_context();
+}
+
+static inline void suspend2_arch_pre_copy(void)
+{
+}
+
+static inline void suspend2_arch_post_copy(void)
+{
+}
+
+static inline void suspend2_arch_pre_copyback(void)
+{
+ save_context();
+}
+
+static inline void suspend2_arch_post_copyback(void)
+{
+}
+
+static inline void suspend2_arch_flush_caches(void)
+{
+}
diff -urN oldtree/include/asm-x86_64/page.h newtree/include/asm-x86_64/page.h
--- oldtree/include/asm-x86_64/page.h 2006-03-27 13:28:15.000000000 -0500
+++ newtree/include/asm-x86_64/page.h 2006-03-27 16:04:08.404498750 -0500
@@ -105,6 +105,8 @@
#include
+extern int page_is_ram(unsigned long pagenr);
+
#endif /* __ASSEMBLY__ */
#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)
diff -urN oldtree/include/asm-x86_64/suspend2.h newtree/include/asm-x86_64/suspend2.h
--- oldtree/include/asm-x86_64/suspend2.h 1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/asm-x86_64/suspend2.h 2006-03-27 16:04:08.408499000 -0500
@@ -0,0 +1,399 @@
+ /*
+ * Copyright 2005 Nigel Cunningham
+ * Based on code
+ * Copyright 2001-2002 Pavel Machek
+ * Based on code
+ * Copyright 2001 Patrick Mochel
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static pgd_t *temp_level4_pgt;
+extern int suspend2_mapping_prepare(void);
+
+/* image of the saved processor states */
+struct suspend2_saved_context {
+ unsigned long eax, ebx, ecx, edx;
+ unsigned long esp, ebp, esi, edi;
+ unsigned long r8, r9, r10, r11;
+ unsigned long r12, r13, r14, r15;
+
+ u16 ds, es, fs, gs, ss;
+ unsigned long gs_base, gs_kernel_base, fs_base;
+ unsigned long cr0, cr2, cr3, cr4, cr8;
+ u16 gdt_pad;
+ u16 gdt_limit;
+ unsigned long gdt_base;
+ u16 idt_pad;
+ u16 idt_limit;
+ unsigned long idt_base;
+ u16 ldt;
+ u16 tss;
+ unsigned long tr;
+ unsigned long safety;
+ unsigned long return_address;
+ unsigned long eflags;
+} __attribute__((packed));
+
+typedef struct suspend2_saved_context suspend2_saved_context_t;
+
+/* temporary storage */
+extern struct suspend2_saved_context suspend2_saved_context;
+
+static inline void suspend2_arch_flush_caches(void)
+{
+#ifdef CONFIG_SMP
+ clear_bit(0, &read_pda(active_mm)->cpu_vm_mask);
+#endif
+ wbinvd();
+ __flush_tlb_all();
+
+}
+
+/*
+ * save_processor_context
+ *
+ * Save the state of the processor before we go to sleep.
+ *
+ * return_stack is the value of the stack pointer (%esp) as the caller sees it.
+ * A good way could not be found to obtain it from here (don't want to make _too_
+ * many assumptions about the layout of the stack this far down.) Also, the
+ * handy little __builtin_frame_pointer(level) where level > 0, is blatantly
+ * buggy - it returns the value of the stack at the proper location, not the
+ * location, like it should (as of gcc 2.91.66)
+ *
+ * Note that the context and timing of this function is pretty critical.
+ * With a minimal amount of things going on in the caller and in here, gcc
+ * does a good job of being just a dumb compiler. Watch the assembly output
+ * if anything changes, though, and make sure everything is going in the right
+ * place.
+ */
+static inline void suspend2_arch_save_processor_context(void)
+{
+ kernel_fpu_begin();
+
+ /*
+ * descriptor tables
+ */
+ asm volatile ("sgdt %0" : "=m" (suspend2_saved_context.gdt_limit));
+ asm volatile ("sidt %0" : "=m" (suspend2_saved_context.idt_limit));
+ asm volatile ("str %0" : "=m" (suspend2_saved_context.tr));
+
+ /*
+ * segment registers
+ */
+ asm volatile ("movw %%ds, %0" : "=r" (suspend2_saved_context.ds));
+ asm volatile ("movw %%es, %0" : "=r" (suspend2_saved_context.es));
+ asm volatile ("movw %%fs, %0" : "=r" (suspend2_saved_context.fs));
+ asm volatile ("movw %%gs, %0" : "=r" (suspend2_saved_context.gs));
+ asm volatile ("movw %%ss, %0" : "=r" (suspend2_saved_context.ss));
+
+ rdmsrl(MSR_FS_BASE, suspend2_saved_context.fs_base);
+ rdmsrl(MSR_GS_BASE, suspend2_saved_context.gs_base);
+ rdmsrl(MSR_KERNEL_GS_BASE, suspend2_saved_context.gs_kernel_base);
+
+ /*
+ * control registers
+ */
+ asm volatile ("movq %%cr0, %0" : "=r" (suspend2_saved_context.cr0));
+ asm volatile ("movq %%cr2, %0" : "=r" (suspend2_saved_context.cr2));
+ asm volatile ("movq %%cr3, %0" : "=r" (suspend2_saved_context.cr3));
+ asm volatile ("movq %%cr4, %0" : "=r" (suspend2_saved_context.cr4));
+ asm volatile ("movq %%cr8, %0" : "=r" (suspend2_saved_context.cr8));
+
+ /*
+ * save the general registers.
+ * note that gcc has constructs to specify output of certain registers,
+ * but they're not used here, because it assumes that you want to modify
+ * those registers, so it tries to be smart and save them beforehand.
+ * It's really not necessary, and kinda fishy (check the assembly output),
+ * so it's avoided.
+ */
+
+ asm volatile ("movq %%rsp, %0" : "=m" (suspend2_saved_context.esp));
+
+ asm volatile ("movq %%rax, %0" : "=m" (suspend2_saved_context.eax));
+ asm volatile ("movq %%rbx, %0" : "=m" (suspend2_saved_context.ebx));
+ asm volatile ("movq %%rcx, %0" : "=m" (suspend2_saved_context.ecx));
+ asm volatile ("movq %%rdx, %0" : "=m" (suspend2_saved_context.edx));
+ asm volatile ("movq %%rbp, %0" : "=m" (suspend2_saved_context.ebp));
+ asm volatile ("movq %%rsi, %0" : "=m" (suspend2_saved_context.esi));
+ asm volatile ("movq %%rdi, %0" : "=m" (suspend2_saved_context.edi));
+ asm volatile ("movq %%r8, %0" : "=m" (suspend2_saved_context.r8));
+ asm volatile ("movq %%r9, %0" : "=m" (suspend2_saved_context.r9));
+ asm volatile ("movq %%r10, %0" : "=m" (suspend2_saved_context.r10));
+ asm volatile ("movq %%r11, %0" : "=m" (suspend2_saved_context.r11));
+ asm volatile ("movq %%r12, %0" : "=m" (suspend2_saved_context.r12));
+ asm volatile ("movq %%r13, %0" : "=m" (suspend2_saved_context.r13));
+ asm volatile ("movq %%r14, %0" : "=m" (suspend2_saved_context.r14));
+ asm volatile ("movq %%r15, %0" : "=m" (suspend2_saved_context.r15));
+
+ /*
+ * eflags
+ */
+ asm volatile ("pushfq ; popq %0" : "=m" (suspend2_saved_context.eflags));
+
+}
+
+static void do_fpu_end(void)
+{
+ /* restore FPU regs if necessary */
+ /* Do it out of line so that gcc does not move cr0 load to some stupid place */
+ kernel_fpu_end();
+ mxcsr_feature_mask_init();
+}
+
+/*
+ * restore_processor_context
+ *
+ * Restore the processor context as it was before we went to sleep
+ * - descriptor tables
+ * - control registers
+ * - segment registers
+ * - flags
+ *
+ * Note that it is critical that this function is declared inline.
+ * It was separated out from restore_state to make that function
+ * a little clearer, but it needs to be inlined because we won't have a
+ * stack when we get here (so we can't push a return address).
+ */
+static inline void restore_processor_context(void)
+{
+ /*
+ * Credit for this goes to the swsusp code. Restoring the
+ * CPU context is the one thing we still do in the same
+ * way, and swsusp did it right first.
+ *
+ * 0xffffffff80000000UL is __START_KERNEL_map.
+ */
+
+ __asm__ __volatile__(
+ "leaq init_level4_pgt(%rip), %rax; \n"
+ "subq $0xffffffff80000000, %rax; \n"
+ "movq %rax, %cr3; \n"
+ "movq mmu_cr4_features(%rip), %rax; \n"
+ "movq %rax, %rdx; \n"
+ "andq $~(1<<7), %rdx; # PGE \n"
+ "movq %rdx, %cr4; # turn off PGE \n"
+ "movq %cr3, %rcx; # flush TLB \n"
+ "movq %rcx, %cr3; \n"
+ "movq %rax, %cr4; # turn PGE back on; \n"
+
+ "movl $24, %eax; \n"
+ "movl %eax, %ds \n");
+ /*
+ * the other general registers
+ *
+ * note that even though gcc has constructs to specify memory
+ * input into certain registers, it will try to be too smart
+ * and save them at the beginning of the function. This is esp.
+ * bad since we don't have a stack set up when we enter, and we
+ * want to preserve the values on exit. So, we set them manually.
+ */
+ asm volatile ("movq %0, %%rsp" :: "m" (suspend2_saved_context.esp));
+ asm volatile ("movq %0, %%rbp" :: "m" (suspend2_saved_context.ebp));
+ asm volatile ("movq %0, %%rbx" :: "m" (suspend2_saved_context.ebx));
+ asm volatile ("movq %0, %%rcx" :: "m" (suspend2_saved_context.ecx));
+ asm volatile ("movq %0, %%rdx" :: "m" (suspend2_saved_context.edx));
+ asm volatile ("movq %0, %%rsi" :: "m" (suspend2_saved_context.esi));
+ asm volatile ("movq %0, %%rdi" :: "m" (suspend2_saved_context.edi));
+ asm volatile ("movq %0, %%r8" :: "m" (suspend2_saved_context.r8));
+ asm volatile ("movq %0, %%r9" :: "m" (suspend2_saved_context.r9));
+ asm volatile ("movq %0, %%r10" :: "m" (suspend2_saved_context.r10));
+ asm volatile ("movq %0, %%r11" :: "m" (suspend2_saved_context.r11));
+ asm volatile ("movq %0, %%r12" :: "m" (suspend2_saved_context.r12));
+ asm volatile ("movq %0, %%r13" :: "m" (suspend2_saved_context.r13));
+ asm volatile ("movq %0, %%r14" :: "m" (suspend2_saved_context.r14));
+ asm volatile ("movq %0, %%r15" :: "m" (suspend2_saved_context.r15));
+
+ /*
+ * the flags
+ */
+ asm volatile ("pushq %0 ; popfq" :: "m" (suspend2_saved_context.eflags));
+
+ asm volatile ("xorq %rax, %rax");
+
+ /*
+ * control registers
+ */
+ asm volatile ("movq %0, %%cr8" :: "r" (suspend2_saved_context.cr8));
+ asm volatile ("movq %0, %%cr4" :: "r" (suspend2_saved_context.cr4));
+ asm volatile ("movq %0, %%cr3" :: "r" (suspend2_saved_context.cr3));
+ asm volatile ("movq %0, %%cr2" :: "r" (suspend2_saved_context.cr2));
+ asm volatile ("movq %0, %%cr0" :: "r" (suspend2_saved_context.cr0));
+
+ /*
+ * now restore the descriptor tables to their proper values
+ * ltr is done in fix_processor_context().
+ */
+
+ asm volatile ("lgdt %0" :: "m" (suspend2_saved_context.gdt_limit));
+ asm volatile ("lidt %0" :: "m" (suspend2_saved_context.idt_limit));
+
+ /*
+ * segment registers
+ */
+ asm volatile ("movw %0, %%ds" :: "r" (suspend2_saved_context.ds));
+ asm volatile ("movw %0, %%es" :: "r" (suspend2_saved_context.es));
+ asm volatile ("movw %0, %%fs" :: "r" (suspend2_saved_context.fs));
+ load_gs_index(suspend2_saved_context.gs);
+ asm volatile ("movw %0, %%ss" :: "r" (suspend2_saved_context.ss));
+
+ wrmsrl(MSR_FS_BASE, suspend2_saved_context.fs_base);
+ wrmsrl(MSR_GS_BASE, suspend2_saved_context.gs_base);
+ wrmsrl(MSR_KERNEL_GS_BASE, suspend2_saved_context.gs_kernel_base);
+
+ /* tell gcc that we clobbered all the registers...
+ * otherwise it might keep some addresses there. */
+ asm volatile ("" : : : "rsp", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15");
+
+ fix_processor_context();
+
+ do_fpu_end();
+
+ suspend2_arch_flush_caches();
+
+ mtrr_ap_init();
+}
+
+#if defined(CONFIG_SUSPEND2) || defined(CONFIG_SMP)
+extern unsigned char * my_saved_context __nosavedata;
+static unsigned long c_loops_per_jiffy_ref[NR_CPUS] __nosavedata;
+#endif
+
+#ifdef CONFIG_SUSPEND2
+#ifndef CONFIG_SMP
+extern unsigned long loops_per_jiffy;
+volatile static unsigned long cpu_khz_ref __nosavedata = 0;
+#endif
+
+/*
+ * APIC support: These routines save the APIC
+ * configuration for the CPU on which they are
+ * being executed
+ */
+extern void suspend_apic_save_state(void);
+extern void suspend_apic_reload_state(void);
+
+static inline void suspend2_arch_pre_copy(void)
+{
+}
+
+static inline void suspend2_arch_post_copy(void)
+{
+}
+
+/* Based on the version from swsusp */
+static int res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long end)
+{
+ long i, j;
+
+ i = pud_index(address);
+ pud = pud + i;
+ for (; i < PTRS_PER_PUD; pud++, i++) {
+ unsigned long paddr;
+ pmd_t *pmd;
+
+ paddr = address + i*PUD_SIZE;
+ if (paddr >= end)
+ break;
+
+ pmd = (pmd_t *)suspend_get_nonconflicting_pages(0);
+ if (!pmd)
+ return -ENOMEM;
+ set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE));
+ for (j = 0; j < PTRS_PER_PMD; pmd++, j++, paddr += PMD_SIZE) {
+ unsigned long pe;
+
+ if (paddr >= end)
+ break;
+ pe = _PAGE_NX | _PAGE_PSE | _KERNPG_TABLE | paddr;
+ pe &= __supported_pte_mask;
+ set_pmd(pmd, __pmd(pe));
+ }
+ }
+ return 0;
+}
+
+static int set_up_temporary_mappings_suspend2(void)
+{
+ unsigned long start, end, next;
+ int error;
+
+ temp_level4_pgt = (pgd_t *)suspend_get_nonconflicting_pages(0);
+ if (!temp_level4_pgt)
+ return -ENOMEM;
+
+ /* It is safe to reuse the original kernel mapping */
+ set_pgd(temp_level4_pgt + pgd_index(__START_KERNEL_map),
+ init_level4_pgt[pgd_index(__START_KERNEL_map)]);
+
+ /* Set up the direct mapping from scratch */
+ start = (unsigned long)pfn_to_kaddr(0);
+ end = (unsigned long)pfn_to_kaddr(end_pfn);
+
+ for (; start < end; start = next) {
+ pud_t *pud = (pud_t *)suspend_get_nonconflicting_pages(0);
+ if (!pud)
+ return -ENOMEM;
+ next = start + PGDIR_SIZE;
+ if (next > end)
+ next = end;
+ if ((error = res_phys_pud_init(pud, __pa(start), __pa(next))))
+ return error;
+ set_pgd(temp_level4_pgt + pgd_index(start),
+ mk_kernel_pgd(__pa(pud)));
+ }
+ return 0;
+}
+
+static inline void suspend2_arch_pre_copyback(void)
+{
+ /* We want to run from swsusp_pg_dir, since swsusp_pg_dir is stored in
+ * constant place in memory.
+ */
+
+ set_up_temporary_mappings_suspend2();
+
+ asm volatile ("movq $0xffff810000000000, %rdx");
+ asm volatile ("movq temp_level4_pgt(%rip), %rax");
+ asm volatile ("subq %rdx, %rax");
+ asm volatile ("movq %rax, %cr3");
+
+ wbinvd();
+ __flush_tlb_all();
+
+ c_loops_per_jiffy_ref[0] =
+ current_cpu_data.loops_per_jiffy;
+#ifndef CONFIG_SMP
+ cpu_khz_ref = cpu_khz;
+ c_loops_per_jiffy_ref[0] = loops_per_jiffy;
+#endif
+
+}
+
+static inline void suspend2_arch_restore_processor_context(void)
+{
+ restore_processor_context();
+}
+
+static inline void suspend2_arch_post_copyback(void)
+{
+ /* Get other CPUs to restore their contexts and flush their tlbs. */
+ BUG_ON(!irqs_disabled());
+
+ current_cpu_data.loops_per_jiffy =
+ c_loops_per_jiffy_ref[0];
+#ifndef CONFIG_SMP
+ loops_per_jiffy = c_loops_per_jiffy_ref[0];
+ cpu_khz = cpu_khz_ref;
+#endif
+}
+
+#endif
diff -urN oldtree/include/linux/bio.h newtree/include/linux/bio.h
--- oldtree/include/linux/bio.h 2006-03-27 13:28:15.000000000 -0500
+++ newtree/include/linux/bio.h 2006-03-27 16:04:08.412499250 -0500
@@ -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_SUSPEND2 8 /* Suspend2 bio - for corruption checking */
#define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag)))
/*
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-03-27 15:56:57.369560750 -0500
@@ -0,0 +1,66 @@
+/*
+ * include/linux/dyn_pageflags.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham
+ *
+ * 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
+
+typedef unsigned long *** dyn_pageflags_t;
+
+#define BITNUMBER(page) (page_to_pfn(page))
+
+#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))
+
+#define BITS_PER_PAGE (PAGE_SIZE << 3)
+#define PAGENUMBER(zone_offset) (zone_offset >> (PAGE_SHIFT + 3))
+#define PAGEINDEX(zone_offset) ((zone_offset & UL_NUM_MASK) >> UL_SHIFT)
+#define PAGEBIT(zone_offset) (zone_offset & BIT_NUM_MASK)
+
+#define PAGE_UL_PTR(bitmap, zone_num, zone_pfn) \
+ ((bitmap[zone_num][PAGENUMBER(zone_pfn)])+PAGEINDEX(zone_pfn))
+
+/* 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))
+*/
+
+#define BITMAP_FOR_EACH_SET(bitmap, counter) \
+ for (counter = get_next_bit_on(bitmap, -1); 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 int dyn_pageflags_pages_per_bitmap(void);
+extern int get_next_bit_on(dyn_pageflags_t bitmap, int counter);
+extern unsigned long *dyn_pageflags_ul_ptr(dyn_pageflags_t *bitmap,
+ struct page *pg);
+
+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
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-03-27 15:30:47.959478750 -0500
@@ -0,0 +1,28 @@
+/* Freezer declarations */
+
+#define FREEZER_ON 0
+#define ABORT_FREEZING 1
+
+#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))
+
+extern void do_freeze_process(struct notifier_block *nl);
+
+#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-03-27 13:28:15.000000000 -0500
+++ newtree/include/linux/kernel.h 2006-03-27 16:04:08.416499500 -0500
@@ -108,6 +108,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/kthread.h newtree/include/linux/kthread.h
--- oldtree/include/linux/kthread.h 2006-03-27 13:28:15.000000000 -0500
+++ newtree/include/linux/kthread.h 2006-03-27 15:30:38.298875000 -0500
@@ -23,10 +23,20 @@
*
* Returns a task_struct or ERR_PTR(-ENOMEM).
*/
+struct task_struct *__kthread_create(int (*threadfn)(void *data),
+ void *data,
+ unsigned long freezer_flags,
+ const char namefmt[],
+ va_list * args);
+
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[], ...);
+struct task_struct *kthread_nofreeze_create(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...);
+
/**
* kthread_run: create and wake a thread.
* @threadfn: the function to run until signal_pending(current).
@@ -35,14 +45,15 @@
*
* Description: Convenient wrapper for kthread_create() followed by
* wake_up_process(). Returns the kthread, or ERR_PTR(-ENOMEM). */
-#define kthread_run(threadfn, data, namefmt, ...) \
-({ \
- struct task_struct *__k \
- = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
- if (!IS_ERR(__k)) \
- wake_up_process(__k); \
- __k; \
-})
+
+extern struct task_struct * kthread_run(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...);
+
+extern struct task_struct * kthread_nofreeze_run(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...);
+
/**
* kthread_bind: bind a just-created kthread to a cpu.
diff -urN oldtree/include/linux/libata.h newtree/include/linux/libata.h
--- oldtree/include/linux/libata.h 2006-03-27 13:28:15.000000000 -0500
+++ newtree/include/linux/libata.h 2006-03-27 16:10:32.844524750 -0500
@@ -33,6 +33,8 @@
#include
#include
#include
+#include
+
/*
* compile-time options: to be removed as soon as all the drivers are
@@ -94,6 +96,38 @@
return (1 << dval) - 1;
}
+/* NEW: debug levels */
+#define HAVE_LIBATA_MSG 1
+
+enum {
+ ATA_MSG_DRV = 0x0001,
+ ATA_MSG_INFO = 0x0002,
+ ATA_MSG_PROBE = 0x0004,
+ ATA_MSG_WARN = 0x0008,
+ ATA_MSG_MALLOC = 0x0010,
+ ATA_MSG_CTL = 0x0020,
+ ATA_MSG_INTR = 0x0040,
+ ATA_MSG_ERR = 0x0080,
+};
+
+#define ata_msg_drv(p) ((p)->msg_enable & ATA_MSG_DRV)
+#define ata_msg_info(p) ((p)->msg_enable & ATA_MSG_INFO)
+#define ata_msg_probe(p) ((p)->msg_enable & ATA_MSG_PROBE)
+#define ata_msg_warn(p) ((p)->msg_enable & ATA_MSG_WARN)
+#define ata_msg_malloc(p) ((p)->msg_enable & ATA_MSG_MALLOC)
+#define ata_msg_ctl(p) ((p)->msg_enable & ATA_MSG_CTL)
+#define ata_msg_intr(p) ((p)->msg_enable & ATA_MSG_INTR)
+#define ata_msg_err(p) ((p)->msg_enable & ATA_MSG_ERR)
+
+static inline u32 ata_msg_init(int dval, int default_msg_enable_bits)
+{
+ if (dval < 0 || dval >= (sizeof(u32) * 8))
+ return default_msg_enable_bits; /* should be 0x1 - only driver info msgs */
+ if (!dval)
+ return 0;
+ return (1 << dval) - 1;
+}
+
/* defines only for the constants which don't work well as enums */
#define ATA_TAG_POISON 0xfafbfcfdU
@@ -146,11 +180,10 @@
* proper HSM is in place. */
ATA_FLAG_DEBUGMSG = (1 << 10),
ATA_FLAG_NO_ATAPI = (1 << 11), /* No ATAPI support */
-
ATA_FLAG_SUSPENDED = (1 << 12), /* port is suspended */
-
ATA_FLAG_PIO_LBA48 = (1 << 13), /* Host DMA engine is LBA28 only */
ATA_FLAG_IRQ_MASK = (1 << 14), /* Mask IRQ in PIO xfers */
+ ATA_FLAG_PATA_MODE = (1 << 15), /* port in PATA mode */
ATA_FLAG_FLUSH_PORT_TASK = (1 << 15), /* Flush port task */
ATA_FLAG_IN_EH = (1 << 16), /* EH in progress */
@@ -240,6 +273,7 @@
struct ata_port_operations;
struct ata_port;
struct ata_queued_cmd;
+struct GTM_buffer;
/* typedefs */
typedef void (*ata_qc_cb_t) (struct ata_queued_cmd *qc);
@@ -364,6 +398,11 @@
u16 cylinders; /* Number of cylinders */
u16 heads; /* Number of heads */
u16 sectors; /* Number of sectors per track */
+
+#ifdef CONFIG_SCSI_SATA_ACPI
+ /* ACPI objects info */
+ acpi_handle obj_handle;
+#endif
};
struct ata_port {
@@ -384,6 +423,7 @@
u8 ctl; /* cache of ATA control register */
u8 last_ctl; /* Cache last written value */
+ u8 legacy_mode;
unsigned int pio_mask;
unsigned int mwdma_mask;
unsigned int udma_mask;
@@ -402,6 +442,13 @@
unsigned int hsm_task_state;
unsigned long pio_task_timeout;
+ struct device *dev;
+
+ u32 msg_enable;
+#ifdef CONFIG_SCSI_SATA_ACPI
+ struct GTM_buffer *gtm;
+ void *gtm_object_area;
+#endif
u32 msg_enable;
struct list_head eh_done_q;
@@ -518,8 +565,10 @@
extern unsigned int ata_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
extern int ata_scsi_device_resume(struct scsi_device *);
extern int ata_scsi_device_suspend(struct scsi_device *);
+extern int ata_scsi_device_shutdown(struct scsi_device *);
extern int ata_device_resume(struct ata_port *, struct ata_device *);
extern int ata_device_suspend(struct ata_port *, struct ata_device *);
+extern int ata_device_shutdown(struct ata_port *, struct ata_device *);
extern int ata_ratelimit(void);
extern unsigned int ata_busy_sleep(struct ata_port *ap,
unsigned long timeout_pat,
diff -urN oldtree/include/linux/netlink.h newtree/include/linux/netlink.h
--- oldtree/include/linux/netlink.h 2006-03-27 13:28:15.000000000 -0500
+++ newtree/include/linux/netlink.h 2006-03-27 16:11:14.235111500 -0500
@@ -21,7 +21,9 @@
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
#define NETLINK_GENERIC 16
-#define NETLINK_TGT 17 /* SCSI target */
+#define NETLINK_SUSPEND2_USERUI 17 /* For suspend2's userui */
+#define NETLINK_SUSPEND2_USM 18 /* For suspend2's userui */
+#define NETLINK_TGT 19 /* SCSI target */
#define MAX_LINKS 32
diff -urN oldtree/include/linux/sched.h newtree/include/linux/sched.h
--- oldtree/include/linux/sched.h 2006-03-27 13:28:15.000000000 -0500
+++ newtree/include/linux/sched.h 2006-03-27 16:04:08.432500500 -0500
@@ -1469,7 +1469,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)
{
@@ -1488,7 +1488,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-03-27 13:28:15.000000000 -0500
+++ newtree/include/linux/suspend.h 2006-03-27 16:04:08.436500750 -0500
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
/* page backup entry */
typedef struct pbe {
@@ -46,6 +47,8 @@
#if defined(CONFIG_VT) && defined(CONFIG_VT_CONSOLE)
extern int pm_prepare_console(void);
extern void pm_restore_console(void);
+extern int freeze_processes(void);
+extern void thaw_processes(int which_threads);
#else
static inline int pm_prepare_console(void) { return 0; }
static inline void pm_restore_console(void) {}
@@ -56,8 +59,12 @@
printk("Warning: fake suspend called\n");
return -EPERM;
}
+static inline int freeze_processes(void) { return 0; }
+static inline void thaw_processes(int which_threads) { }
#endif /* CONFIG_PM */
+extern char resume2_file[256];
+
#ifdef CONFIG_SUSPEND_SMP
extern void disable_nonboot_cpus(void);
extern void enable_nonboot_cpus(void);
@@ -69,8 +76,6 @@
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-03-27 16:04:08.440501000 -0500
@@ -0,0 +1,217 @@
+#ifndef _LINUX_SUSPEND2_H
+#define _LINUX_SUSPEND2_H
+
+#include
+#include
+#ifdef CONFIG_ACPI
+#include
+#include
+#endif
+
+/* arch/i386/mm/init.c */
+extern char __nosave_begin, __nosave_end;
+
+extern char __nosavedata swsusp_pg_dir[PAGE_SIZE]
+ __attribute__ ((aligned (PAGE_SIZE)));
+
+#define SECTOR_SIZE 512
+
+/* kernel/power/process.c */
+
+/* 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))
+
+/*
+ * First status register - this is suspend's return code.
+ *
+ * All the rest are in kernel/power/suspend2_common.h
+ */
+#define SUSPEND_ABORTED 0
+
+/* Second status register - ditto */
+#define SUSPEND_RETRY_RESUME 0
+
+/* Debug sections - if debugging compiled in */
+enum {
+ SUSPEND_ANY_SECTION,
+ SUSPEND_FREEZER,
+ SUSPEND_EAT_MEMORY,
+ SUSPEND_PAGESETS,
+ SUSPEND_IO,
+ SUSPEND_BMAP,
+ SUSPEND_HEADER,
+ SUSPEND_WRITER,
+ SUSPEND_MEMORY,
+ SUSPEND_EXTENTS,
+ SUSPEND_SPINLOCKS,
+ SUSPEND_MEM_POOL,
+ SUSPEND_RANGE_PARANOIA,
+ SUSPEND_NOSAVE,
+ SUSPEND_INTEGRITY
+};
+
+/* 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_NOPAGESET2,
+ 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,
+};
+
+#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 (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 == 0) \
+ __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_ACT_USED,
+ SUSPEND_DBG_USED,
+ SUSPEND_LVL_USED,
+ SUSPEND_TRYING_TO_RESUME,
+ SUSPEND_FORK_COPYBACK_THREAD,
+ SUSPEND_TRY_RESUME_RD,
+ SUSPEND_IGNORE_ROOTFS,
+};
+
+#define test_and_set_suspend_state(bit) \
+ (test_and_set_bit(bit, &software_suspend_state))
+
+#define get_suspend_state() (software_suspend_state)
+#define restore_suspend_state(saved_state) \
+ do { software_suspend_state = saved_state; } while(0)
+
+/* --------------------------------------------------------------------- */
+#ifdef CONFIG_SUSPEND2
+
+/* Used in init dir files */
+extern unsigned long software_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 void suspend_handle_keypress(unsigned int keycode, int source);
+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, &software_suspend_state))
+
+#define clear_suspend_state(bit) \
+ (clear_bit(bit, &software_suspend_state))
+
+#define set_suspend_state(bit) \
+ (set_bit(bit, &software_suspend_state))
+
+extern inline void suspend_copyback_low(void);
+extern inline void suspend_copyback_high(void);
+
+extern void suspend2_try_suspend(void);
+
+/* --------------------------------------------------------------------- */
+#else
+/* --------------------------------------------------------------------- */
+
+#define software_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; }
+#define suspend_handle_keypress(a, b) do { } while(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 */
+
+#if defined(CONFIG_SUSPEND2) && defined(CONFIG_ACPI)
+static inline int may_try_suspend2(u32 state)
+{
+ if (state == ACPI_STATE_S4) {
+ suspend2_try_suspend();
+ 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/workqueue.h newtree/include/linux/workqueue.h
--- oldtree/include/linux/workqueue.h 2006-03-27 13:28:15.000000000 -0500
+++ newtree/include/linux/workqueue.h 2006-03-27 15:30:38.306875500 -0500
@@ -55,9 +55,12 @@
} while (0)
extern struct workqueue_struct *__create_workqueue(const char *name,
- int singlethread);
-#define create_workqueue(name) __create_workqueue((name), 0)
-#define create_singlethread_workqueue(name) __create_workqueue((name), 1)
+ int singlethread,
+ unsigned long freezer_flag);
+#define create_workqueue(name) __create_workqueue((name), 0, 0)
+#define create_nofreeze_workqueue(name) __create_workqueue((name), 0, PF_NOFREEZE)
+#define create_singlethread_workqueue(name) __create_workqueue((name), 1, 0)
+#define create_nofreeze_singlethread_workqueue(name) __create_workqueue((name), 1, PF_NOFREEZE)
extern void destroy_workqueue(struct workqueue_struct *wq);
diff -urN oldtree/include/scsi/scsi_host.h newtree/include/scsi/scsi_host.h
--- oldtree/include/scsi/scsi_host.h 2006-03-27 13:28:15.000000000 -0500
+++ newtree/include/scsi/scsi_host.h 2006-03-27 15:57:04.369998250 -0500
@@ -318,6 +318,7 @@
*/
int (*resume)(struct scsi_device *);
int (*suspend)(struct scsi_device *);
+ int (*shutdown)(struct scsi_device *);
/*
* Name of proc directory
diff -urN oldtree/init/do_mounts.c newtree/init/do_mounts.c
--- oldtree/init/do_mounts.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/init/do_mounts.c 2006-03-27 15:56:45.400812750 -0500
@@ -139,11 +139,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 +200,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");
@@ -412,9 +418,25 @@
is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
+ /* Suspend2:
+ * By this point, suspend_early_init has been called to initialise our
+ * proc interface. If modules are built in, they have registered (all
+ * of the above via late_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 > /proc/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;
+ if (test_suspend_state(SUSPEND_RESUME_NOT_DONE))
+ suspend2_try_resume();
+
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-03-27 13:28:15.000000000 -0500
+++ newtree/init/do_mounts_initrd.c 2006-03-27 15:56:45.412813500 -0500
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#include "do_mounts.h"
@@ -59,10 +60,16 @@
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 > /proc/suspend2/do_resume.\n");
+ clear_suspend_state(SUSPEND_BOOT_TIME);
+
/* 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-03-27 13:28:15.000000000 -0500
+++ newtree/init/main.c 2006-03-27 15:56:45.420814000 -0500
@@ -723,7 +723,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/audit.c newtree/kernel/audit.c
--- oldtree/kernel/audit.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/audit.c 2006-03-27 15:30:38.314876000 -0500
@@ -294,6 +294,9 @@
}
} else {
DECLARE_WAITQUEUE(wait, current);
+
+ try_to_freeze();
+
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&kauditd_wait, &wait);
diff -urN oldtree/kernel/fork.c newtree/kernel/fork.c
--- oldtree/kernel/fork.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/fork.c 2006-03-27 15:56:26.323620500 -0500
@@ -35,6 +35,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -172,7 +173,12 @@
if (!tsk)
return NULL;
- ti = alloc_thread_info(tsk);
+ if (unlikely(test_suspend_state(SUSPEND_FORK_COPYBACK_THREAD))) {
+ extern void * suspend_get_nonconflicting_pages(int);
+ ti = suspend_get_nonconflicting_pages(get_order(THREAD_SIZE));
+ } else
+ ti = alloc_thread_info(tsk);
+
if (!ti) {
free_task_struct(tsk);
return NULL;
diff -urN oldtree/kernel/kmod.c newtree/kernel/kmod.c
--- oldtree/kernel/kmod.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/kmod.c 2006-03-27 15:56:16.186987000 -0500
@@ -36,6 +36,7 @@
#include
#include
#include
+#include
#include
extern int max_threads;
@@ -249,6 +250,9 @@
if (!khelper_wq)
return -EBUSY;
+ if (freezer_is_on())
+ return 0;
+
if (path[0] == '\0')
return 0;
diff -urN oldtree/kernel/kthread.c newtree/kernel/kthread.c
--- oldtree/kernel/kthread.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/kthread.c 2006-03-27 15:30:38.330877000 -0500
@@ -26,6 +26,7 @@
/* Information passed to kthread() from keventd. */
int (*threadfn)(void *data);
void *data;
+ unsigned long freezer_flags;
struct completion started;
/* Result passed back to kthread_create() from keventd. */
@@ -87,6 +88,10 @@
/* By default we can run anywhere, unlike keventd. */
set_cpus_allowed(current, CPU_MASK_ALL);
+ /* Set our freezer flags */
+ current->flags &= ~PF_NOFREEZE;
+ current->flags |= (create->freezer_flags & PF_NOFREEZE);
+
/* OK, tell user we're spawned, wait for stop or wakeup */
__set_current_state(TASK_INTERRUPTIBLE);
complete(&create->started);
@@ -122,16 +127,18 @@
complete(&create->done);
}
-struct task_struct *kthread_create(int (*threadfn)(void *data),
+struct task_struct *__kthread_create(int (*threadfn)(void *data),
void *data,
+ unsigned long freezer_flags,
const char namefmt[],
- ...)
+ va_list * args)
{
struct kthread_create_info create;
DECLARE_WORK(work, keventd_create_kthread, &create);
create.threadfn = threadfn;
create.data = data;
+ create.freezer_flags = freezer_flags;
init_completion(&create.started);
init_completion(&create.done);
@@ -144,18 +151,89 @@
queue_work(helper_wq, &work);
wait_for_completion(&create.done);
}
- if (!IS_ERR(create.result)) {
- va_list args;
- va_start(args, namefmt);
+ if (!IS_ERR(create.result))
vsnprintf(create.result->comm, sizeof(create.result->comm),
- namefmt, args);
- va_end(args);
- }
+ namefmt, *args);
return create.result;
}
+
+struct task_struct *kthread_create(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...)
+{
+ struct task_struct * result;
+
+ va_list args;
+ va_start(args, namefmt);
+ result = __kthread_create(threadfn, data, 0, namefmt, &args);
+ va_end(args);
+ return result;
+}
+
EXPORT_SYMBOL(kthread_create);
+struct task_struct *kthread_nofreeze_create(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...)
+{
+ struct task_struct * result;
+
+ va_list args;
+ va_start(args, namefmt);
+ result = __kthread_create(threadfn, data, PF_NOFREEZE, namefmt, &args);
+ va_end(args);
+ return result;
+}
+
+EXPORT_SYMBOL(kthread_nofreeze_create);
+
+/**
+ * kthread_run: create and wake a thread.
+ * @threadfn: the function to run until signal_pending(current).
+ * @data: data ptr for @threadfn.
+ * @namefmt: printf-style name for the thread.
+ *
+ * Description: Convenient wrapper for kthread_create() followed by
+ * wake_up_process(). Returns the kthread, or ERR_PTR(-ENOMEM).
+ **/
+struct task_struct * kthread_run(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...)
+{
+ struct task_struct *__k;
+ va_list args;
+
+ va_start(args, namefmt);
+ __k = __kthread_create(threadfn, data, 0, namefmt, &args);
+ va_end(args);
+
+ if(!IS_ERR(__k))
+ wake_up_process(__k);
+
+ return __k;
+}
+
+EXPORT_SYMBOL(kthread_run);
+
+struct task_struct * kthread_nofreeze_run(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...)
+{
+ struct task_struct *__k;
+ va_list args;
+
+ va_start(args, namefmt);
+ __k = __kthread_create(threadfn, data, PF_NOFREEZE, namefmt, &args);
+ va_end(args);
+
+ if(!IS_ERR(__k))
+ wake_up_process(__k);
+
+ return __k;
+}
+EXPORT_SYMBOL(kthread_nofreeze_run);
+
void kthread_bind(struct task_struct *k, unsigned int cpu)
{
BUG_ON(k->state != TASK_INTERRUPTIBLE);
diff -urN oldtree/kernel/power/Kconfig newtree/kernel/power/Kconfig
--- oldtree/kernel/power/Kconfig 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/power/Kconfig 2006-03-27 16:04:08.444501250 -0500
@@ -98,3 +98,76 @@
bool
depends on HOTPLUG_CPU && X86 && PM
default y
+
+config SUSPEND_DEBUG_PAGEALLOC
+ bool
+ depends on DEBUG_PAGEALLOC && (SOFTWARE_SUSPEND || SUSPEND2)
+ default y
+
+config SUSPEND2_CRYPTO
+ bool
+ depends on SUSPEND2 && CRYPTO
+ default y
+
+menuconfig SUSPEND2
+ bool "Suspend2"
+ select DYN_PAGEFLAGS
+ depends on PM
+ 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_CHECKSUMMING
+ bool ' Checksum images - developer option (SLOW!)'
+ depends on PM_DEBUG && SUSPEND2
+ ---help---
+ This option implements checksumming of images. It is not designed
+ for everyone to use, but as a development tool.
+
+ 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!
diff -urN oldtree/kernel/power/Makefile newtree/kernel/power/Makefile
--- oldtree/kernel/power/Makefile 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/power/Makefile 2006-03-27 16:12:38.356368750 -0500
@@ -2,11 +2,31 @@
ifeq ($(CONFIG_PM_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
+CFLAGS_atomic_copy.o := -O0
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_SMP) += smp.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 proc.o \
+ power_off.o atomic_copy.o netlink.o
+
+#ifdef CONFIG_NET
+suspend_core-objs += storage.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_SUSPEND2_CHECKSUMMING) += suspend_checksums.o
+
+obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o snapshot.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-03-27 16:04:08.452501750 -0500
@@ -0,0 +1,520 @@
+/*
+ * kernel/power/atomic_copy.c
+ *
+ * Copyright 2004-2006 Nigel Cunningham
+ *
+ * Distributed under GPLv2.
+ *
+ * Routines for doing the atomic save/restore.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "suspend2_common.h"
+#include "io.h"
+#include "power_off.h"
+#include "version.h"
+#include "ui.h"
+#include "modules.h"
+#include "atomic_copy.h"
+#include "suspend2.h"
+#include "checksum.h"
+#include "pageflags.h"
+#include "storage.h"
+
+#include
+
+static int state1 __nosavedata = 0;
+static int state2 __nosavedata = 0;
+static int state3 __nosavedata = 0;
+static int io_speed_save[2][2] __nosavedata;
+
+static dyn_pageflags_t __nosavedata origmap;
+static dyn_pageflags_t __nosavedata copymap;
+static unsigned long __nosavedata origoffset;
+static unsigned long __nosavedata copyoffset;
+static int __nosavedata loop;
+static __nosavedata int o_zone_num, c_zone_num;
+static __nosavedata int is_resuming;
+
+__nosavedata char resume_commandline[COMMAND_LINE_SIZE];
+
+static atomic_t atomic_copy_hold;
+static atomic_t restore_thread_ready;
+
+suspend2_saved_context_t suspend2_saved_context; /* temporary storage */
+
+struct zone_data {
+ unsigned long start_pfn;
+ unsigned long end_pfn;
+ int is_highmem;
+};
+
+static __nosavedata struct zone_data *zone_nosave;
+static __nosavedata int num_zones;
+
+extern void suspend_power_down(void);
+
+/**
+ * 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_pages(0);
+
+ BUG_ON(!zone_nosave);
+
+ for_each_zone(zone) {
+ if (zone->spanned_pages) {
+ zone_nosave[num_zones].start_pfn = zone->zone_start_pfn;
+ zone_nosave[num_zones].end_pfn = zone->zone_start_pfn +
+ zone->spanned_pages - 1;
+ zone_nosave[num_zones].is_highmem = is_highmem(zone);
+ }
+ num_zones++;
+ }
+}
+
+/**
+ * __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, long counter)
+{
+ unsigned long *ul_ptr = NULL;
+ int reset_ul_ptr = 1;
+ BUG_ON(counter == max_pfn);
+
+ if (counter == -1) {
+ *zone_num = 0;
+
+ /*
+ * Test the end because the start can validly
+ * be zero.
+ */
+ 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_nosave[*zone_num].end_pfn && *zone_num < num_zones)
+ (*zone_num)++;
+
+ if (*zone_num == num_zones)
+ return max_pfn;
+ counter = zone_nosave[*zone_num].start_pfn;
+ reset_ul_ptr = 1;
+ } else
+ if (!(counter & BIT_NUM_MASK))
+ reset_ul_ptr = 1;
+ 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) {
+ counter += BIT_NUM_MASK - 1;
+ continue;
+ }
+ }
+ } while((counter < max_pfn) && !test_bit(PAGEBIT(counter), ul_ptr));
+
+ return counter;
+}
+
+/**
+ * copyback_prepare: Prepare to do an atomic restore.
+ *
+ * Highlevel preparation for restoring the atomic copy.
+ **/
+
+static void copyback_prepare(void)
+{
+ int loop;
+
+ 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];
+
+ suspend_init_nosave_zone_table();
+
+ memcpy(resume_commandline, saved_command_line, COMMAND_LINE_SIZE);
+
+ suspend_deactivate_storage(1);
+
+ device_suspend(PMSG_FREEZE);
+
+ /* Arch specific preparation */
+ suspend2_arch_pre_copyback();
+
+ local_irq_disable(); /* irqs might have been re-enabled on us by buggy drivers */
+
+ device_power_down(PMSG_FREEZE);
+
+ barrier();
+ mb();
+}
+
+/*
+ * 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.
+ */
+
+static void copyback_post(void)
+{
+ int loop;
+
+ /* Arch specific code */
+ suspend2_arch_post_copyback();
+
+ 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);
+
+ local_irq_disable();
+ device_power_up();
+ local_irq_enable();
+
+ device_resume();
+
+ 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();
+
+ check_shift_keys(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_pre_copy: Prepare for an atomic copy.
+ *
+ * Prepare to do an atomic copy of memory. We suspend and power
+ * down devices, do anything arch specific and disable interrupts.
+ * Secondary cpus were hotunplugged prior to freezing processes.
+ */
+
+static void suspend_pre_copy(void)
+{
+ suspend2_arch_pre_copy();
+
+ device_suspend(PMSG_FREEZE);
+
+ mb();
+ barrier();
+
+ local_irq_disable();
+
+ device_power_down(PMSG_FREEZE);
+}
+
+/*
+ * suspend_post_copy: Steps after doing an atomic copy.
+ *
+ * Steps taken after saving CPU state to save the
+ * image and powerdown/reboot or recover on failure.
+ */
+
+static void suspend_post_copy(void)
+{
+ suspend2_arch_post_copy();
+
+ 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();
+}
+
+/*
+ * copyback_low: Do the atomic restore of memory.
+ *
+ * Iterate through the source and destination bitmaps, copying
+ * lowmem pages back.
+ */
+
+static inline void copyback_low(void)
+{
+ unsigned long *origpage;
+ unsigned long *copypage;
+
+ o_zone_num = 0;
+ c_zone_num = 0;
+
+ origmap = pageset1_map;
+ copymap = pageset1_copy_map;
+
+ origoffset = __suspend_get_next_bit_on(origmap, &o_zone_num, -1);
+ copyoffset = __suspend_get_next_bit_on(copymap, &c_zone_num, -1);
+
+ while (origoffset < max_pfn) {
+ if (!zone_nosave[o_zone_num].is_highmem) {
+ origpage = (unsigned long *) __va(origoffset << PAGE_SHIFT);
+ copypage = (unsigned long *) __va(copyoffset << PAGE_SHIFT);
+
+ loop = (PAGE_SIZE / sizeof(unsigned long)) - 1;
+
+ while (loop >= 0) {
+ *(origpage + loop) = *(copypage + loop);
+ loop--;
+ }
+ }
+
+ origoffset = __suspend_get_next_bit_on(origmap, &o_zone_num, origoffset);
+ copyoffset = __suspend_get_next_bit_on(copymap, &c_zone_num, copyoffset);
+ }
+}
+
+/*
+ * copyback_high: Restore highmem pages.
+ *
+ * Iterate through the source and destination bitmaps, restoring
+ * highmem pages that were atomically copied.
+ */
+static void copyback_high(void)
+{
+ unsigned long *origpage;
+ unsigned long *copypage;
+
+ origoffset = get_next_bit_on(origmap, -1);
+ copyoffset = get_next_bit_on(copymap, -1);
+
+ while (origoffset < max_pfn) {
+ if (PageHighMem(pfn_to_page(origoffset))) {
+ 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 = get_next_bit_on(origmap, origoffset);
+ copyoffset = get_next_bit_on(copymap, copyoffset);
+ }
+}
+
+/*
+ * do_suspend2_lowlevel - Do atomic copy or restore of memory.
+ * @resume: Boolean. Whether we're suspending or resuming.
+ *
+ * High level routine for suspending and resuming. Everything
+ * hinges around saving and restoring the processor context
+ * inline in this routine, particularly the return address.
+ */
+void do_suspend2_lowlevel(int resume)
+{
+ is_resuming = resume;
+
+ if (resume) {
+ copyback_prepare();
+
+ suspend2_arch_save_processor_context();
+
+ copyback_low();
+
+ suspend2_arch_restore_processor_context();
+ } else {
+ suspend_pre_copy();
+
+ suspend2_arch_save_processor_context();
+ }
+
+ if (is_resuming) {
+ suspend2_arch_flush_caches();
+
+ /*
+ * Now we are running with our old stack, and with registers
+ * copied from suspend time. Let's copy back those remaining
+ * highmem pages.
+ */
+ copyback_high();
+ suspend2_arch_flush_caches();
+
+ touch_softlockup_watchdog();
+
+ suspend_checksum_print_differences();
+
+ copyback_post();
+
+ } else {
+ /* If everything goes okay, this function does not return. */
+ suspend_post_copy();
+ }
+}
+
+/* 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)
+{
+ unsigned long i, source_index, dest_index;
+
+ source_index = get_next_bit_on(pageset1_map, -1);
+ dest_index = get_next_bit_on(pageset1_copy_map, -1);
+
+ 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);
+
+ copyvirt = (unsigned long *) page_address(pfn_to_page(dest_index));
+
+ if (PageHighMem(origpage))
+ origvirt = kmap_atomic(origpage, KM_USER1);
+ else
+ origvirt = page_address(origpage);
+
+ while (loop >= 0) {
+ *(copyvirt + loop) = *(origvirt + loop);
+ loop--;
+ }
+
+ if (PageHighMem(origpage))
+ kunmap_atomic(origvirt, KM_USER1);
+
+ source_index = get_next_bit_on(pageset1_map, source_index);
+ dest_index = get_next_bit_on(pageset1_copy_map, dest_index);
+ }
+}
+
+/*
+ * __suspend_atomic_restore: Separate thread for doing the atomic restore.
+ *
+ * This is our thread for doing the copyback. We start another thread because
+ * we want to have our stack in a page that doesn't conflict with the image
+ * being restored. The simplest way of doing that is to get a non-conflicting
+ * page when allocating the stack.
+ */
+int __suspend_atomic_restore(void *data)
+{
+ struct page *my_thread_info = virt_to_page(current->thread_info);
+
+ BUG_ON(PagePageset1(my_thread_info));
+ BUG_ON(THREAD_SIZE > PAGE_SIZE && PagePageset1(++my_thread_info));
+
+ atomic_set(&restore_thread_ready, 1);
+
+ while atomic_read(&atomic_copy_hold)
+ yield();
+
+ suspend_prepare_status(DONT_CLEAR_BAR, "Copying original kernel back");
+
+ /*
+ * If you're hitting this BUG_ON, you have a process that's
+ * not freezing which is started prior to this.
+ */
+ BUG_ON(freeze_processes());
+
+ do_suspend2_lowlevel(1);
+
+ printk("Returned from do_suspend2_lowlevel when resuming?!");
+ BUG();
+
+ return 0;
+}
+
+/*
+ * 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.
+ */
+void suspend_atomic_restore(void)
+{
+ struct task_struct *work_thread;
+
+ disable_nonboot_cpus();
+
+ yield();
+
+ set_suspend_state(SUSPEND_FORK_COPYBACK_THREAD);
+ BUG_ON(atomic_read(&restore_thread_ready));
+
+ atomic_set(&atomic_copy_hold, 1);
+
+ /* Now start the new thread */
+ work_thread = kthread_run(__suspend_atomic_restore, 0, "kcopyback");
+ BUG_ON(IS_ERR(work_thread));
+
+ while (!atomic_read(&restore_thread_ready))
+ yield();
+
+ atomic_set(&atomic_copy_hold, 0);
+
+ while(1) {
+ try_to_freeze();
+ yield();
+ }
+}
diff -urN oldtree/kernel/power/atomic_copy.h newtree/kernel/power/atomic_copy.h
--- oldtree/kernel/power/atomic_copy.h 1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/atomic_copy.h 2006-03-27 16:04:08.456502000 -0500
@@ -0,0 +1,13 @@
+/*
+ * kernel/power/atomic_copy.h
+ *
+ * Copyright 2006 Nigel Cunningham
+ *
+ * Distributed under GPLv2.
+ *
+ * Routines for doing the atomic save/restore.
+ */
+
+extern inline void move_stack_to_nonconflicing_area(void);
+extern int save_image_part1(void);
+extern void suspend_atomic_restore(void);
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-03-27 16:04:08.460502250 -0500
@@ -0,0 +1,73 @@
+/*
+ * kernel/power/block_io.h
+ *
+ * Copyright 2004-2006 Nigel Cunningham
+ *
+ * Distributed under GPLv2.
+ *
+ * This file contains declarations for functions exported from
+ * block_io.c, which contains low level io functions.
+ */
+
+#include
+#include "extent.h"
+
+/*
+ * submit_params
+ *
+ * The structure we use for tracking submitted I/O.
+ */
+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;
+};
+
+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 (*submit_io) (int rw,
+ struct submit_params *submit_info, int syncio);
+ int (*bdev_page_io) (int rw, struct block_device *bdev, long pos,
+ struct page *page);
+ int (*rw_page) (int rw, struct page *page, int readahead_index,
+ int sync);
+ void (*wait_on_readahead) (int readahead_index);
+ 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 *need_extra_next;
+ int (*forward_one_page) (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, 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;
diff -urN oldtree/kernel/power/checksum.h newtree/kernel/power/checksum.h
--- oldtree/kernel/power/checksum.h 1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/checksum.h 2006-03-27 16:04:08.460502250 -0500
@@ -0,0 +1,21 @@
+/*
+ * kernel/power/checksums.h
+ *
+ * Copyright 2004-2006 Nigel Cunningham
+ *
+ * Distributed under GPLv2.
+ *
+ * Checksums.
+ */
+
+#ifdef CONFIG_SUSPEND2_CHECKSUMS
+extern void suspend_verify_checksums(void);
+extern void suspend_checksum_calculate_checksums(void);
+extern void suspend_checksum_print_differences(void);
+extern int suspend_allocate_checksum_pages(void);
+#else
+static inline void suspend_verify_checksums(void) { };
+static inline void suspend_checksum_calculate_checksums(void) { };
+static inline void suspend_checksum_print_differences(void) { };
+static inline int suspend_allocate_checksum_pages(void) { return 0; };
+#endif
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-03-27 16:04:08.464502500 -0500
@@ -0,0 +1,582 @@
+/*
+ * kernel/power/compression.c
+ *
+ * Copyright (C) 2003-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ * This file contains data compression routines for suspend,
+ * using cryptoapi.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "suspend2.h"
+#include "modules.h"
+#include "proc.h"
+#include "suspend2_common.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];
+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 if successful, -ENONEM otherwise.
+ */
+static int suspend_compress_crypto_prepare(void)
+{
+ if (!*suspend_compressor_name) {
+ printk("Suspend2: Compression enabled but no compressor name set.\n");
+ return 1;
+ }
+
+ if (!(suspend_compressor_transform = crypto_alloc_tfm(suspend_compressor_name, 0))) {
+ printk("Suspend2: Failed to initialise the %s compression transform.\n",
+ suspend_compressor_name);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * 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()))
+ 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_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_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. */
+ len = snprintf_used(buffer, size, "- Compressor %s enabled.\n",
+ suspend_compressor_name);
+ 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 PAGE_SIZE;
+}
+
+static unsigned long suspend_compress_storage_needed(void)
+{
+ return 2 * sizeof(unsigned long) + sizeof(int);
+}
+
+/*
+ * 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 proc entry.
+ */
+
+int suspend_expected_compression_ratio(void)
+{
+ if (suspend_compression_ops.disabled)
+ return 100;
+ else
+ return 100 - suspend_expected_compression;
+}
+
+static void suspend_compressor_disable_if_empty(void)
+{
+ suspend_compression_ops.disabled = !(*suspend_compressor_name);
+}
+
+static int suspend_compress_initialise(int starting_cycle)
+{
+ if (starting_cycle)
+ suspend_compressor_disable_if_empty();
+
+ return 0;
+}
+
+/*
+ * data for our proc entries.
+ */
+static struct suspend_proc_data proc_params[] = {
+ {
+ .filename = "expected_compression",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_INTEGER,
+ .data = {
+ .integer = {
+ .variable = &suspend_expected_compression,
+ .minimum = 0,
+ .maximum = 99,
+ }
+ }
+ },
+
+ {
+ .filename = "disable_compression",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_INTEGER,
+ .data = {
+ .integer = {
+ .variable = &suspend_compression_ops.disabled,
+ .minimum = 0,
+ .maximum = 1,
+ }
+ }
+ },
+
+ {
+ .filename = "compressor",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_STRING,
+ .data = {
+ .string = {
+ .variable = suspend_compressor_name,
+ .max_length = 31,
+ }
+ },
+ .write_proc = &suspend_compressor_disable_if_empty,
+ }
+};
+
+/*
+ * 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,
+
+ .initialise = suspend_compress_initialise,
+
+ .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(proc_params) / sizeof(struct suspend_proc_data);
+
+ printk("Suspend2 Compression Driver loading.\n");
+ if (!(result = suspend_register_module(&suspend_compression_ops))) {
+ for (i=0; i< numfiles; i++)
+ suspend_register_procfile(&proc_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_procfile(&proc_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-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/power/disk.c 2006-03-27 17:13:57.414295500 -0500
@@ -19,6 +19,7 @@
#include
#include
#include
+#include
#include "power.h"
@@ -84,7 +85,7 @@
if (!(error = swsusp_shrink_memory()))
return 0;
thaw:
- thaw_processes();
+ thaw_processes(FREEZER_ALL_THREADS);
enable_nonboot_cpus();
pm_restore_console();
return error;
@@ -93,7 +94,7 @@
static void unprepare_processes(void)
{
platform_finish();
- thaw_processes();
+ thaw_processes(FREEZER_ALL_THREADS);
enable_nonboot_cpus();
pm_restore_console();
}
@@ -130,7 +131,6 @@
if (in_suspend) {
device_resume();
pr_debug("PM: writing image.\n");
- error = swsusp_write();
if (!error)
power_down(pm_disk_mode);
else {
@@ -187,25 +187,6 @@
return 0;
}
- pr_debug("PM: Checking swsusp image.\n");
-
- if ((error = swsusp_check()))
- goto Done;
-
- pr_debug("PM: Preparing processes for restore.\n");
-
- if ((error = prepare_processes())) {
- swsusp_close();
- goto Done;
- }
-
- pr_debug("PM: Reading swsusp image.\n");
-
- if ((error = swsusp_read())) {
- swsusp_free();
- goto Thaw;
- }
-
pr_debug("PM: Preparing devices for restore.\n");
if ((error = device_suspend(PMSG_FREEZE))) {
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-03-27 16:04:08.480503500 -0500
@@ -0,0 +1,534 @@
+/*
+ * kernel/power/suspend_core/encryption.c
+ *
+ * Copyright (C) 2003-2006 Nigel Cunningham
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+
+#include "suspend2.h"
+#include "modules.h"
+#include "proc.h"
+#include "suspend2_common.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_tfm *suspend_encryptor_transform;
+static char suspend_encryptor_key[256];
+static int suspend_key_len;
+static char suspend_encryptor_iv[256];
+static int suspend_encryptor_mode;
+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;
+
+ 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;
+ }
+ }
+
+ 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_crypto_cleanup
+ *
+ * Description: Frees memory allocated for our labours.
+ */
+
+static int suspend_encrypt_rw_cleanup(int rw)
+{
+ if (suspend_encryptor_transform) {
+ crypto_free_tfm(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, -ENONEM otherwise.
+ */
+
+static int suspend_encrypt_crypto_prepare(int rw)
+{
+ if (!*suspend_encryptor_name) {
+ printk("Suspend2: Encryptor enabled but no name set.\n");
+ return 1;
+ }
+
+ if (!(suspend_encryptor_transform = crypto_alloc_tfm(suspend_encryptor_name,
+ 1 << suspend_encryptor_mode))) {
+ printk("Suspend2: Failed to initialise the encryption transform (%s, mode %d).\n",
+ suspend_encryptor_name, suspend_encryptor_mode);
+ return 1;
+ }
+
+ if (rw == READ)
+ bufofs = PAGE_SIZE;
+ else
+ bufofs = 0;
+
+ suspend_key_len = strlen(suspend_encryptor_key);
+
+ if (crypto_cipher_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;
+ }
+
+ if (rw != READ) {
+ crypto_cipher_set_iv(suspend_encryptor_transform,
+ suspend_encryptor_iv,
+ crypto_tfm_alg_ivsize(suspend_encryptor_transform));
+ }
+
+ 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_cipher_encrypt(suspend_encryptor_transform,
+ 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_crypto_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_cipher_decrypt(suspend_encryptor_transform,
+ 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;
+
+ len = snprintf_used(buffer, size, "- Encryptor %s enabled.\n",
+ suspend_encryptor_name);
+ 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 2 * sizeof(unsigned long) + sizeof(int);
+}
+
+/* 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_mode;
+ buf_offset++;
+
+ *(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_mode = (int) *(buffer + buf_offset);
+ buf_offset++;
+
+ 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;
+}
+
+static void suspend_encryptor_disable_if_empty(void)
+{
+ suspend_encryption_ops.disabled = !(*suspend_encryptor_name);
+}
+
+static int suspend_encrypt_initialise(int starting_cycle)
+{
+ if (starting_cycle)
+ suspend_encryptor_disable_if_empty();
+
+ return 0;
+}
+/*
+ * data for our proc entries.
+ */
+
+static struct suspend_proc_data proc_params[] = {
+ {
+ .filename = "encryptor",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_STRING,
+ .data = {
+ .string = {
+ .variable = suspend_encryptor_name,
+ .max_length = 31,
+ }
+ },
+ .write_proc = suspend_encryptor_disable_if_empty,
+ },
+
+ {
+ .filename = "encryption_mode",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_INTEGER,
+ .data = {
+ .integer = {
+ .variable = &suspend_encryptor_mode,
+ .minimum = 0,
+ .maximum = 3,
+ }
+ }
+ },
+
+ {
+ .filename = "encryption_save_key_and_iv",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_INTEGER,
+ .data = {
+ .integer = {
+ .variable = &suspend_encryptor_save_key_and_iv,
+ .minimum = 0,
+ .maximum = 1,
+ }
+ }
+ },
+
+ {
+ .filename = "encryption_key",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_STRING,
+ .data = {
+ .string = {
+ .variable = suspend_encryptor_key,
+ .max_length = 255,
+ }
+ }
+ },
+
+ {
+ .filename = "encryption_iv",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_STRING,
+ .data = {
+ .string = {
+ .variable = suspend_encryptor_iv,
+ .max_length = 255,
+ }
+ }
+ },
+
+ {
+ .filename = "disable_encryption",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_INTEGER,
+ .data = {
+ .integer = {
+ .variable = &suspend_encryption_ops.disabled,
+ .minimum = 0,
+ .maximum = 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,
+
+ .initialise = suspend_encrypt_initialise,
+
+ .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(proc_params) / sizeof(struct suspend_proc_data);
+
+ printk("Suspend2 Encryption Driver loading.\n");
+ if (!(result = suspend_register_module(&suspend_encryption_ops))) {
+ for (i=0; i< numfiles; i++)
+ suspend_register_procfile(&proc_params[i]);
+ } else
+ printk("Suspend2 Encryption Driver unable to register!\n");
+ return result;
+}
+
+late_initcall(suspend_encrypt_load);
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-03-27 16:04:08.480503500 -0500
@@ -0,0 +1,336 @@
+/*
+ * kernel/power/extent.c
+ *
+ * (C) 2003-2006 Nigel Cunningham
+ *
+ * Distributed under GPLv2.
+ *
+ * These functions encapsulate the manipulation of storage metadata. For
+ * pageflags, we use dynamically allocated bitmaps.
+ */
+
+#include
+#include
+#include "modules.h"
+#include "extent.h"
+#include "ui.h"
+
+int extents_allocated = 0;
+
+/* get_extent
+ *
+ * Returns a free extent. May fail, returning NULL instead.
+ */
+
+static struct extent *get_extent(void)
+{
+ struct extent *result;
+
+ if (!(result = kmalloc(sizeof(struct extent), GFP_ATOMIC)))
+ return NULL;
+
+ extents_allocated++;
+ result->minimum = result->maximum = 0;
+ result->next = NULL;
+
+ return result;
+}
+
+/* put_extent.
+ *
+ * Frees an extent. Assumes unlinking is done by the caller.
+ */
+void put_extent(struct extent *extent)
+{
+ BUG_ON(!extent);
+
+ kfree(extent);
+ extents_allocated--;
+}
+
+/* put_extent_chain.
+ *
+ * Frees a whole chain of extents.
+ */
+void put_extent_chain(struct extent_chain *chain)
+{
+ struct extent *this;
+
+ this = chain->first;
+
+ while(this) {
+ struct extent *next = this->next;
+ kfree(this);
+ chain->frees++;
+ 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;
+}
+
+/*
+ * add_to_extent_chain
+ *
+ * Add an extent to an existing chain.
+ */
+
+int 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++;
+ put_extent(to_free);
+ }
+
+ chain->last_touched = start_at;
+ chain->size+= (maximum - minimum + 1);
+
+ return 0;
+ }
+
+ new_extent = 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;
+}
+
+/* append_extent_to_extent_chain
+ *
+ * Used where we know a extent is to be added to the end of the list
+ * and does not need merging with the current last extent.
+ */
+
+int append_extent_to_extent_chain(struct extent_chain *chain,
+ unsigned long minimum, unsigned long maximum)
+{
+ struct extent *newextent = NULL;
+
+ newextent = get_extent();
+ if (!newextent) {
+ printk("Error unable to append a new extent to the chain.\n");
+ return 2;
+ }
+
+ chain->allocs++;
+ chain->size+= (maximum - minimum + 1);
+ newextent->minimum = minimum;
+ newextent->maximum = maximum;
+ newextent->next = NULL;
+
+ if (chain->last) {
+ chain->last->next = newextent;
+ chain->last = newextent;
+ } else
+ chain->last = chain->first = newextent;
+
+ return 0;
+}
+
+/* serialise_extent_chain
+ *
+ * Write a chain in the image.
+ */
+int serialise_extent_chain(struct extent_chain *chain)
+{
+ struct extent *this;
+ int ret, i = 1;
+
+ if ((ret = suspend_active_writer->rw_header_chunk(WRITE,
+ (char *) chain,
+ sizeof(struct extent_chain) - 2 * sizeof(struct extent *))))
+ return ret;
+
+ this = chain->first;
+ while (this) {
+ if ((ret = suspend_active_writer->rw_header_chunk(WRITE,
+ (char *) this,
+ 2 * sizeof(unsigned long))))
+ return ret;
+ this = this->next;
+ i++;
+ }
+ return ret;
+}
+
+/* load_extent_chain
+ *
+ * Read back a chain saved in the image.
+ */
+int load_extent_chain(struct extent_chain *chain)
+{
+ struct extent *this, *last = NULL;
+ int i, ret;
+
+ if (!(ret = suspend_active_writer->rw_header_chunk(READ,
+ (char *) chain,
+ sizeof(struct extent_chain) - 2 * sizeof(struct extent *))))
+ 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,
+ (char *) this,
+ 2 * sizeof(unsigned long))))
+ return ret;
+ if (last)
+ last->next = this;
+ else
+ chain->first = this;
+ last = this;
+ }
+ chain->last = last;
+ return ret;
+}
+
+/* extent_state_next
+ *
+ * Given a state, progress to the next valid entry. We may begin in an
+ * invalid state, as we do when invoked from 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 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;
+}
+
+/* extent_state_goto_start
+ *
+ * Find the first valid value in a group of chains.
+ */
+void extent_state_goto_start(struct extent_iterate_state *state)
+{
+ state->current_chain = -1;
+ state->current_extent = NULL;
+ state->current_offset = 0;
+}
+
+/* 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 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;
+ }
+}
+
+/* extent_start_restore
+ *
+ * Restore the position saved by extent_state_save.
+ */
+
+void 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) {
+ 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-03-27 16:04:08.484503750 -0500
@@ -0,0 +1,82 @@
+/*
+ * kernel/power/extent.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham
+ *
+ * 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.
+ *
+ */
+
+#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 extent_state_eof(state) ((state)->num_chains < (state)->current_chain)
+
+#define 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 extents_allocated;
+void put_extent(struct extent *extent);
+void put_extent_chain(struct extent_chain *chain);
+int append_extent_to_extent_chain(struct extent_chain *chain,
+ unsigned long minimum, unsigned long maximum);
+int add_to_extent_chain(struct extent_chain *chain,
+ unsigned long minimum, unsigned long maximum);
+int serialise_extent_chain(struct extent_chain *chain);
+int 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 extent_state_save(struct extent_iterate_state *state,
+ struct extent_iterate_saved_state *saved_state);
+void extent_state_restore(struct extent_iterate_state *state,
+ struct extent_iterate_saved_state *saved_state);
+void extent_state_goto_start(struct extent_iterate_state *state);
+unsigned long 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-03-27 16:04:08.488504000 -0500
@@ -0,0 +1,981 @@
+/*
+ * kernel/power/io.c
+ *
+ * Copyright (C) 1998-2001 Gabor Kuti
+ * Copyright (C) 1998,2001,2002 Pavel Machek
+ * Copyright (C) 2002-2003 Florent Chabaud
+ * Copyright (C) 2002-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ * It contains high level IO routines for suspending.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "version.h"
+#include "modules.h"
+#include "pageflags.h"
+#include "io.h"
+#include "ui.h"
+#include "suspend2_common.h"
+#include "suspend2.h"
+#include "storage.h"
+
+/* attempt_to_parse_resume_device
+ *
+ * Can we suspend, using the current resume2= parameter?
+ */
+int 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->disabled) {
+ printk(name_suspend
+ "Writer '%s' is disabled. Ignoring it.\n",
+ this_writer->name);
+ 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();
+ 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;
+ strncpy(sh->machine, system_utsname.machine, 65);
+ strncpy(sh->version, system_utsname.version, 65);
+ 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->disabled)
+ 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->disabled)
+ 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->disabled)
+ 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->disabled)
+ 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 current_page_index = -1, pc, step = 1, nextupdate = 0, i;
+ int result;
+ struct suspend_module_ops *first_filter = suspend_get_next_filter(NULL);
+
+ current_page_index = get_next_bit_on(*pageflags, -1);
+
+ pc = finish_at / 5;
+
+ /* Read the pages */
+ for (i=0; i< finish_at; i++) {
+ struct page *page = pfn_to_page(current_page_index);
+
+ /* 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.");
+ return result;
+ } else
+ panic("Failed to read chunk %d/%d of the image. (%d)",
+ i, finish_at, result);
+ }
+
+ /* Interactivity*/
+ check_shift_keys(0, NULL);
+
+ if (test_result_state(SUSPEND_ABORTED) && write)
+ return 1;
+
+ /* Prepare next */
+ current_page_index = get_next_bit_on(*pageflags,
+ current_page_index);
+ }
+
+ printk("done.\n");
+
+ suspend_update_status(base + finish_at, barmax, " %d/%d MB ",
+ MB(base + finish_at), MB(barmax));
+ 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) {
+
+ /* 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.disabled = this_module->disabled;
+ 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,
+ (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,
+ (char *) &len, sizeof(int));
+ if (len)
+ suspend_active_writer->rw_header_chunk(
+ WRITE, buffer, len);
+ }
+
+ /* Write a blank header to terminate the list */
+ suspend_module_header.name[0] = '\0';
+ suspend_active_writer->rw_header_chunk(WRITE,
+ (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->disabled = 1;
+
+ /* Get the first module header */
+ result = suspend_active_writer->rw_header_chunk(READ,
+ (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.disabled) &&
+ (suspend_module_header.type == FILTER_MODULE)) {
+ 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();
+ result = -EINVAL;
+ noresume_reset_modules();
+ 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,
+ (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, 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->disabled = suspend_module_header.disabled;
+ }
+
+ /* Get the next module header */
+ result = suspend_active_writer->rw_header_chunk(READ,
+ (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,
+ 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->machine, system_utsname.machine, 65))
+ return "Incorrect machine type.";
+
+ if (strncmp(sh->version, system_utsname.version, 65))
+ return "Right kernel version but wrong build number.";
+
+ if (sh->page_size != PAGE_SIZE)
+ return "Incorrect PAGE_SIZE.";
+
+ if ((sh->root_fs == current->fs->rootmnt->mnt_sb->s_dev) &&
+ (!test_suspend_state(SUSPEND_IGNORE_ROOTFS)))
+ return "Root filesystem has been mounted prior to trying 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)
+ return -ENOMEM;
+
+ /* Check for an image */
+ if (!(result = suspend_active_writer->image_exists())) {
+ result = -ENODATA;
+ noresume_reset_modules();
+ 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();
+ 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();
+ 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();
+ goto out;
+ }
+
+ /* Read suspend header */
+ if ((result = suspend_active_writer->rw_header_chunk(READ,
+ header_buffer, sizeof(struct suspend_header))) < 0) {
+ noresume_reset_modules();
+ 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();
+ 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;
+ suspend_default_console_level = 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;
+ goto out;
+ }
+
+ suspend_prepare_console();
+
+ check_shift_keys(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);
+
+ allocate_dyn_pageflags(&conflicting_pages_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);
+ relocate_dyn_pageflags(&conflicting_pages_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();
+ goto out_reset_console;
+ }
+
+ check_shift_keys(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();
+ goto out_reset_console;
+ }
+
+ /* Read the original kernel back */
+ check_shift_keys(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();
+ goto out_reset_console;
+ }
+
+ check_shift_keys(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);
+ free_dyn_pageflags(&conflicting_pages_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,
+ 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->machine,
+ suspend_header->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);
+ check_shift_keys(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-03-27 16:04:08.492504250 -0500
@@ -0,0 +1,45 @@
+/*
+ * kernel/power/io.h
+ *
+ * Copyright (C) 2005-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ * It contains high level IO routines for suspending.
+ *
+ */
+
+#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;
+ char machine[65];
+ char version[65];
+ 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 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;
diff -urN oldtree/kernel/power/main.c newtree/kernel/power/main.c
--- oldtree/kernel/power/main.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/power/main.c 2006-03-27 16:04:08.496504500 -0500
@@ -9,6 +9,7 @@
*/
#include
+#include
#include
#include
#include
@@ -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();
@@ -133,7 +134,7 @@
static void suspend_finish(suspend_state_t state)
{
device_resume();
- thaw_processes();
+ thaw_processes(FREEZER_ALL_THREADS);
enable_nonboot_cpus();
if (pm_ops && pm_ops->finish)
pm_ops->finish(state);
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-03-27 16:04:08.500504750 -0500
@@ -0,0 +1,313 @@
+/*
+ * kernel/power/modules.c
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham
+ *
+ */
+
+#include
+#include
+#include "suspend2.h"
+#include "modules.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->disabled)
+ continue;
+ if (this_module->storage_needed)
+ bytes += this_module->storage_needed();
+ }
+
+ return bytes;
+}
+
+/*
+ * 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->disabled)
+ 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->disabled)
+ continue;
+ if (this_module->print_debug_info) {
+ int result;
+ result = this_module->print_debug_info(buffer + len,
+ buffer_size - len);
+ len += result;
+ }
+ }
+
+ return len;
+}
+
+/*
+ * suspend_register_module
+ *
+ * Register a module.
+ */
+int suspend_register_module(struct suspend_module_ops *module)
+{
+ if (suspend_find_module_given_name(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) {
+ if (this_module->disabled)
+ 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->disabled)
+ 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->disabled)
+ 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-03-27 16:04:08.500504750 -0500
@@ -0,0 +1,155 @@
+/*
+ * kernel/power/modules.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham
+ *
+ * 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.
+ *
+ */
+
+/* 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 disabled;
+ 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. */
+ CHECKSUM_MODULE
+};
+
+enum {
+ SUSPEND_ASYNC,
+ SUSPEND_SYNC
+};
+
+struct suspend_module_ops {
+ /* Functions common to all modules */
+ int type;
+ char *name;
+ struct module *module;
+ int disabled;
+ 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);
+
+ /*
+ * 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, 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);
+}
+
+extern int suspend_expected_compression_ratio(void);
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-03-27 16:04:08.504505000 -0500
@@ -0,0 +1,372 @@
+/*
+ * kernel/power/netlink.c
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ * Functions for communicating with a userspace helper via netlink.
+ */
+
+
+#include
+#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;
+
+ 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-03-27 16:04:08.508505250 -0500
@@ -0,0 +1,47 @@
+/*
+ * kernel/power/netlink.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ * Declarations for functions for communicating with a userspace helper
+ * via netlink.
+ */
+
+#include
+#include
+
+#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;
+};
+
+void suspend_send_netlink_message(struct user_helper_data *uhd,
+ int type, void* params, size_t len);
+int suspend_netlink_setup(struct user_helper_data *uhd);
+void suspend_netlink_close(struct user_helper_data *uhd);
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-03-27 17:05:03.160906750 -0500
@@ -0,0 +1,360 @@
+/*
+ * kernel/power/pagedir.c
+ *
+ * Copyright (C) 1998-2001 Gabor Kuti
+ * Copyright (C) 1998,2001,2002 Pavel Machek
+ * Copyright (C) 2002-2003 Florent Chabaud
+ * Copyright (C) 2002-2006 Nigel Cunningham
+ *
+ * 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
+#include
+#include
+#include
+
+#include "pageflags.h"
+#include "ui.h"
+#include "pagedir.h"
+
+int extra_pagedir_pages_allocated;
+
+/* Not static so allocation routine can BUG if recursively called */
+dyn_pageflags_t conflicting_pages_map;
+
+#define PageConflicting(page) (test_dynpageflag(&conflicting_pages_map, page))
+#define SetPageConflicting(page) (set_dynpageflag(&conflicting_pages_map, page))
+#define ClearPageConflicting(page) (clear_dynpageflag(&conflicting_pages_map, page))
+
+/* suspend_free_extra_pagedir_memory
+ *
+ * Description: Free a previously pagedir metadata.
+ */
+void suspend_free_extra_pagedir_memory(void)
+{
+ unsigned long pagenumber;
+
+ free_dyn_pageflags(&pageset1_map);
+ free_dyn_pageflags(&pageset2_map);
+ free_dyn_pageflags(&pageset1_copy_map);
+
+ /* Free allocated pages */
+ if (allocd_pages_map) {
+ BITMAP_FOR_EACH_SET(allocd_pages_map, pagenumber) {
+ struct page *page = pfn_to_page(pagenumber);
+ ClearPageNosave(page);
+ __free_page(page);
+ extra_pagedir_pages_allocated--;
+ }
+ free_dyn_pageflags(&allocd_pages_map);
+ }
+}
+
+/* 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;
+
+ if (num_to_alloc < 1)
+ num_to_alloc = 0;
+
+ if (num_to_alloc) {
+ int num_added = 0;
+
+ if (order >= MAX_ORDER)
+ order = MAX_ORDER - 1;
+
+ while (num_added < num_to_alloc) {
+ struct page *newpage;
+ unsigned long virt;
+
+ 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;
+ return 1;
+ }
+
+ newpage = virt_to_page(virt);
+ for (j = 0; j < (1 << order); j++) {
+ SetPageNosave(newpage + j);
+ /* Pages will be freed one at a time. */
+ SetPageAllocd(newpage + j);
+ extra_pagedir_pages_allocated++;
+ }
+ num_added+= (1 << order);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * 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);
+
+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);
+ }
+ }
+ }
+
+ 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, i;
+
+ BUG_ON(in_atomic() && !irqs_disabled());
+
+ clear_dyn_pageflags(pageset2_map);
+
+ if (test_action_state(SUSPEND_NO_PAGESET2))
+ return;
+
+ /*
+ * Note that we don't clear the map to begin with!
+ * This is because if we eat memory, we loose track
+ * of LRU pages that are still in use but taken off
+ * the LRU. If I can figure out how the VM keeps
+ * track of them, I might be able to tweak this a
+ * little further and decrease pageset one's size
+ * further.
+ *
+ * (Memory grabbing clears the pageset2 flag on
+ * pages that are really freed!).
+ */
+
+ 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->mm || p->active_mm) && (p->flags & PF_NOFREEZE)) {
+ 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());
+
+ for_each_zone(zone) {
+ if (!zone->present_pages)
+ continue;
+ for (i = 0; i < zone->spanned_pages; i++) {
+ struct page *page = pfn_to_page(zone->zone_start_pfn + i);
+ BUG_ON(PagePageset2(page) && PageSlab(page));
+ }
+ }
+
+ BUG_ON(in_atomic() && !irqs_disabled());
+
+}
+
+/* suspend_get_nonconflicting_pages
+ *
+ * Description: Gets higher-order pages that won't be overwritten
+ * while copying the original pages.
+ *
+ * Note that if only one of the allocated pages overlaps
+ * with the pages that overlap, another set must be
+ * tried. Therefore, you shouldn't use this function
+ * much, and not with high orders.
+ */
+
+unsigned long suspend_get_nonconflicting_pages(const int order)
+{
+ struct page *page;
+ unsigned long new_page, i;
+ int more = 0;
+
+ do {
+ new_page = __get_free_pages(GFP_ATOMIC | __GFP_NOWARN, order);
+ if (!new_page)
+ return 0;
+ page = virt_to_page(new_page);
+ more = 0;
+ for (i = 0; i < (1UL << order); i++) {
+ if (PagePageset1(page + i)) {
+ more = 1;
+ break;
+ }
+ }
+ if (more) {
+ for (i = 0; i < (1UL << order); i++)
+ if (PagePageset1(page + i))
+ SetPageConflicting(page + i);
+ else {
+ __free_pages(page + i, 0);
+ }
+ }
+ }
+ while (more);
+
+ memset((void*)new_page, 0, PAGE_SIZE * (1<
+ *
+ * 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_pages(int order);
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-03-27 16:04:08.512505500 -0500
@@ -0,0 +1,143 @@
+/*
+ * kernel/power/pageflags.c
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ * Routines for serialising and relocating pageflags in which we
+ * store our image metadata.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#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 in_use_map;
+dyn_pageflags_t allocd_pages_map;
+#ifdef CONFIG_DEBUG_PAGEALLOC
+dyn_pageflags_t unmap_map;
+#endif
+dyn_pageflags_t checksum_map;
+
+static int num_zones(void)
+{
+ int result = 0;
+ struct zone *zone;
+
+ for_each_zone(zone)
+ result++;
+
+ return result;
+}
+
+static int pages_for_zone(struct zone *zone)
+{
+ return (zone->spanned_pages + (PAGE_SIZE << 3) - 1) /
+ (PAGE_SIZE << 3);
+}
+
+/* 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 = 0;
+ struct zone *zone;
+
+ if (!*pagemap)
+ return;
+
+ for_each_zone(zone) {
+ int size = pages_for_zone(zone);
+ suspend_active_writer->rw_header_chunk(WRITE, (char *) &zone_num, sizeof(int));
+ suspend_active_writer->rw_header_chunk(WRITE, (char *) &size, sizeof(int));
+
+ for (i = 0; i < size; i++)
+ suspend_active_writer->rw_header_chunk(WRITE, (char *) pagemap[zone_num][i], PAGE_SIZE);
+ zone_num++;
+ }
+ zone_num = -1;
+ suspend_active_writer->rw_header_chunk(WRITE, (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 = 0, zone_check = 0;
+ struct zone *zone;
+
+ if (!pagemap)
+ return;
+
+ for_each_zone(zone) {
+ int size = 0;
+ suspend_active_writer->rw_header_chunk(READ, (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, (char *) &size, sizeof(int));
+
+ for (i = 0; i < size; i++)
+ suspend_active_writer->rw_header_chunk(READ, (char *) pagemap[zone_num][i], PAGE_SIZE);
+ zone_num++;
+ }
+ suspend_active_writer->rw_header_chunk(READ, (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.
+ */
+
+extern int num_zones(void);
+
+void relocate_dyn_pageflags(dyn_pageflags_t *pagemap)
+{
+ int i, zone_num = 0;
+ struct zone *zone;
+
+ if (!*pagemap)
+ return;
+
+ suspend_relocate_if_required((void *) pagemap, sizeof (void *) * num_zones());
+
+ for_each_zone(zone) {
+ int pages = (zone->spanned_pages + (PAGE_SIZE << 3) - 1) >>
+ (PAGE_SHIFT + 3);
+
+ suspend_relocate_if_required((void *) &((*pagemap)[zone_num]), sizeof(void *) * pages);
+
+ for (i = 0; i < pages; i++)
+ suspend_relocate_if_required((void *) &((*pagemap)[zone_num][i]),
+ PAGE_SIZE);
+ zone_num++;
+ }
+}
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-03-27 16:04:08.516505750 -0500
@@ -0,0 +1,86 @@
+/*
+ * kernel/power/pageflags.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham
+ *
+ * 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
+#include
+
+extern dyn_pageflags_t in_use_map;
+extern dyn_pageflags_t allocd_pages_map;
+#ifdef CONFIG_DEBUG_PAGEALLOC
+extern dyn_pageflags_t unmap_map;
+#endif
+extern dyn_pageflags_t pageset2_map;
+extern dyn_pageflags_t conflicting_pages_map;
+extern dyn_pageflags_t checksum_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 PageAllocd(page) (test_dynpageflag(&allocd_pages_map, page))
+#define SetPageAllocd(page) (set_dynpageflag(&allocd_pages_map, page))
+#define ClearPageAllocd(page) (clear_dynpageflag(&allocd_pages_map, page))
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+#define PageUnmap(page) (test_dynpageflag(&unmap_map, page))
+#define SetPageUnmap(page) (set_dynpageflag(&unmap_map, page))
+#define ClearPageUnmap(page) (clear_dynpageflag(&unmap_map, page))
+#endif
+
+static inline int PageChecksumIgnore(struct page *page)
+{
+ return checksum_map ?
+ test_dynpageflag(&checksum_map, page) :
+ 0;
+}
+
+static inline void SetPageChecksumIgnore(struct page *page)
+{
+ if (checksum_map)
+ set_dynpageflag(&checksum_map, page);
+};
+
+static inline void ClearPageChecksumIgnore(struct page *page)
+{
+ if (checksum_map)
+ clear_dynpageflag(&checksum_map, page);
+};
+
+extern void save_dyn_pageflags(dyn_pageflags_t pagemap);
+extern void load_dyn_pageflags(dyn_pageflags_t pagemap);
+void relocate_dyn_pageflags(dyn_pageflags_t *pagemap);
+
diff -urN oldtree/kernel/power/power.h newtree/kernel/power/power.h
--- oldtree/kernel/power/power.h 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/power/power.h 2006-03-27 16:14:19.890714250 -0500
@@ -1,6 +1,8 @@
#include
#include
+#include "suspend.h"
+
struct swsusp_info {
struct new_utsname uts;
u32 version_code;
@@ -35,9 +37,6 @@
extern struct subsystem power_subsys;
-/* References to section boundaries */
-extern const void __nosave_begin, __nosave_end;
-
extern struct pbe *pagedir_nosave;
/* Preferred image size in bytes (default 500 MB) */
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-03-27 16:04:08.524506250 -0500
@@ -0,0 +1,78 @@
+/*
+ * kernel/power/power_off.c
+ *
+ * Copyright (C) 2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ * Support for powering down.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "suspend2_common.h"
+#include "suspend2.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;
+ }
+ }
+
+ 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);
+ }
+
+ if (pm_ops && pm_ops->enter && suspend_powerdown_method && try_pm_state_powerdown())
+ return;
+
+ kernel_power_off();
+ 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-03-27 16:04:08.524506250 -0500
@@ -0,0 +1,13 @@
+/*
+ * kernel/power/power_off.h
+ *
+ * Copyright (C) 2006 Nigel Cunningham
+ *
+ * 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-03-27 17:08:10.404608750 -0500
@@ -0,0 +1,750 @@
+/*
+ * kernel/power/prepare_image.c
+ *
+ * Copyright (C) 2003-2006 Nigel Cunningham
+ *
+ * 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
+#include
+#include
+
+#include "suspend2.h"
+#include "pageflags.h"
+#include "modules.h"
+#include "suspend2_common.h"
+#include "io.h"
+#include "ui.h"
+#include "extent.h"
+#include "prepare_image.h"
+#include "checksum.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 = 100;
+
+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;
+}
+
+long real_nr_free_pages(void)
+{
+ return nr_free_pages() + num_pcp_pages();
+}
+
+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 = orig_num_free - final + 100;
+}
+
+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 ? : 100) / 100);
+}
+
+static int header_storage_needed(void)
+{
+ unsigned long bytes = ((extents_allocated * 2 * sizeof(unsigned long)) +
+ sizeof(struct suspend_header) +
+ sizeof(struct suspend_module_header) +
+ (int) suspend_header_storage_for_modules() +
+ (dyn_pageflags_pages_per_bitmap() << PAGE_SHIFT) +
+ suspend_num_modules *
+ (sizeof(struct suspend_module_header) + sizeof(int)));
+
+ return ((int) ((bytes + (int) PAGE_SIZE - 1) >> PAGE_SHIFT));
+}
+
+static void display_stats(int always, int sub_extra_pd1_allow)
+{
+ unsigned long storage_allocated = suspend_active_writer->storage_allocated();
+ 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 i, order, loop, cpu;
+ struct page *page;
+ unsigned long flags;
+ 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);
+ ClearPagePageset2(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);
+ ClearPagePageset2(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 =
+ zone->spanned_pages - 1;
+
+ while (posn < last_in_zone && !PageInUse(posn))
+ posn++;
+ return (posn - page);
+}
+
+static struct page *rotext_start, *rotext_end;
+static struct page *nosave_start, *nosave_end;
+#ifdef CONFIG_DEBUG_RODATA
+static struct page *rtas_start, *rtas_end;
+static struct page *rodata_start, *rodata_end;
+extern char __start_rodata, __end_rodata;
+#endif
+#ifdef CONFIG_PPC_RTAS
+extern unsigned int rtas_data, rtas_size;
+#endif
+#ifdef CONFIG_PPC
+extern char _etext[];
+#else
+extern char _text[], _etext[];
+#endif
+
+#ifdef CONFIG_X86_32 /* 2.6.15 and later */
+extern int bad_ppro;
+
+/*
+ * Copied from arch/i386/mm/init.c. It should be moved to
+ * an include file after testing.
+ */
+static inline int page_kills_ppro(unsigned long pagenr)
+{
+ if (pagenr >= 0x70000 && pagenr <= 0x7003F)
+ return 1;
+ return 0;
+}
+
+#else
+#define bad_ppro (0)
+#define page_kills_ppro(pfn) (0)
+#endif
+
+static __init int page_nosave_init(void)
+{
+#ifdef CONFIG_DEBUG_RODATA
+ rodata_start = virt_to_page(&__start_rodata);
+ rodata_end = virt_to_page(&__end_rodata);
+#endif
+#ifdef CONFIG_PPC
+ rotext_start = virt_to_page(PAGE_OFFSET);
+#else
+ rotext_start = virt_to_page(&_text);
+#endif
+ rotext_end = virt_to_page(&_etext);
+
+ nosave_start = virt_to_page(&__nosave_begin);
+ nosave_end = virt_to_page(((char *) &__nosave_end) - 1);
+
+#ifdef CONFIG_PPC_RTAS
+ rtas_start = virt_to_page(__va(rtas_data));
+ rtas_end = virt_to_page(__va(rtas_data) + rtas_size);
+#endif
+ 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 chunk_size, 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);
+ clear_dyn_pageflags(pageset1_copy_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) {
+ for (loop = 0; loop < zone->spanned_pages; loop++) {
+ unsigned long pfn = zone->zone_start_pfn + loop;
+ struct page *page = pfn_to_page(pfn);
+
+ if (
+#if 0
+#ifdef CONFIG_DEBUG_RODATA
+ (page >= rodata_start && page <= rodata_end) ||
+#endif
+#ifdef CONFIG_DEBUG_ROTEXT
+ (page >= rotext_start && page <= rotext_end) ||
+#endif
+#ifdef CONFIG_PPC_RTAS
+ (page >= rtas_start && page <= rtas_end) ||
+#endif
+ !pfn_valid(pfn) ||
+ (bad_ppro && page_kills_ppro(pfn)) ||
+ (checksum_map && PageChecksumIgnore(page)) ||
+ !page_is_ram(pfn)) {
+#endif
+ (page >= nosave_start && page <= nosave_end) ||
+ PageAllocd(page)) {
+ num_nosave++;
+ continue;
+ }
+ if (!PageReserved(page)) {
+ if ((chunk_size=size_of_free_region(page))!=0) {
+ num_free += chunk_size;
+ loop += chunk_size - 1;
+ continue;
+ }
+ } else {
+ if (PageHighMem(page)) {
+ /* HighMem pages may be marked Reserved. We ignore them. */
+ num_nosave++;
+ 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);
+ BITMAP_FOR_EACH_SET(allocd_pages_map, loop)
+ SetPagePageset1Copy(pfn_to_page(loop));
+ 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_stats
+ *
+ * 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_stats(int storage_unavailable)
+{
+ struct pageset_sizes_result result;
+
+ suspend_mark_pages_for_pageset2(); /* Need to call this before getting pageset1_size! */
+ 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 (!storage_unavailable) {
+ storage_available = suspend_active_writer->storage_available();
+ display_stats(0, 0);
+ }
+ BUG_ON(in_atomic() && !irqs_disabled());
+ return;
+}
+
+/* update_image
+ *
+ * Allocate [more] memory and storage for the image.
+ */
+static int update_image(void)
+{
+ int result2, param_used;
+
+ suspend_recalculate_stats(0);
+
+ if (suspend_allocate_checksum_pages()) {
+ suspend_message(SUSPEND_ANY_SECTION, SUSPEND_LOW, 1,
+ "Still need to get more pages for checksum pages.\n");
+ return 1;
+ }
+
+ /* 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();
+ if (freeze_processes()) {
+ set_result_state(SUSPEND_FREEZING_FAILED);
+ set_result_state(SUSPEND_ABORTED);
+ }
+ return 1;
+ }
+
+ param_used = header_storage_needed();
+ if ((result2 = suspend_active_writer->allocate_header_space(param_used))) {
+ suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_LOW, 1,
+ "Still need to get more storage space for header.\n");
+ if (freeze_processes()) {
+ set_result_state(SUSPEND_FREEZING_FAILED);
+ set_result_state(SUSPEND_ABORTED);
+ }
+ storage_allocated = suspend_active_writer->storage_allocated();
+ return 1;
+ }
+
+ header_space_allocated = param_used;
+
+ /*
+ * 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();
+
+ if (freeze_processes()) {
+ set_result_state(SUSPEND_FREEZING_FAILED);
+ set_result_state(SUSPEND_ABORTED);
+ }
+
+ suspend_recalculate_stats(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)));
+
+ check_shift_keys(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), 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 orig_memory_still_to_eat, last_amount_needed = 0, times_criteria_met = 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_stats(0);
+
+ orig_memory_still_to_eat = amount_needed(1);
+ last_amount_needed = orig_memory_still_to_eat;
+
+ switch (image_size_limit) {
+ case -1: /* Don't eat any memory */
+ if (orig_memory_still_to_eat) {
+ set_result_state(SUSPEND_ABORTED);
+ set_result_state(SUSPEND_WOULD_EAT_MEMORY);
+ }
+ break;
+ case -2: /* Free caches only */
+ free_flags = GFP_NOIO | __GFP_HIGHMEM;
+ break;
+ default:
+ free_flags = GFP_ATOMIC | __GFP_HIGHMEM;
+ }
+
+ thaw_processes(FREEZER_KERNEL_THREADS);
+
+ /* -- Stage 2: Eat memory -- */
+
+ while (((amount_needed(1) > 0) || (image_size_limit == -2)) &&
+ (!test_result_state(SUSPEND_ABORTED)) &&
+ (times_criteria_met < 10)) {
+ int amount_freed;
+ int amount_wanted = orig_memory_still_to_eat - amount_needed(1);
+
+ suspend_prepare_status(CLEAR_BAR, "Seeking to free %dMB of memory.", MB(amount_needed(1)));
+
+ if (amount_wanted < 1)
+ amount_wanted = 1; /* image_size_limit == -2 */
+
+ if (orig_memory_still_to_eat)
+ suspend_update_status(orig_memory_still_to_eat - amount_needed(1),
+ orig_memory_still_to_eat,
+ " Image size %d ",
+ MB(storage_needed(1, 0)));
+ else
+ suspend_update_status(0, 1, "Image size %d ",
+ MB(storage_needed(1, 0)));
+
+ if ((last_amount_needed - amount_needed(1)) < 10)
+ times_criteria_met++;
+ else
+ times_criteria_met = 0;
+ last_amount_needed = amount_needed(1);
+ amount_freed = shrink_all_memory(last_amount_needed);
+ suspend_recalculate_stats(0);
+
+ did_eat_memory = 1;
+
+ check_shift_keys(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_stats(0);
+ }
+
+ /* Blank out image size display */
+ suspend_update_status(100, 100, NULL);
+
+ if (!test_result_state(SUSPEND_ABORTED)) {
+ /* Include image size limit when checking what to report */
+ if (amount_needed(1) - extra_pd1_pages_allowance > 0)
+ set_result_state(SUSPEND_UNABLE_TO_FREE_ENOUGH_MEMORY);
+
+ /* But don't include it when deciding whether to abort (soft limit) */
+ if ((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);
+ }
+
+ check_shift_keys(1, "Memory eating completed.");
+ }
+
+ return 0;
+}
+
+/* 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();
+
+ check_shift_keys(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);
+ }
+
+ check_shift_keys(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-03-27 16:22:46.418370250 -0500
@@ -0,0 +1,31 @@
+/*
+ * kernel/power/prepare_image.h
+ *
+ * Copyright (C) 2003-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ */
+
+extern int suspend_prepare_image(void);
+extern void suspend_recalculate_stats(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);
+#endif
+
+#define MIN_FREE_RAM 2000
+
+extern long extra_pd1_pages_allowance;
+extern long storage_needed(int use_ecr, int ignore_extra_p1_allowance);
+extern long ram_to_suspend(void);
diff -urN oldtree/kernel/power/proc.c newtree/kernel/power/proc.c
--- oldtree/kernel/power/proc.c 1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/proc.c 2006-03-27 16:04:08.532506750 -0500
@@ -0,0 +1,306 @@
+/*
+ * kernel/power/proc.c
+ *
+ * Copyright (C) 2002-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ * This file contains support for proc entries for tuning Suspend2.
+ *
+ * We have a generic handler that deals with the most common cases, and
+ * hooks for special handlers to use.
+ */
+
+#include
+#include
+#include
+
+#include "proc.h"
+#include "suspend2.h"
+#include "storage.h"
+
+static int suspend_proc_initialised = 0;
+
+static struct list_head suspend_proc_entries;
+static struct proc_dir_entry *suspend_dir;
+static struct suspend_proc_data proc_params[];
+
+extern void __suspend_try_resume(void);
+extern void suspend_main(void);
+
+/* suspend_read_proc
+ *
+ * Generic handling for reading the contents of bits, integers,
+ * unsigned longs and strings.
+ */
+static int suspend_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+ struct suspend_proc_data *proc_data = (struct suspend_proc_data *) data;
+
+ if (suspend_start_anything(0))
+ return -EBUSY;
+
+ if (proc_data->needs_storage_manager & 1)
+ suspend_prepare_usm();
+
+ switch (proc_data->type) {
+ case SUSPEND_PROC_DATA_CUSTOM:
+ if (proc_data->data.special.read_proc) {
+ read_proc_t *read_proc = proc_data->data.special.read_proc;
+ len = read_proc(page, start, off, count, eof, data);
+ } else
+ len = 0;
+ break;
+ case SUSPEND_PROC_DATA_BIT:
+ len = sprintf(page, "%d\n",
+ -test_bit(proc_data->data.bit.bit,
+ proc_data->data.bit.bit_vector));
+ break;
+ case SUSPEND_PROC_DATA_INTEGER:
+ {
+ int *variable = proc_data->data.integer.variable;
+ len = sprintf(page, "%d\n", *variable);
+ break;
+ }
+ case SUSPEND_PROC_DATA_UL:
+ {
+ long *variable = proc_data->data.ul.variable;
+ len = sprintf(page, "%lu\n", *variable);
+ break;
+ }
+ case SUSPEND_PROC_DATA_STRING:
+ {
+ char *variable = proc_data->data.string.variable;
+ len = sprintf(page, "%s\n", variable);
+ break;
+ }
+ }
+ /* Side effect routine? */
+ if (proc_data->read_proc)
+ proc_data->read_proc();
+
+ if (len <= count)
+ *eof = 1;
+
+ if (proc_data->needs_storage_manager & 1)
+ suspend_cleanup_usm();
+
+ suspend_finish_anything(0);
+
+ return len;
+}
+
+/* suspend_write_proc
+ *
+ * Generic routine for handling writing to files representing
+ * bits, integers and unsigned longs.
+ */
+
+static int suspend_write_proc(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ struct suspend_proc_data *proc_data = (struct suspend_proc_data *) data;
+ char *my_buf = (char *) get_zeroed_page(GFP_ATOMIC);
+ int result = count, assigned_temp_buffer = 0;
+
+ if (!my_buf)
+ return -ENOMEM;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+
+ if (copy_from_user(my_buf, buffer, count))
+ return -EFAULT;
+
+ if (suspend_start_anything(proc_data == &proc_params[0]))
+ return -EBUSY;
+
+ my_buf[count] = 0;
+
+ if (proc_data->needs_storage_manager & 2)
+ suspend_prepare_usm();
+
+ switch (proc_data->type) {
+ case SUSPEND_PROC_DATA_CUSTOM:
+ if (proc_data->data.special.write_proc) {
+ write_proc_t *write_proc = proc_data->data.special.write_proc;
+ result = write_proc(file, buffer, count, data);
+ }
+ break;
+ case SUSPEND_PROC_DATA_BIT:
+ {
+ int value = simple_strtoul(my_buf, NULL, 0);
+ if (value)
+ set_bit(proc_data->data.bit.bit,
+ (proc_data->data.bit.bit_vector));
+ else
+ clear_bit(proc_data->data.bit.bit,
+ (proc_data->data.bit.bit_vector));
+ }
+ break;
+ case SUSPEND_PROC_DATA_INTEGER:
+ {
+ int *variable = proc_data->data.integer.variable;
+ int minimum = proc_data->data.integer.minimum;
+ int maximum = proc_data->data.integer.maximum;
+ *variable = simple_strtol(my_buf, NULL, 0);
+ if (((*variable) < minimum))
+ *variable = minimum;
+
+ if (((*variable) > maximum))
+ *variable = maximum;
+ break;
+ }
+ case SUSPEND_PROC_DATA_UL:
+ {
+ unsigned long *variable = proc_data->data.ul.variable;
+ unsigned long minimum = proc_data->data.ul.minimum;
+ unsigned long maximum = proc_data->data.ul.maximum;
+ *variable = simple_strtoul(my_buf, NULL, 0);
+
+ if (minimum && ((*variable) < minimum))
+ *variable = minimum;
+
+ if (maximum && ((*variable) > maximum))
+ *variable = maximum;
+ break;
+ }
+ break;
+ case SUSPEND_PROC_DATA_STRING:
+ {
+ int copy_len = count;
+ char *variable =
+ proc_data->data.string.variable;
+
+ if (proc_data->data.string.max_length &&
+ (copy_len > proc_data->data.string.max_length))
+ copy_len = proc_data->data.string.max_length;
+
+ if (!variable) {
+ proc_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;
+ }
+ free_page((unsigned long) my_buf);
+ /* Side effect routine? */
+ if (proc_data->write_proc)
+ proc_data->write_proc();
+
+ /* Free temporary buffers */
+ if (assigned_temp_buffer) {
+ free_page((unsigned long) proc_data->data.string.variable);
+ proc_data->data.string.variable = NULL;
+ }
+
+ if (proc_data->needs_storage_manager & 2)
+ suspend_cleanup_usm();
+
+ suspend_finish_anything(proc_data == &proc_params[0]);
+
+ return result;
+}
+
+/* Non-module proc 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_proc's test so that
+ * suspend_start_anything still gets a 1 when the user echos > do_suspend!
+ */
+
+static struct suspend_proc_data proc_params[] = {
+ { .filename = "do_suspend",
+ .permissions = PROC_WRITEONLY,
+ .type = SUSPEND_PROC_DATA_CUSTOM,
+ .write_proc = suspend_main,
+ .needs_storage_manager = 2,
+ },
+
+ { .filename = "do_resume",
+ .permissions = PROC_WRITEONLY,
+ .type = SUSPEND_PROC_DATA_CUSTOM,
+ .write_proc = __suspend_try_resume,
+ .needs_storage_manager = 2,
+ },
+};
+
+/* suspend_initialise_proc
+ *
+ * Initialise the /proc/suspend2 directory.
+ */
+
+static void suspend_initialise_proc(void)
+{
+ int i;
+ int numfiles = sizeof(proc_params) / sizeof(struct suspend_proc_data);
+
+ if (suspend_proc_initialised)
+ return;
+
+ suspend_dir = proc_mkdir("suspend2", NULL);
+
+ BUG_ON(!suspend_dir);
+
+ INIT_LIST_HEAD(&suspend_proc_entries);
+
+ suspend_proc_initialised = 1;
+
+ for (i=0; i< numfiles; i++)
+ suspend_register_procfile(&proc_params[i]);
+}
+
+/* suspend_register_procfile
+ *
+ * Helper for registering a new /proc/suspend2 entry.
+ */
+
+struct proc_dir_entry *suspend_register_procfile(
+ struct suspend_proc_data *suspend_proc_data)
+{
+ struct proc_dir_entry *new_entry;
+
+ if (!suspend_proc_initialised)
+ suspend_initialise_proc();
+
+ new_entry = create_proc_entry(
+ suspend_proc_data->filename,
+ suspend_proc_data->permissions,
+ suspend_dir);
+ if (new_entry) {
+ list_add_tail(&suspend_proc_data->proc_data_list, &suspend_proc_entries);
+ new_entry->read_proc = suspend_read_proc;
+ new_entry->write_proc = suspend_write_proc;
+ new_entry->data = suspend_proc_data;
+ } else {
+ printk("Error! create_proc_entry returned NULL.\n");
+ INIT_LIST_HEAD(&suspend_proc_data->proc_data_list);
+ }
+ return new_entry;
+}
+
+/* suspend_unregister_procfile
+ *
+ * Helper for removing unwanted /proc/suspend2 entries.
+ *
+ */
+void suspend_unregister_procfile(struct suspend_proc_data *suspend_proc_data)
+{
+ if (list_empty(&suspend_proc_data->proc_data_list))
+ return;
+
+ remove_proc_entry(
+ suspend_proc_data->filename,
+ suspend_dir);
+ list_del(&suspend_proc_data->proc_data_list);
+}
diff -urN oldtree/kernel/power/proc.h newtree/kernel/power/proc.h
--- oldtree/kernel/power/proc.h 1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/power/proc.h 2006-03-27 16:04:08.536507000 -0500
@@ -0,0 +1,75 @@
+/*
+ * kernel/power/proc.h
+ *
+ * Copyright (C) 2004-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ * It provides declarations for suspend to use in managing
+ * /proc/suspend2. When we switch to kobjects,
+ * this will become redundant.
+ *
+ */
+
+#include
+
+struct suspend_proc_data {
+ char *filename;
+ int permissions;
+ 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 {
+ read_proc_t *read_proc;
+ write_proc_t *write_proc;
+ void *data;
+ } special;
+ } data;
+
+ /* Side effects routines. Used, eg, for reparsing the
+ * resume2 entry when it changes */
+ void (*read_proc) (void);
+ void (*write_proc) (void);
+ struct list_head proc_data_list;
+};
+
+enum {
+ SUSPEND_PROC_DATA_NONE,
+ SUSPEND_PROC_DATA_CUSTOM,
+ SUSPEND_PROC_DATA_BIT,
+ SUSPEND_PROC_DATA_INTEGER,
+ SUSPEND_PROC_DATA_UL,
+ SUSPEND_PROC_DATA_STRING
+};
+
+#define PROC_WRITEONLY 0200
+#define PROC_READONLY 0400
+#define PROC_RW 0600
+
+struct proc_dir_entry *suspend_register_procfile(
+ struct suspend_proc_data *suspend_proc_data);
+void suspend_unregister_procfile(struct suspend_proc_data *suspend_proc_data);
+
diff -urN oldtree/kernel/power/process.c newtree/kernel/power/process.c
--- oldtree/kernel/power/process.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/power/process.c 2006-03-27 16:19:30.458123500 -0500
@@ -1,165 +1,423 @@
/*
- * drivers/power/process.c - Functions for starting/stopping processes on
- * suspend transitions.
+ * kernel/power/process.c
*
- * Originally from swsusp.
+ * Copyright (C) 1998-2001 Gabor Kuti
+ * Copyright (C) 1998,2001,2002 Pavel Machek
+ * Copyright (C) 2002-2003 Florent Chabaud
+ * Copyright (C) 2002-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ *
+ * Freeze_and_free contains the routines software suspend uses to freeze other
+ * processes during the suspend cycle and to (if necessary) free up memory in
+ * accordance with limitations on the image size.
+ *
+ * Ideally, the image saved to disk would be an atomic copy of the entire
+ * contents of all RAM and related hardware state. One of the first
+ * prerequisites for getting our approximation of this is stopping the activity
+ * of other processes. We can't stop all other processes, however, since some
+ * are needed in doing the I/O to save the image. Freeze_and_free.c contains
+ * the routines that control suspension and resuming of these processes.
+ *
+ * Under high I/O load, we need to be careful about the order in which we
+ * freeze processes. If we freeze processes in the wrong order, we could
+ * deadlock others. The freeze_order array this specifies the order in which
+ * critical processes are frozen. All others are suspended after these have
+ * entered the refrigerator.
+ *
+ * Another complicating factor is that freeing memory requires the processes
+ * to not be frozen, but at the end of freeing memory, they need to be frozen
+ * so that we can be sure we actually have eaten enough memory. This is why
+ * freezing and freeing are in the one file. The freezer is not called from
+ * the main logic, but indirectly, via the code for eating memory. The eat
+ * memory logic is iterative, first freezing processes and checking the stats,
+ * then (if necessary) unfreezing them and eating more memory until it looks
+ * like the criteria are met (at which point processes are frozen & stats
+ * checked again).
*/
-
-#undef DEBUG
-
-#include
-#include
#include
+#include
#include
+#include
+#include
+#include
+#include
#include
+#include
+
+unsigned long freezer_state = 0;
+
+#if 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
+
+/* Timeouts when freezing */
+#define FREEZER_TOTAL_TIMEOUT (5 * HZ)
+#define FREEZER_CHECK_TIMEOUT (HZ / 10)
+
+DECLARE_COMPLETION(kernelspace_thaw);
+DECLARE_COMPLETION(userspace_thaw);
+static atomic_t nr_userspace_frozen;
+static atomic_t nr_kernelspace_frozen;
+
+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);
+ }
+}
/*
- * Timeout for stopping processes
+ * Done after userspace is frozen, so there should be no danger of
+ * fses being unmounted while we're in here.
*/
-#define TIMEOUT (20 * HZ)
+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);
+ 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);
-static inline int freezeable(struct task_struct * p)
+ return 0;
+}
+
+/*
+ * freezeable
+ *
+ * Description: Determine whether a process should be frozen yet.
+ * Parameters: struct task_struct * The process to consider.
+ * int Boolean - 0 = userspace else all.
+ * Returns: int 0 if don't freeze yet, otherwise do.
+ */
+static int freezeable(struct task_struct * p, int all_freezable)
{
if ((p == current) ||
+ (p->flags & PF_FROZEN) ||
(p->flags & PF_NOFREEZE) ||
(p->exit_state == EXIT_ZOMBIE) ||
(p->exit_state == EXIT_DEAD) ||
(p->state == TASK_STOPPED) ||
- (p->state == TASK_TRACED))
+ (p->state == TASK_TRACED) ||
+ (!p->mm && !all_freezable))
return 0;
return 1;
}
-/* Refrigerator is place where frozen processes are stored :-). */
-void refrigerator(void)
+static void __freeze_process(struct completion *completion_handler,
+ atomic_t *nr_frozen)
{
- /* Hmm, should we be allowed to suspend when there are realtime
- processes around? */
long save;
+
+ freezer_message("%s (%d) frozen.\n",
+ current->comm, current->pid);
save = current->state;
- pr_debug("%s entered refrigerator\n", current->comm);
- printk("=");
+
+ atomic_inc(nr_frozen);
+ wait_for_completion(completion_handler);
+ atomic_dec(nr_frozen);
+
+ current->state = save;
+ freezer_message("%s (%d) leaving freezer.\n",
+ current->comm, current->pid);
+}
+
+/*
+ * Refrigerator
+ */
+void refrigerator(void)
+{
+ unsigned long flags;
+
+ might_sleep();
+ /* Locking to handle race against waking the process in
+ * freeze threads. */
+ spin_lock_irqsave(¤t->sighand->siglock, flags);
frozen_process(current);
- spin_lock_irq(¤t->sighand->siglock);
- recalc_sigpending(); /* We sent fake signal, clean it up */
- spin_unlock_irq(¤t->sighand->siglock);
-
- while (frozen(current)) {
- current->state = TASK_UNINTERRUPTIBLE;
- schedule();
+
+ recalc_sigpending();
+ spin_unlock_irqrestore(¤t->sighand->siglock, flags);
+
+ if (test_freezer_state(FREEZER_ON)) {
+ if (current->mm)
+ __freeze_process(&userspace_thaw, &nr_userspace_frozen);
+ else
+ __freeze_process(&kernelspace_thaw,
+ &nr_kernelspace_frozen);
}
- pr_debug("%s left refrigerator\n", current->comm);
- current->state = save;
+
+ spin_lock_irqsave(¤t->sighand->siglock, flags);
+ recalc_sigpending();
+ current->flags &= ~PF_FROZEN;
+ spin_unlock_irqrestore(¤t->sighand->siglock, flags);
+
+ return;
}
-static inline void freeze_process(struct task_struct *p)
+void thaw_processes(int do_all_threads)
{
+ if (do_all_threads) {
+ clear_freezer_state(FREEZER_ON);
+ clear_freezer_state(ABORT_FREEZING);
+ }
+
+ complete_all(&kernelspace_thaw);
+ while (atomic_read(&nr_kernelspace_frozen) > 0)
+ yield();
+
+ init_completion(&kernelspace_thaw);
+ freezer_make_fses_rw();
+
+ if (do_all_threads) {
+ complete_all(&userspace_thaw);
+ while (atomic_read(&nr_userspace_frozen) > 0)
+ yield();
+ init_completion(&userspace_thaw);
+ }
+}
+
+/*
+ * num_freezeable
+ *
+ * Description: Determine how many processes of our type are still to be
+ * frozen. As a side effect, update the progress bar too.
+ * Parameters: int Which type we are trying to freeze.
+ * int Whether we are displaying our progress.
+ */
+static int num_freezeable(int do_all_threads) {
+
+ struct task_struct *g, *p;
+ int todo_this_type = 0;
+
+ read_lock(&tasklist_lock);
+ do_each_thread(g, p) {
+ if (freezeable(p, do_all_threads))
+ todo_this_type++;
+ } while_each_thread(g, p);
+ read_unlock(&tasklist_lock);
+
+ return todo_this_type;
+}
+
+/*
+ * num_uninterruptible
+ *
+ * Description: Determine how many processes of our type are in state
+ * task uninterruptible.
+ * Parameters: int Which type we are trying to freeze.
+ */
+static int num_uninterruptible(int do_all_threads) {
+
+ struct task_struct *g, *p;
+ int count = 0;
+
+ read_lock(&tasklist_lock);
+ do_each_thread(g, p) {
+ if (freezeable(p, do_all_threads) &&
+ p->state == TASK_UNINTERRUPTIBLE)
+ count++;
+ } while_each_thread(g, p);
+ read_unlock(&tasklist_lock);
+
+ return count;
+}
+
+/*
+ * Tell threads of the type to enter the freezer.
+ */
+static void signal_threads(int do_all_threads)
+{
+ struct task_struct *g, *p;
unsigned long flags;
- if (!freezing(p)) {
+ read_lock(&tasklist_lock);
+ do_each_thread(g, p) {
+ if (!freezeable(p, do_all_threads))
+ continue;
+
freeze(p);
spin_lock_irqsave(&p->sighand->siglock, flags);
signal_wake_up(p, 0);
spin_unlock_irqrestore(&p->sighand->siglock, flags);
- }
+ } while_each_thread(g, p);
+ read_unlock(&tasklist_lock);
}
-/* 0 = success, else # of processes that we failed to stop */
-int freeze_processes(void)
+/*
+ * Prod processes that haven't entered the refrigerator yet.
+ */
+static void prod_processes(int do_all_threads)
{
- int todo, nr_user, user_frozen;
- unsigned long start_time;
struct task_struct *g, *p;
unsigned long flags;
- printk( "Stopping tasks: " );
- start_time = jiffies;
- user_frozen = 0;
- do {
- nr_user = todo = 0;
- read_lock(&tasklist_lock);
- do_each_thread(g, p) {
- if (!freezeable(p))
- continue;
- if (frozen(p))
- continue;
- if (p->mm && !(p->flags & PF_BORROWED_MM)) {
- /* The task is a user-space one.
- * Freeze it unless there's a vfork completion
- * pending
- */
- if (!p->vfork_done)
- freeze_process(p);
- nr_user++;
- } else {
- /* Freeze only if the user space is frozen */
- if (user_frozen)
- freeze_process(p);
- todo++;
- }
- } while_each_thread(g, p);
- read_unlock(&tasklist_lock);
- todo += nr_user;
- if (!user_frozen && !nr_user) {
- sys_sync();
- start_time = jiffies;
+ read_lock(&tasklist_lock);
+ do_each_thread(g, p) {
+ if (!freezeable(p, do_all_threads))
+ continue;
+
+ spin_lock_irqsave(&p->sighand->siglock, flags);
+ if (!(p->flags & PF_FROZEN)) {
+ recalc_sigpending();
+ signal_wake_up(p, 0);
}
- user_frozen = !nr_user;
- yield(); /* Yield is okay here */
- if (todo && time_after(jiffies, start_time + TIMEOUT))
- break;
- } while(todo);
-
- /* This does not unfreeze processes that are already frozen
- * (we have slightly ugly calling convention in that respect,
- * and caller must call thaw_processes() if something fails),
- * but it cleans up leftover PF_FREEZE requests.
- */
- if (todo) {
- printk( "\n" );
- printk(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);
- if (freezing(p)) {
- pr_debug(" clean up: %s\n", p->comm);
- p->flags &= ~PF_FREEZE;
- spin_lock_irqsave(&p->sighand->siglock, flags);
- recalc_sigpending_tsk(p);
- spin_unlock_irqrestore(&p->sighand->siglock, flags);
- }
- } while_each_thread(g, p);
- read_unlock(&tasklist_lock);
- return todo;
- }
-
- printk( "|\n" );
- BUG_ON(in_atomic());
- return 0;
+ spin_unlock_irqrestore(&p->sighand->siglock, flags);
+ } while_each_thread(g, p);
+ read_unlock(&tasklist_lock);
}
-void thaw_processes(void)
+/*
+ * Freezer failure.
+ *
+ * Check whether we failed to freeze all the processes that
+ * should be frozen. If we find a task that failed to freeze,
+ * we give useful information on what failed and how.
+ */
+static int freezer_failure(int do_all_threads)
{
+ int result = 0;
struct task_struct *g, *p;
- printk( "Restarting tasks..." );
read_lock(&tasklist_lock);
do_each_thread(g, p) {
- if (!freezeable(p))
+ if (!freezeable(p, do_all_threads) ||
+ p->state == TASK_UNINTERRUPTIBLE)
continue;
- if (!thaw_process(p))
- printk(KERN_INFO " Strange, %s not stopped\n", p->comm );
- } while_each_thread(g, p);
+ if (!result) {
+ printk(KERN_ERR "Stopping tasks failed.\n");
+ printk(KERN_ERR "Tasks that refused to be "
+ "refrigerated and haven't since exited:\n");
+ set_freezer_state(ABORT_FREEZING);
+ result = 1;
+ }
+
+ if ((freezing(p))) {
+ printk(" - %s (#%d) signalled but "
+ "didn't enter refrigerator.\n",
+ p->comm, p->pid);
+ } else
+ printk(" - %s (#%d) signalled "
+ "and todo list empty.\n",
+ p->comm, p->pid);
+ } while_each_thread(g, p);
read_unlock(&tasklist_lock);
- schedule();
- printk( " done\n" );
+
+ return result;
+}
+
+/*
+ * freeze_threads
+ *
+ * Freeze a set of threads having particular attributes.
+ *
+ * Types:
+ * 2: User threads.
+ * 3: Kernel threads.
+ */
+static int freeze_threads(int do_all_threads)
+{
+ int result = 0, still_to_do;
+ unsigned long start_time = jiffies;
+
+ if (do_all_threads)
+ freezer_make_fses_ro();
+
+ signal_threads(do_all_threads);
+
+ /* Watch them do it, wake them if they ignore us. */
+ do {
+ prod_processes(do_all_threads);
+
+ set_task_state(current, TASK_INTERRUPTIBLE);
+ schedule_timeout(FREEZER_CHECK_TIMEOUT);
+
+ still_to_do = num_freezeable(do_all_threads) -
+ num_uninterruptible(do_all_threads);
+
+ } while(still_to_do && (!test_freezer_state(ABORT_FREEZING)) &&
+ !time_after(jiffies, start_time + FREEZER_TOTAL_TIMEOUT));
+
+ /*
+ * Did we time out? See if we failed to freeze processes as well.
+ *
+ */
+ if ((time_after(jiffies, start_time + FREEZER_TOTAL_TIMEOUT))
+ && (still_to_do))
+ result = freezer_failure(do_all_threads);
+
+ BUG_ON(in_atomic());
+
+ return 0;
+}
+
+/*
+ * freeze_processes - Freeze processes prior to saving an image of memory.
+ *
+ * Return value: 0 = success, 1 = faulure.
+ */
+int freeze_processes(void)
+{
+ enum system_states old_state = system_state;
+ int result = 0;
+
+ if (!test_freezer_state(FREEZER_ON)) {
+ /*
+ * No race. While !FREEZER_ON, processes
+ * won't enter __freeze_process
+ */
+ init_completion(&userspace_thaw);
+ init_completion(&kernelspace_thaw);
+ set_freezer_state(FREEZER_ON);
+ }
+
+ /* Now freeze processes that were syncing and are still running */
+ if (freeze_threads(0) || (test_freezer_state(ABORT_FREEZING))) {
+ result = 1;
+ goto out;
+ }
+
+ /* Freeze kernel threads */
+ if (freeze_threads(1) || (test_freezer_state(ABORT_FREEZING)))
+ result = 1;
+
+out:
+ system_state = old_state;
+ return result;
}
+EXPORT_SYMBOL(freezer_state);
EXPORT_SYMBOL(refrigerator);
diff -urN oldtree/kernel/power/smp.c newtree/kernel/power/smp.c
--- oldtree/kernel/power/smp.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/power/smp.c 2006-03-27 16:04:08.540507250 -0500
@@ -2,7 +2,7 @@
* drivers/power/smp.c - Functions for stopping other CPUs.
*
* Copyright 2004 Pavel Machek
- * Copyright (C) 2002-2003 Nigel Cunningham
+ * Copyright (C) 2002-2003 Nigel Cunningham
*
* This file is released under the GPLv2.
*/
diff -urN oldtree/kernel/power/snapshot.c newtree/kernel/power/snapshot.c
--- oldtree/kernel/power/snapshot.c 2006-03-27 13:28:15.000000000 -0500
+++ newtree/kernel/power/snapshot.c 2006-03-27 16:04:08.544507500 -0500
@@ -177,7 +177,6 @@
return 0;
page = pfn_to_page(pfn);
- BUG_ON(PageReserved(page) && PageNosave(page));
if (PageNosave(page))
return 0;
if (PageReserved(page) && pfn_is_nosave(pfn))
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-03-27 16:04:08.544507500 -0500
@@ -0,0 +1,323 @@
+/*
+ * kernel/power/storage.c
+ *
+ * Copyright (C) 2005-2006 Nigel Cunningham
+ *
+ * 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
+#include
+
+#include "proc.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))
+ 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.disabled)
+ 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.disabled)
+ 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("--- Deactivate storage 1 ---\n");
+ suspend_deactivate_storage(1);
+ schedule();
+ printk("--- Activate storage 1 ---\n");
+ suspend_activate_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.disabled)
+ 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 /proc/suspend entries.
+ */
+
+static struct suspend_proc_data proc_params[] = {
+ { .filename = "disable_storage_manager",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_INTEGER,
+ .data = {
+ .integer = {
+ .variable = &usm_ops.disabled,
+ .minimum = 0,
+ .maximum = 1,
+ }
+ }
+ },
+ { .filename = "storage_manager",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_STRING,
+ .data = {
+ .string = {
+ .variable = usm_helper_data.program,
+ .max_length = 254,
+ }
+ }
+ },
+ { .filename = "activate_storage",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_INTEGER,
+ .data = {
+ .integer = {
+ .variable = &storage_manager_action,
+ .minimum = 0,
+ .maximum = 1,
+ }
+ },
+ .write_proc = storage_manager_activate,
+ },
+
+#ifdef CONFIG_PM_DEBUG
+ { .filename = "simulate_atomic_copy",
+ .permissions = PROC_RW,
+ .type = SUSPEND_PROC_DATA_NONE,
+ .write_proc = 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_proc_init
+ * Description: Boot time initialisation for user interface.
+ */
+static __init int suspend_usm_proc_init(void)
+{
+ int result, i, numfiles = sizeof(proc_params) / sizeof(struct suspend_proc_data);
+
+ if (!(result = suspend_register_module(&usm_ops)))
+ for (i=0; i< numfiles; i++)
+ suspend_register_procfile(&proc_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_proc_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-03-27 16:04:08.548507750 -0500
@@ -0,0 +1,25 @@
+/*
+ * kernel/power/storage.h
+ *
+ * Copyright (C) 2005-2006 Nigel Cunningham
+ *
+ * This file is released under the GPLv2.
+ */
+
+int suspend_prepare_usm(void);
+void suspend_cleanup_usm(void);
+
+int suspend_activate_storage(int force);
+int suspend_deactivate_storage(int force);
+
+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-03-27 17:08:55.599433250 -0500
@@ -0,0 +1,1080 @@
+/*
+ * 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
+ * Copyright (C) 1998,2001,2002 Pavel Machek
+ * Copyright (C) 2002-2003 Florent Chabaud
+ * Copyright (C) 2002-2006 Nigel Cunningham
+ *
+ * \section Credits
+ *
+ * Nigel would like to thank the following people for their work:
+ *
+ * Pavel Machek
+ * 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
+ * Support the possibility of hardware state restoring.
+ *
+ * Raph
+ * Support for preserving states of network devices and virtual console
+ * (including X and svgatextmode)
+ *
+ * Kurt Garloff
+ * Straightened the critical function in order to prevent compilers from
+ * playing tricks with local variables.
+ *
+ * Andreas Mohr
+ *
+ * Alex Badea
+ * Fixed runaway init
+ *
+ * Jeff Snyder
+ * ACPI patch
+ *
+ * Nathan Friess
+ * Some patches.
+ *
+ * Michael Frank