diff -ruNp 624-filewriter.patch-old/kernel/power/suspend_file.c 
624-filewriter.patch-new/kernel/power/suspend_file.c
--- 624-filewriter.patch-old/kernel/power/suspend_file.c        1970-01-01 
10:00:00.000000000 +1000
+++ 624-filewriter.patch-new/kernel/power/suspend_file.c        2005-07-05 
23:48:59.000000000 +1000
@@ -0,0 +1,1616 @@
+/*
+ * Filewriter.c
+ *
+ * Copyright 2005 Nigel Cunningham <[EMAIL PROTECTED]>
+ *
+ * Distributed under GPLv2.
+ * 
+ * This file encapsulates functions for usage of a simple file as a
+ * backing store. It is based upon the swapwriter, and shares the
+ * same basic working. Here, though, we have nothing to do with
+ * swapspace, and only one device to worry about.
+ *
+ * The user can just
+ *
+ * echo Suspend2 > /path/to/my_file
+ *
+ * and
+ *
+ * echo /path/to/my_file > /proc/software_suspend/filewriter_target
+ *
+ * then put what they find in /proc/software_suspend/resume2
+ * as their resume2= parameter in lilo.conf (and rerun lilo if using it).
+ *
+ * Having done this, they're ready to suspend and resume.
+ *
+ * TODO:
+ * - File resizing.
+ */
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/mount.h>
+#include <linux/statfs.h>
+
+#include "suspend2_core/suspend.h"
+#include "suspend2_core/suspend2_common.h"
+#include "suspend2_core/version.h"
+#include "suspend2_core/proc.h"
+#include "suspend2_core/plugins.h"
+#include "suspend2_core/ui.h"
+#include "suspend2_core/extent.h"
+#include "suspend2_core/utility.h"
+#include "suspend2_core/io.h"
+
+#include "block_io.h"
+
+/*
+ *             General Declarations.
+ */
+
+static struct suspend_proc_data filewriter_proc_data[];
+static struct suspend_plugin_ops filewriterops;
+
+/*
+ *             External Declarations
+ */
+
+extern asmlinkage long sys_open(const char __user * filename, int flags, int 
mode);
+extern asmlinkage long sys_close(unsigned int fd);
+
+/*
+ *             Forward Declarations
+ */
+
+static int filewriter_invalidate_image(void);
+static int filewriter_storage_available(void);
+
+/*
+ *             Details of our target.
+ */
+
+char filewriter_target[256];
+static struct inode * target_inode;
+static int target_fd = -1;
+static struct block_device * target_bdev;
+static int used_devt = 0;
+static dev_t target_dev_t = 0;
+static int target_firstblock = 0;
+static int target_blocksize = PAGE_SIZE;
+static int target_storage_available = 0;
+static unsigned int target_blkbits;
+#define target_blockshift (PAGE_SHIFT - target_blkbits)
+#define target_blocksperpage (1 << target_blockshift)
+
+static int target_type = -1;
+
+/*
+static char * description[7] = {
+       "Socket",
+       "Link",
+       "Regular file",
+       "Block device",
+       "Directory",
+       "Character device",
+       "Fifo",
+};
+*/
+
+static char HaveImage[] = "HaveImage\n";
+static char NoImage[] =   "Suspend2\n";
+static const int resumed_before_byte = sizeof(HaveImage) + 1;
+#define sig_size resumed_before_byte
+
+/* Header_pages must be big enough for signature */
+static int header_pages, main_pages;
+
+static unsigned long * header_link = NULL;
+#define BYTES_PER_HEADER_PAGE (PAGE_SIZE - sizeof(sector_t))
+
+#define target_is_normal_file() (S_ISREG(target_inode->i_mode))
+
+/*
+ *             Readahead Variables
+ */
+
+// Higher Level
+static int readahead_index = 0, readahead_submit_index = 0;
+static int readahead_allocs = 0, readahead_frees = 0;
+
+static char * filewriter_buffer = NULL;
+static int filewriter_buffer_posn = 0;
+static int filewriter_page_index = 0;
+
+/*
+ * ---------------------------------------------------------------
+ *
+ *     Internal Data Structures
+ *
+ * ---------------------------------------------------------------
+ */
+
+/* header_data contains data that is needed to reload pagedir1, and
+ * is therefore saved in the suspend header.
+ *
+ * Pagedir2 data gets stored before pagedir1 (save order), and the first
+ * page for pagedir1 to use is set when pagedir2 is written (when we know how
+ * much storage it used). Since this first entry is almost certainly not at the
+ * start of a extent, the firstoffset variable below tells us where to start in
+ * the extent. All of this means we don't have to worry about getting different
+ * compression ratios for the kernel and cache (when compressing the image).
+ * We can simply allocate one pool of storage (size determined using expected
+ * compression ratio) and use it without worrying whether one pageset
+ * compresses better and the other worse (this is what happens). As long as the
+ * user gets the expected compression right, it will work.
+ */
+
+static struct {
+       /* Location of start of pagedir 1 */
+       struct extent * pd1start_block_extent;
+       int pd1start_extent_number;
+       unsigned long pd1start_block_offset;
+
+} filewriter_header_data;
+
+/* Extent chain for blocks */
+static struct extentchain block_chain;
+
+/*
+ * ---------------------------------------------------------------
+ *
+ *     Current state.
+ *
+ * ---------------------------------------------------------------
+ */
+
+/* Which pagedir are we saving/reloading? Needed so we can know whether to
+ * remember the last block used at the end of writing pageset2, and
+ * get that location when saving or reloading pageset1.*/
+static int current_stream = 0;
+
+/* Pointer to current entry being loaded/saved. */
+static struct extent * currentblockextent = NULL;
+static unsigned long currentblockoffset = 0;
+
+/* Header Page Information */
+static struct submit_params * first_header_submit_info = NULL,
+ * last_header_submit_info = NULL, * current_header_submit_info = NULL;
+
+/*
+ *             Helpers.
+ */
+
+/* 
+ * Return the type of target we have, an index into the descriptions
+ * above.
+ */
+static int get_target_type(struct inode * inode)
+{
+       switch (inode->i_mode & S_IFMT) {
+               case S_IFSOCK:
+                       target_type = 0;
+                       break;
+               case S_IFLNK:
+                       target_type = 1;
+                       break;
+               case S_IFREG:
+                       target_type = 2;
+                       break;
+               case S_IFBLK:
+                       target_type = 3;
+                       break;
+               case S_IFDIR:
+                       target_type = 4;
+                       break;
+               case S_IFCHR:
+                       target_type = 5;
+                       break;
+               case S_IFIFO:
+                       target_type = 6;
+                       break;
+       }
+       return target_type;
+}
+       
+#define target_is_usable (!(target_type == 1 || target_type == 4))
+#define target_num_sectors (target_inode->i_size >> target_blkbits)
+
+static int size_ignoring_sparseness(void)
+{
+       int mappable = 0, i;
+       
+       if (target_is_normal_file()) {
+               int extent_min = -1, extent_max = -1;
+
+               for (i = 0; i <= target_num_sectors; i++) {
+                       sector_t new_sector = bmap(target_inode, i);
+                       if (!new_sector) {
+                               if (i == extent_max + 1)
+                                       extent_max++;
+                               else
+                                       extent_min = extent_max = i;
+                       } else
+                               mappable++;
+               }
+       
+               return mappable >> (PAGE_SHIFT - target_blkbits);
+       } else
+               return filewriter_storage_available();
+}
+
+static void get_main_pool_phys_params(void)
+{
+       int i;
+       
+       if (block_chain.first)
+               put_extent_chain(&block_chain);
+
+       if (target_is_normal_file()) {
+               int header_sectors = (header_pages << target_blockshift);
+               int extent_min = -1, extent_max = -1, real_sector = 0;
+
+
+               for (i = 0; i <= target_num_sectors; i++) {
+                       sector_t new_sector =
+                               bmap(target_inode, header_sectors + i);
+                       
+                       /* 
+                        * I'd love to be able to fill in holes and resize 
+                        * files, but not yet...
+                        */
+
+                       if (!new_sector)
+                               continue;
+                       
+                       real_sector++;
+
+                       if (real_sector < header_sectors)
+                               continue;
+
+                       if (new_sector == extent_max + 1)
+                               extent_max++;
+                       else {
+                               if (extent_min > -1)
+                                       append_extent_to_extent_chain(
+                                               &block_chain,
+                                               extent_min, extent_max);
+                               extent_min = extent_max = new_sector;
+                       }
+               }
+               if (extent_min > -1)
+                       append_extent_to_extent_chain(&block_chain,
+                                      extent_min, extent_max);
+       } else
+               if (target_storage_available > 0) {
+                       unsigned long new_start =
+                        last_header_submit_info ?
+                        last_header_submit_info->block[target_blocksperpage -1]
+                               + 1: 0;
+
+                       append_extent_to_extent_chain(&block_chain,
+                        new_start, new_start +
+                        (min(main_pages, target_storage_available) << 
+                                       target_blockshift) - 1);
+               }
+}
+
+static void get_target_info(void)
+{
+       if (target_bdev) {
+               /* 
+                * Don't replace the inode if we got the bdev from opening
+                * a file.
+                */
+               if (!target_inode)
+                       target_inode = target_bdev->bd_inode;
+               target_type = get_target_type(target_inode);
+               target_blkbits = target_bdev->bd_inode->i_blkbits;
+               target_storage_available = size_ignoring_sparseness();
+       } else {
+               target_type = -1;
+               target_inode = NULL;
+               target_blkbits = 0;
+               target_storage_available = 0;
+       }       
+}
+
+static int set_target_blocksize(void)
+{
+       if ((suspend_bio_ops.get_block_size(target_bdev) 
+                                       != target_blocksize) &&
+           (suspend_bio_ops.set_block_size(target_bdev, target_blocksize)
+                                == -EINVAL)) {
+               printk(KERN_ERR name_suspend "Filewriter: Failed to set the 
blocksize.\n");
+               return 1;
+       }
+
+       return 0;
+               
+}
+
+static int try_to_open_target_device(void)
+{
+       if (!target_dev_t)
+               return 1;
+
+       if (!target_bdev) {
+               target_bdev = open_by_devnum(target_dev_t, FMODE_READ);
+
+               if (IS_ERR(target_bdev)) {
+                       target_bdev = NULL;
+                       return 1;
+               }
+               used_devt = 1;
+
+               if (set_target_blocksize()) {
+                       blkdev_put(target_bdev);
+                       target_bdev = NULL;
+                       return 1;
+               }
+       }
+
+       get_target_info();
+
+       return 0;
+}
+
+static int try_to_parse_target_dev_t(char * commandline)
+{
+       struct kstat stat;
+       int error;
+
+       target_dev_t = name_to_dev_t(commandline);
+
+       if (!target_dev_t) {
+               error = vfs_stat(commandline, &stat);
+               if (!error)
+                       target_dev_t = stat.rdev;
+       }
+
+       if (!target_dev_t) {
+               if (test_suspend_state(SUSPEND_TRYING_TO_RESUME))
+                       suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ,
+                               "Failed to translate \"%s\" into a device 
id.\n",
+                               commandline);
+               else
+                       printk(name_suspend "Can't translate \"%s\" into a 
device id yet.\n",
+                                       commandline);
+               return 1;
+       }
+       
+       try_to_open_target_device();
+
+       if (IS_ERR(target_bdev)) {
+               printk("Open by devnum returned %p given %x.\n",
+                               target_bdev, target_dev_t);
+               target_bdev = NULL;
+               if (test_suspend_state(SUSPEND_BOOT_TIME))
+                       suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ,
+                               "Failed to get access to the device on which"
+                               " Software Suspend's header should be found.");
+               else
+                       printk("Failed to get access to the device on which "
+                               "Software Suspend's header should be found.\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+static void filewriter_noresume_reset(void)
+{
+       /* 
+        * If we have read part of the image, we might have filled header_data 
with
+        * data that should be zeroed out.
+        */
+
+       memset((char *) &filewriter_header_data, 0, 
sizeof(filewriter_header_data));
+}
+
+/*
+ *
+ */
+
+int parse_signature(char * header, int restore)
+{
+       int have_image = !memcmp(HaveImage, header, sizeof(HaveImage) - 1);
+       int non_image_header = !memcmp(NoImage, header, sizeof(NoImage) - 1);
+
+       if (!have_image && !non_image_header)
+               return -1;
+
+       if (non_image_header)
+               return 0;
+       
+       clear_suspend_state(SUSPEND_RESUMED_BEFORE);
+
+       if (header[resumed_before_byte] & 1)
+               set_suspend_state(SUSPEND_RESUMED_BEFORE);
+
+       /* Invalidate Image */
+       if (restore)
+               strcpy(header, NoImage);
+
+       return 1;
+}
+
+/*
+ * prepare_signature
+ */
+
+static int prepare_signature(struct submit_params * header_page_info,
+               char * current_header)
+{
+       /* 
+        * Explicitly put the \0 that clears the 'tried to resume from
+        * this image before' flag.
+        */
+       strncpy(current_header, HaveImage, sizeof(HaveImage));
+       current_header[resumed_before_byte] = 0;
+       return 0;
+}
+
+static void free_header_data(void)
+{
+       if (!first_header_submit_info)
+               return;
+
+       while (first_header_submit_info) {
+               struct submit_params * next = first_header_submit_info->next;
+               kfree(first_header_submit_info);
+               first_header_submit_info = next;
+       }
+       
+       suspend_message(SUSPEND_WRITER, SUSPEND_LOW, 1,
+                       " Freed swap pages in free_header_data.\n");
+       first_header_submit_info = last_header_submit_info = NULL;
+       return;
+}
+
+static int filewriter_storage_available(void)
+{
+       int result = 0;
+
+       if (!target_inode)
+               return 0;
+
+       switch (target_type) {
+               case 0:
+               case 5:
+               case 6: /* Socket, Char, Fifi */
+                       return -1;
+               case 2: /* Regular file: current size - holes + free space on 
part */
+                       result = target_storage_available;
+                       break;
+               case 3: /* Block device */
+                       if (target_bdev->bd_disk) {
+                               if (target_bdev->bd_part)
+                                       result = (unsigned 
long)target_bdev->bd_part->nr_sects >> (PAGE_SHIFT - 9);
+                               else
+                                       result = (unsigned 
long)target_bdev->bd_disk->capacity >> (PAGE_SHIFT - 9);
+                       } else {
+                               printk("bdev->bd_disk null.\n");
+                               return 0;
+                       }
+       }
+
+       return result;
+}
+
+static int filewriter_storage_allocated(void)
+{
+       int result;
+
+       if (!target_inode)
+               return 0;
+
+       if (target_is_normal_file()) {
+               result = (int) target_storage_available;
+       } else
+               result = header_pages + main_pages;
+
+       return result;
+}
+
+static int filewriter_initialise(int starting_cycle)
+{
+       if (!starting_cycle)
+               return 0;
+
+       target_fd = sys_open(filewriter_target, O_RDWR, 0);
+
+       if (target_fd < 0) {
+               printk("Open file %s returned %d.\n", filewriter_target, 
target_fd);
+               return target_fd;
+       }
+
+       target_inode = current->files->fd[target_fd]->f_dentry->d_inode;
+       BUG_ON(target_bdev);
+       target_bdev = target_inode->i_bdev ? target_inode->i_bdev : 
target_inode->i_sb->s_bdev;
+       set_target_blocksize();
+       get_target_info();
+
+       return 0;
+}
+
+static void filewriter_cleanup(int finishing_cycle)
+{
+       if (target_bdev) {
+               if (used_devt) {
+                       blkdev_put(target_bdev);
+                       used_devt = 0;
+               }
+               target_bdev = NULL;
+               get_target_info();
+       }
+
+       if (!finishing_cycle)
+               return;
+
+       if (target_fd >= 0)
+               sys_close(target_fd);
+
+       target_fd = -1;
+}
+
+static int filewriter_release_storage(void)
+{
+       if ((TEST_ACTION_STATE(SUSPEND_KEEP_IMAGE)) && 
test_suspend_state(SUSPEND_NOW_RESUMING))
+               return 0;
+
+       /* Free metadata */
+       free_header_data();
+
+       put_extent_chain(&block_chain);
+
+       header_pages = main_pages = 0;
+       return 0;
+}
+
+static int filewriter_allocate_header_space(int space_requested)
+{
+       int i, j, pages_to_get;
+       int ret = 0;
+
+       /* We only steal pages from the main pool. If it doesn't have any 
yet... */
+       
+       if (!block_chain.first)
+               return 0;
+
+       pages_to_get = space_requested - header_pages;
+
+       if (pages_to_get < 1)
+               return 0;
+
+       for (i= header_pages; i < space_requested; i++) {
+               struct submit_params * new_submit_param;
+
+               /* Get a submit structure */
+               new_submit_param = kmalloc(sizeof(struct submit_params), 
GFP_ATOMIC);
+               
+               if (!new_submit_param) {
+                       printk("Failed to kmalloc a struct submit param.\n");
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               memset(new_submit_param, 0, sizeof(struct submit_params));
+
+               if (last_header_submit_info) {
+                       last_header_submit_info->next = new_submit_param;
+                       last_header_submit_info = new_submit_param;
+               } else
+                       last_header_submit_info = first_header_submit_info =
+                               new_submit_param;
+
+               for (j = 0; j < target_blocksperpage; j++) {
+                       unsigned long newvalue;
+
+                       /*
+                        *  Steal one from main extent chain. If, as a result,
+                        *  it is too small, more storage will be allocated or
+                        *  memory eaten.
+                        */
+
+                       if (block_chain.first->minimum <
+                                       block_chain.first->maximum) {
+                               newvalue = block_chain.first->minimum;
+                               block_chain.first->minimum++;
+                       } else {
+                               struct extent * oldfirst =
+                                       block_chain.first;
+                               block_chain.first = oldfirst->next;
+                               block_chain.frees++;
+                               if (block_chain.last == oldfirst)
+                                       block_chain.last = NULL;
+                               newvalue = oldfirst->minimum;
+                               put_extent(oldfirst);
+                       }
+                       
+                       block_chain.size--;
+
+                       new_submit_param->block[j] = newvalue;
+               }
+
+               new_submit_param->dev = target_bdev;
+               new_submit_param->readahead_index = -1;
+
+               header_pages++;
+
+               suspend_message(SUSPEND_WRITER, SUSPEND_MEDIUM, 0,
+                       " Got header page %d/%d. Dev is %x. Block is %lu. "
+                       "Target block size is %d.\n",
+                       i, space_requested,
+                       new_submit_param->dev,
+                       new_submit_param->block[0],
+                       new_submit_param->dev->bd_block_size);
+
+               if (!block_chain.size)
+                       break;
+       }
+out:
+       return ret;
+}
+
+static int filewriter_allocate_storage(int space_requested)
+{
+       int result = 0;
+       int blocks_to_get = (space_requested << target_blockshift) - 
block_chain.size;
+       
+       /* Only release_storage reduces the size */
+       if (blocks_to_get < 1)
+               return 0;
+
+       main_pages = space_requested;
+
+       get_main_pool_phys_params();
+
+       suspend_message(SUSPEND_WRITER, SUSPEND_MEDIUM, 0,
+               "Finished with block_chain.size == %d.\n",
+               block_chain.size);
+
+       if (block_chain.size < ((header_pages + main_pages) << 
target_blockshift))
+               result = -ENOSPC;
+
+       return result;
+}
+
+static int filewriter_write_header_chunk(char * buffer, int buffer_size);
+static int filewriter_write_header_init(void)
+{
+       char new_sig[sig_size];
+       struct extent * extent;
+       
+       filewriter_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+       header_link =
+               (unsigned long *) (filewriter_buffer + BYTES_PER_HEADER_PAGE);
+       filewriter_page_index = 1;
+       filewriter_buffer_posn = 0;
+
+       current_header_submit_info = first_header_submit_info;
+       
+       /* We change it once the whole header is written */
+       strcpy(new_sig, NoImage);
+       filewriter_write_header_chunk(new_sig, sig_size);
+
+       /* Must calculate extent number before writing the header! */
+       filewriter_header_data.pd1start_extent_number = 1;
+       extent = block_chain.first;
+
+       while (extent != filewriter_header_data.pd1start_block_extent) {
+               filewriter_header_data.pd1start_extent_number++;
+               extent = extent->next;
+       }
+
+       /* Info needed to bootstrap goes at the start of the header.
+        * First we save the 'header_data' struct, including the number
+        * of header pages. Then we save the structs containing data needed
+        * for reading the header pages back.
+        * Note that even if header pages take more than one page, when we
+        * read back the info, we will have restored the location of the
+        * next header page by the time we go to use it.
+        */
+       filewriter_write_header_chunk((char *) &filewriter_header_data, 
+                       sizeof(filewriter_header_data));
+
+       return 0;
+}
+
+static int filewriter_write_header_chunk(char * buffer, int buffer_size)
+{
+       int bytes_left = buffer_size;
+       
+       /* 
+        * We buffer the writes until a page is full and to use the last
+        * sizeof(swp_entry_t) bytes for links between pages. This is 
+        * totally transparent to the caller.
+        *
+        * Note also that buffer_size can be > PAGE_SIZE.
+        */
+
+       suspend_message(SUSPEND_WRITER, SUSPEND_HIGH, 0,
+               "\nStart of write_header_chunk loop with %d bytes to store.\n",
+               buffer_size);
+
+       while (bytes_left) {
+               char * source_start = buffer + buffer_size - bytes_left;
+               char * dest_start = filewriter_buffer + filewriter_buffer_posn;
+               int dest_capacity = BYTES_PER_HEADER_PAGE - 
filewriter_buffer_posn;
+               sector_t next_header_page;
+               if (bytes_left <= dest_capacity) {
+                       memcpy(dest_start, source_start, bytes_left);
+                       filewriter_buffer_posn += bytes_left;
+                       return 0;
+               }
+       
+               /* A page is full */
+               memcpy(dest_start, source_start, dest_capacity);
+               bytes_left -= dest_capacity;
+
+               BUG_ON(!current_header_submit_info);
+
+               if (!current_header_submit_info->next) {
+                       *header_link = 0;
+               } else {
+                       next_header_page =
+                               current_header_submit_info->next->block[0];
+
+                       *header_link = next_header_page;
+               }
+
+               suspend_message(SUSPEND_WRITER, SUSPEND_HIGH, 0,
+                       "Writing header page %d. "
+                       "Dev is %x. Block is %lu. Blocksperpage is %d. 
Bd_block_size is %d.\n",
+                       filewriter_page_index,
+                       current_header_submit_info->dev->bd_dev,
+                       current_header_submit_info->block[0],
+                       target_blocksperpage,
+                       current_header_submit_info->dev->bd_block_size);
+               
+               current_header_submit_info->page =
+                       virt_to_page(filewriter_buffer);
+               check_shift_keys(0, NULL);
+               suspend_bio_ops.submit_io(WRITE, current_header_submit_info, 0);
+
+               filewriter_buffer_posn = 0;
+               filewriter_page_index++;
+               current_header_submit_info = current_header_submit_info->next;
+       }
+
+       return 0;
+}
+
+static int filewriter_write_header_cleanup(void)
+{
+       /* Write any unsaved data */
+       if (filewriter_buffer_posn) {
+               *header_link = 0;
+
+               suspend_message(SUSPEND_WRITER, SUSPEND_HIGH, 0,
+                       "Writing header page %d. "
+                       "Dev is %x. Block is %lu. Blocksperpage is %d.\n",
+                       filewriter_page_index,
+                       current_header_submit_info->dev->bd_dev,
+                       current_header_submit_info->block[0],
+                       target_blocksperpage);
+               
+               current_header_submit_info->page =
+                       virt_to_page(filewriter_buffer);
+               suspend_bio_ops.submit_io(WRITE, 
+                               current_header_submit_info, 0);
+       }
+
+       suspend_bio_ops.finish_all_io();
+
+       /* Adjust image header */
+       suspend_bio_ops.bdev_page_io(READ, target_bdev, target_firstblock,
+                       virt_to_page(filewriter_buffer));
+
+       prepare_signature(first_header_submit_info, filewriter_buffer);
+               
+       suspend_bio_ops.bdev_page_io(WRITE, target_bdev, target_firstblock,
+                       virt_to_page(filewriter_buffer));
+
+       free_pages((unsigned long) filewriter_buffer, 0);
+       filewriter_buffer = NULL;
+       header_link = NULL;
+       
+       suspend_bio_ops.finish_all_io();
+
+       return 0;
+}
+
+/* ------------------------- HEADER READING ------------------------- */
+
+/*
+ * read_header_init()
+ * 
+ * Description:
+ * 1. Attempt to read the device specified with resume2=.
+ * 2. Check the contents of the swap header for our signature.
+ * 3. Warn, ignore, reset and/or continue as appropriate.
+ * 4. If continuing, read the filewriter configuration section
+ *    of the header and set up block device info so we can read
+ *    the rest of the header & image.
+ *
+ * Returns:
+ * May not return if user choose to reboot at a warning.
+ * -EINVAL if cannot resume at this time. Booting should continue
+ * normally.
+ */
+
+static int filewriter_read_header_init(void)
+{
+       filewriter_page_index = 1;
+
+       filewriter_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+       filewriter_buffer_posn = sig_size;
+
+       /* Read filewriter configuration */
+       suspend_bio_ops.bdev_page_io(READ, target_bdev, target_firstblock,
+                       virt_to_page((unsigned long) filewriter_buffer));
+       
+       suspend_message(SUSPEND_WRITER, SUSPEND_HIGH, 0,
+               "Retrieving %d bytes from %x:%x to page %d, %p-%p.\n",
+               target_bdev->bd_dev, target_firstblock,
+               sizeof(filewriter_header_data),
+               filewriter_page_index,
+               filewriter_buffer, filewriter_buffer + 
sizeof(filewriter_header_data) - 1);
+       memcpy(&filewriter_header_data,
+                       filewriter_buffer + filewriter_buffer_posn,
+                       sizeof(filewriter_header_data));
+       
+       filewriter_buffer_posn += sizeof(filewriter_header_data);
+
+       return 0;
+}
+
+static int filewriter_read_header_chunk(char * buffer, int buffer_size)
+{
+       int bytes_left = buffer_size, ret = 0;
+       
+       /* Read a chunk of the header */
+       while ((bytes_left) && (!ret)) {
+               sector_t next =
+                  *((sector_t *) (filewriter_buffer + BYTES_PER_HEADER_PAGE));
+               char * dest_start = buffer + buffer_size - bytes_left;
+               char * source_start =
+                       filewriter_buffer + filewriter_buffer_posn;
+               int source_capacity =
+                       BYTES_PER_HEADER_PAGE - filewriter_buffer_posn;
+
+               if (bytes_left <= source_capacity) {
+                       memcpy(dest_start, source_start, bytes_left);
+                       filewriter_buffer_posn += bytes_left;
+                       return buffer_size;
+               }
+
+               /* Next to read the next page */
+               memcpy(dest_start, source_start, source_capacity);
+               bytes_left -= source_capacity;
+
+               filewriter_page_index++;
+
+               suspend_bio_ops.bdev_page_io(READ, target_bdev,
+                               next, virt_to_page(filewriter_buffer));
+
+               filewriter_buffer_posn = 0;
+       }
+
+       return buffer_size - bytes_left;
+}
+
+static int filewriter_read_header_cleanup(void)
+{
+       free_pages((unsigned long) filewriter_buffer, 0);
+       return 0;
+}
+
+static int filewriter_serialise_extents(void)
+{
+       serialise_extent_chain(&block_chain);
+       return 0;
+}
+
+static int filewriter_load_extents(void)
+{
+       int i = 1;
+       struct extent * extent;
+       
+       load_extent_chain(&block_chain);
+
+       extent = block_chain.first;
+
+       while (i < filewriter_header_data.pd1start_extent_number) {
+               extent = extent->next;
+               i++;
+       }
+
+       filewriter_header_data.pd1start_block_extent = extent;
+
+       return 0;
+}
+
+static int filewriter_write_init(int stream_number)
+{
+       if (stream_number == 1) {
+               currentblockextent = 
filewriter_header_data.pd1start_block_extent;
+               currentblockoffset = 
filewriter_header_data.pd1start_block_offset;
+       } else {
+               currentblockextent = block_chain.first;
+               currentblockoffset = currentblockextent->minimum;
+       }
+
+       BUG_ON(!currentblockextent);
+
+       filewriter_page_index = 1;
+       current_stream = stream_number;
+
+       suspend_bio_ops.reset_io_stats();
+
+       return 0;
+}
+
+static int filewriter_write_chunk(struct page * buffer_page)
+{
+       int i;
+       struct submit_params submit_params;
+
+       BUG_ON(!currentblockextent);
+       submit_params.readahead_index = -1;
+       submit_params.page = buffer_page;
+       submit_params.dev = target_bdev;
+               
+       /* Get the blocks */
+       for (i = 0; i < target_blocksperpage; i++) {
+               submit_params.block[i] = currentblockoffset;
+               GET_EXTENT_NEXT(currentblockextent, currentblockoffset);
+       }
+
+       if(!submit_params.block[0])
+               return -EIO;
+
+       if (TEST_ACTION_STATE(SUSPEND_TEST_FILTER_SPEED))
+               return 0;
+               
+       suspend_bio_ops.submit_io(WRITE, &submit_params, 0);
+
+       filewriter_page_index++;
+
+       return 0;
+}
+
+static int filewriter_write_cleanup(void)
+{
+       if (current_stream == 2) {
+               filewriter_header_data.pd1start_block_extent = 
currentblockextent;
+               filewriter_header_data.pd1start_block_offset = 
currentblockoffset;
+       }
+       
+       suspend_bio_ops.finish_all_io();
+       
+       suspend_bio_ops.check_io_stats();
+
+       return 0;
+}
+
+static int filewriter_read_init(int stream_number)
+{
+       if (stream_number == 1) {
+               currentblockextent = 
filewriter_header_data.pd1start_block_extent;
+               currentblockoffset = 
filewriter_header_data.pd1start_block_offset;
+       } else {
+               currentblockextent = NULL;
+               currentblockoffset = 0;
+               currentblockextent =
+                       block_chain.first;
+               currentblockoffset = currentblockextent->minimum;
+       }
+
+       BUG_ON(!currentblockextent);
+
+       filewriter_page_index = 1;
+
+       suspend_bio_ops.reset_io_stats();
+
+       readahead_index = readahead_submit_index = -1;
+       readahead_allocs = readahead_frees = 0;
+
+       return 0;
+}
+
+static int filewriter_begin_read_chunk(struct page * page, 
+               int readahead_index, int sync)
+{
+       int i;
+       struct submit_params submit_params;
+
+       BUG_ON(!currentblockextent);
+       
+       submit_params.readahead_index = readahead_index;
+       submit_params.page = page;
+       submit_params.dev = target_bdev;
+               
+       /* Get the blocks. There is no chance that they span chains. */
+       for (i = 0; i < target_blocksperpage; i++) {
+               submit_params.block[i] = currentblockoffset;
+               GET_EXTENT_NEXT(currentblockextent, currentblockoffset);
+       }
+
+       if ((i = suspend_bio_ops.submit_io(READ, &submit_params, sync)))
+               return -EPERM;
+
+       filewriter_page_index++;
+
+       check_shift_keys(0, NULL);
+
+       return 0;
+}
+
+/* Note that we ignore the sync parameter. We are implementing
+ * read ahead, and will always wait until our readhead buffer has
+ * been read before returning.
+ */
+
+static int filewriter_read_chunk(struct page * buffer_page, int sync)
+{
+       static int last_result;
+       unsigned long * virt;
+
+       if (sync == SUSPEND_ASYNC)
+               return filewriter_begin_read_chunk(buffer_page, -1, sync);
+
+       /* Start new readahead while we wait for our page */
+       if (readahead_index == -1) {
+               last_result = 0;
+               readahead_index = readahead_submit_index = 0;
+       }
+
+       /* Start a new readahead? */
+       if (last_result) {
+               /* We failed to submit a read, and have cleaned up
+                * all the readahead previously submitted */
+               if (readahead_submit_index == readahead_index)
+                       return -EPERM;
+               goto wait;
+       }
+       
+       do {
+               if (suspend_bio_ops.prepare_readahead(readahead_submit_index))
+                       break;
+
+               readahead_allocs++;
+
+               last_result = filewriter_begin_read_chunk(
+                       
suspend_bio_ops.readahead_pages[readahead_submit_index], 
+                       readahead_submit_index, SUSPEND_ASYNC);
+               if (last_result) {
+                       printk("Begin read chunk for page %d returned %d.\n",
+                               readahead_submit_index, last_result);
+                       
suspend_bio_ops.cleanup_readahead(readahead_submit_index);
+                       break;
+               }
+
+               readahead_submit_index++;
+
+               if (readahead_submit_index == MAX_READAHEAD)
+                       readahead_submit_index = 0;
+
+       } while((!last_result) && (readahead_submit_index != readahead_index) &&
+                       (!suspend_bio_ops.readahead_ready(readahead_index)));
+
+wait:
+       suspend_bio_ops.wait_on_readahead(readahead_index);
+
+       virt = kmap_atomic(buffer_page, KM_USER1);
+       memcpy(virt, 
page_address(suspend_bio_ops.readahead_pages[readahead_index]),
+                       PAGE_SIZE);
+       kunmap_atomic(virt, KM_USER1);
+
+       suspend_bio_ops.cleanup_readahead(readahead_index);
+
+       readahead_frees++;
+
+       readahead_index++;
+       if (readahead_index == MAX_READAHEAD)
+               readahead_index = 0;
+
+       return 0;
+}
+
+static int filewriter_read_cleanup(void)
+{
+       suspend_bio_ops.finish_all_io();
+       while (readahead_index != readahead_submit_index) {
+               suspend_bio_ops.cleanup_readahead(readahead_index);
+               readahead_frees++;
+               readahead_index++;
+               if (readahead_index == MAX_READAHEAD)
+                       readahead_index = 0;
+       }
+       suspend_bio_ops.check_io_stats();
+       BUG_ON(readahead_allocs != readahead_frees);
+       return 0;
+}
+
+/* filewriter_invalidate_image
+ * 
+ */
+static int filewriter_invalidate_image(void)
+{
+       char * cur;
+       int result = 0;
+       
+       cur = (char *) get_zeroed_page(GFP_ATOMIC);
+       if (!cur) {
+               printk("Unable to allocate a page for restoring the image 
signature.\n");
+               return -ENOMEM;
+       }
+
+       /*
+        * If nr_suspends == 0, we must be booting, so no swap pages
+        * will be recorded as used yet.
+        */
+
+       if (nr_suspends > 0)
+               filewriter_release_storage();
+
+       /* 
+        * We don't do a sanity check here: we want to restore the swap 
+        * whatever version of kernel made the suspend image.
+        * 
+        * We need to write swap, but swap may not be enabled so
+        * we write the device directly
+        */
+       
+       suspend_bio_ops.bdev_page_io(READ, target_bdev,
+                       target_firstblock, virt_to_page(cur));
+
+       result = parse_signature(cur, 1);
+               
+       if (result == -1)
+               goto out;
+
+       strcpy(cur, NoImage);
+       cur[resumed_before_byte] = 0;
+
+       suspend_bio_ops.bdev_page_io(WRITE, target_bdev, target_firstblock,
+                       virt_to_page(cur));
+
+       if (!nr_suspends)
+               printk(KERN_WARNING name_suspend "Image invalidated.\n");
+out:
+       suspend_bio_ops.finish_all_io();
+       free_pages((unsigned long) cur, 0);
+       return 0;
+}
+
+/*
+ * workspace_size
+ *
+ * Description:
+ * Returns the number of bytes of RAM needed for this
+ * code to do its work. (Used when calculating whether
+ * we have enough memory to be able to suspend & resume).
+ *
+ */
+static unsigned long filewriter_memory_needed(void)
+{
+       return 0;
+}
+
+/* Print debug info
+ *
+ * Description:
+ */
+
+static int filewriter_print_debug_stats(char * buffer, int size)
+{
+       int len = 0;
+       struct sysinfo sysinfo;
+       
+       if (active_writer != &filewriterops) {
+               len = suspend_snprintf(buffer, size, "- Filewriter 
inactive.\n");
+               return len;
+       }
+
+       len = suspend_snprintf(buffer, size, "- Filewriter active.\n");
+
+       si_swapinfo(&sysinfo);
+       
+       len+= suspend_snprintf(buffer+len, size-len, "  Storage available for 
image: %ld pages.\n",
+                       sysinfo.freeswap + filewriter_storage_allocated());
+
+       return len;
+       return 0;
+}
+
+/*
+ * Storage needed
+ *
+ * Returns amount of space in the image header required
+ * for the filewriter's data.
+ *
+ * We ensure the space is allocated, but actually save the
+ * data from write_header_init and therefore don't also define a
+ * save_config_info routine.
+ */
+static unsigned long filewriter_storage_needed(void)
+{
+       return strlen(filewriter_target) + 1;
+}
+
+/*
+ * Image_exists
+ *
+ */
+
+static int filewriter_image_exists(void)
+{
+       int signature_found;
+       char * diskpage;
+       
+       if (try_to_open_target_device())
+               return 0;
+
+       diskpage = (char *) get_zeroed_page(GFP_ATOMIC);
+
+       /* FIXME: Make sure bdev_page_io handles wrong parameters */
+       suspend_bio_ops.bdev_page_io(READ, target_bdev,
+                       target_firstblock, virt_to_page(diskpage));
+       suspend_bio_ops.finish_all_io();
+       signature_found = parse_signature(diskpage, 0);
+       free_pages((unsigned long) diskpage, 0);
+
+       if (!signature_found) {
+               return 0;       /* non fatal error */
+       } else if (signature_found == -1) {
+               printk(KERN_ERR name_suspend
+                       "Unable to find a signature. Could you have moved "
+                       "the file?\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * Mark resume attempted.
+ *
+ * Record that we tried to resume from this image.
+ */
+
+static void filewriter_mark_resume_attempted(void)
+{
+       char * diskpage;
+       int signature_found;
+       
+       if (!target_dev_t) {
+               printk("Not even trying to record attempt at resuming"
+                               " because target_dev_t is not set.\n");
+               return;
+       }
+       
+       diskpage = (char *) get_zeroed_page(GFP_ATOMIC);
+
+       /* FIXME: Make sure bdev_page_io handles wrong parameters */
+       suspend_bio_ops.bdev_page_io(READ, target_bdev, target_firstblock, 
virt_to_page(diskpage));
+       signature_found = parse_signature(diskpage, 0);
+
+       switch (signature_found) {
+               case 1:
+                       diskpage[resumed_before_byte] |= 1;
+                       break;
+       }
+       
+       suspend_bio_ops.bdev_page_io(WRITE, target_bdev, target_firstblock,
+                       virt_to_page(diskpage));
+       suspend_bio_ops.finish_all_io();
+       free_pages((unsigned long) diskpage, 0);
+       return;
+}
+
+/*
+ * Parse Image Location
+ *
+ * Attempt to parse a resume2= parameter.
+ * Swap Writer accepts:
+ * resume2=swap:DEVNAME[:[EMAIL PROTECTED]
+ *
+ * Where:
+ * DEVNAME is convertable to a dev_t by name_to_dev_t
+ * FIRSTBLOCK is the location of the first block in the swap file
+ * (specifying for a swap partition is nonsensical but not prohibited).
+ * BLOCKSIZE is the logical blocksize >= 512 & <= PAGE_SIZE, 
+ * mod 512 == 0 of the device.
+ * Data is validated by attempting to read a swap header from the
+ * location given. Failure will result in filewriter refusing to
+ * save an image, and a reboot with correct parameters will be
+ * necessary.
+ */
+
+static int filewriter_parse_image_location(char * commandline, int only_writer)
+{
+       char *thischar, *devstart = NULL, *colon = NULL, *at_symbol = NULL;
+       char * diskpage = NULL;
+       int signature_found, result = -EINVAL, temp_result;
+
+       if (strncmp(commandline, "file:", 5)) {
+               if (!only_writer)
+                       return 1;
+       } else
+               commandline += 5;
+
+       devstart = thischar = commandline;
+       while ((*thischar != ':') && (*thischar != '@') &&
+               ((thischar - commandline) < 250) && (*thischar))
+               thischar++;
+
+       if (*thischar == ':') {
+               colon = thischar;
+               *colon = 0;
+               thischar++;
+       }
+
+       while ((*thischar != '@') && ((thischar - commandline) < 250) && 
(*thischar))
+               thischar++;
+
+       if (*thischar == '@') {
+               at_symbol = thischar;
+               *at_symbol = 0;
+       }
+       
+       if (colon)
+               target_firstblock = (int) simple_strtoul(colon + 1, NULL, 0);
+       else
+               target_firstblock = 0;
+
+       if (at_symbol) {
+               target_blocksize = (int) simple_strtoul(at_symbol + 1, NULL, 0);
+               if (target_blocksize & 0x1FF)
+                       printk("Filewriter: Blocksizes are usually a multiple 
of 512. Don't expect this to work!\n");
+       } else
+               target_blocksize = 4096;
+       
+       temp_result = try_to_parse_target_dev_t(devstart);
+
+       if (colon)
+               *colon = ':';
+       if (at_symbol)
+               *at_symbol = '@';
+
+       if (temp_result)
+               goto out;
+
+       diskpage = (char *) get_zeroed_page(GFP_ATOMIC);
+       temp_result = suspend_bio_ops.bdev_page_io(READ, target_bdev, 
target_firstblock, virt_to_page(diskpage));
+
+       suspend_bio_ops.finish_all_io();
+       
+       if (temp_result) {
+               printk(KERN_ERR name_suspend "Filewriter: Failed to submit 
I/O.\n");
+               goto out;
+       }
+
+       signature_found = parse_signature(diskpage, 0);
+
+       if (signature_found != -1) {
+               printk(KERN_ERR name_suspend "Filewriter: File signature 
found.\n");
+               result = 0;
+       } else
+               printk(KERN_ERR name_suspend "Filewriter: Sorry. No signature 
found at specified location.\n");
+
+out:
+       if (diskpage)
+               free_page((unsigned long) diskpage);
+       return result;
+}
+
+static void set_filewriter_target(int test_it)
+{
+       char * buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+       char * buffer2 = (char *) get_zeroed_page(GFP_ATOMIC);
+       int offset = 0, fd;
+       int sector;
+
+       if(target_bdev)
+               filewriter_cleanup(0);
+
+       fd = sys_open(filewriter_target, O_RDONLY, 0);
+
+       if (fd < 0) {
+               printk("Filewriter: Unable to open %s.\n", filewriter_target);
+               goto cleanup1;
+       }
+
+       *resume2_file = 0;
+       
+       target_inode = current->files->fd[fd]->f_dentry->d_inode;
+       target_type = get_target_type(target_inode);
+       
+       if (!target_is_usable) {
+               printk("Filewriter: %s is a link or directory. You can't 
suspend to them!\n",
+                               filewriter_target);
+               goto cleanup2;
+       }
+
+       target_bdev = target_inode->i_bdev ? target_inode->i_bdev : 
target_inode->i_sb->s_bdev;
+       sector = bmap(target_inode, 0);
+
+       if (target_bdev && sector) {
+
+               target_blkbits = target_bdev->bd_inode->i_blkbits;
+
+               suspend_bio_ops.bdev_page_io(READ, target_bdev, sector,
+                       virt_to_page(buffer));
+
+               bdevname(target_bdev, buffer2);
+               offset += snprintf(buffer + offset, PAGE_SIZE - offset, 
+                               "/dev/%s", buffer2);
+               
+               if (sector)
+                       offset += snprintf(buffer + offset, PAGE_SIZE - offset,
+                               ":0x%x", sector);
+
+               if (target_inode->i_sb->s_blocksize != PAGE_SIZE)
+                       offset += snprintf(buffer + offset, PAGE_SIZE - offset,
+                               "@%lu", target_inode->i_sb->s_blocksize);
+               
+       } else
+               offset += snprintf(buffer + offset, PAGE_SIZE - offset,
+                               "%s is not a valid target.", filewriter_target);
+                       
+       sprintf(resume2_file, "file:%s", buffer);
+
+cleanup2:
+       if (test_it)
+               sys_close(fd);
+
+cleanup1:
+       free_pages((unsigned long) buffer, 0);
+       free_pages((unsigned long) buffer2, 0);
+}
+
+/* filewriter_save_config_info
+ *
+ * Description:        Save the target's name, not for resume time, but for 
all_settings.
+ * Arguments:  Buffer:         Pointer to a buffer of size PAGE_SIZE.
+ * Returns:    Number of bytes used for saving our data.
+ */
+
+static int filewriter_save_config_info(char * buffer)
+{
+       strcpy(buffer, filewriter_target);
+       return strlen(filewriter_target) + 1;
+}
+
+/* filewriter_load_config_info
+ *
+ * Description:        Reload target's name.
+ * Arguments:  Buffer:         Pointer to the start of the data.
+ *             Size:           Number of bytes that were saved.
+ */
+
+static void filewriter_load_config_info(char * buffer, int size)
+{
+       strcpy(filewriter_target, buffer);
+}
+
+static void test_filewriter_target(void)
+{
+       set_filewriter_target(1);
+}
+
+extern void attempt_to_parse_resume_device(void);
+
+static struct suspend_proc_data filewriter_proc_data[] = {
+
+       {
+        .filename                      = "filewriter_target",
+        .permissions                   = PROC_RW,
+        .type                          = SUSPEND_PROC_DATA_STRING,
+        .data = {
+                .string = {
+                        .variable      = filewriter_target,
+                        .max_length    = 256,
+                }
+        },
+        .write_proc                    = test_filewriter_target,
+       },
+
+       { .filename                     = "disable_filewriter",
+         .permissions                  = PROC_RW,
+         .type                         = SUSPEND_PROC_DATA_INTEGER,
+         .data = {
+               .integer = {
+                       .variable       = &filewriterops.disabled,
+                       .minimum        = 0,
+                       .maximum        = 1,
+               }
+         },
+         .write_proc                   = attempt_to_parse_resume_device,
+       }
+};
+
+static struct suspend_plugin_ops filewriterops = {
+       .type                                   = WRITER_PLUGIN,
+       .name                                   = "File Writer",
+       .module                                 = THIS_MODULE,
+       .memory_needed                          = filewriter_memory_needed,
+       .print_debug_info                       = filewriter_print_debug_stats,
+       .save_config_info                       = filewriter_save_config_info,
+       .load_config_info                       = filewriter_load_config_info,
+       .storage_needed                         = filewriter_storage_needed,
+       .initialise                             = filewriter_initialise,
+       .cleanup                                = filewriter_cleanup,
+
+       .write_init                             = filewriter_write_init,
+       .write_cleanup                          = filewriter_write_cleanup,
+       .read_init                              = filewriter_read_init,
+       .read_cleanup                           = filewriter_read_cleanup,
+
+       .ops = {
+               .writer = {
+                .write_chunk           = filewriter_write_chunk,
+                .read_chunk            = filewriter_read_chunk,
+                .noresume_reset        = filewriter_noresume_reset,
+                .storage_available     = filewriter_storage_available,
+                .storage_allocated     = filewriter_storage_allocated,
+                .release_storage       = filewriter_release_storage,
+                .allocate_header_space = filewriter_allocate_header_space,
+                .allocate_storage      = filewriter_allocate_storage,
+                .image_exists          = filewriter_image_exists,
+                .mark_resume_attempted = filewriter_mark_resume_attempted,
+                .write_header_init     = filewriter_write_header_init,
+                .write_header_chunk    = filewriter_write_header_chunk,
+                .write_header_cleanup  = filewriter_write_header_cleanup,
+                .read_header_init      = filewriter_read_header_init,
+                .read_header_chunk     = filewriter_read_header_chunk,
+                .read_header_cleanup   = filewriter_read_header_cleanup,
+                .serialise_extents     = filewriter_serialise_extents,
+                .load_extents          = filewriter_load_extents,
+                .invalidate_image      = filewriter_invalidate_image,
+                .parse_image_location  = filewriter_parse_image_location,
+               }
+       }
+};
+
+/* ---- Registration ---- */
+static __init int filewriter_load(void)
+{
+       int result;
+       int i, numfiles = sizeof(filewriter_proc_data) / sizeof(struct 
suspend_proc_data);
+       
+       printk("Software Suspend FileWriter loading.\n");
+
+       if (!(result = suspend_register_plugin(&filewriterops))) {
+               for (i=0; i< numfiles; i++)
+                       suspend_register_procfile(&filewriter_proc_data[i]);
+       } else
+               printk("Software Suspend FileWriter unable to register!\n");
+       return result;
+}
+
+#ifdef MODULE
+static __exit void filewriter_unload(void)
+{
+       int i, numfiles = sizeof(filewriter_proc_data) / sizeof(struct 
suspend_proc_data);
+
+       printk("Software Suspend FileWriter unloading.\n");
+
+       for (i=0; i< numfiles; i++)
+               suspend_unregister_procfile(&filewriter_proc_data[i]);
+       suspend_unregister_plugin(&filewriterops);
+}
+
+module_init(filewriter_load);
+module_exit(filewriter_unload);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nigel Cunningham");
+MODULE_DESCRIPTION("Suspend2 filewriter");
+#else
+late_initcall(filewriter_load);
+#endif

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to