[PATCH] [45/48] Suspend2 2.1.9.8 for 2.6.12: 621-swsusp-tidy.patch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



diff -ruNp 622-swapwriter.patch-old/kernel/power/suspend_swap.c 622-swapwriter.patch-new/kernel/power/suspend_swap.c
--- 622-swapwriter.patch-old/kernel/power/suspend_swap.c	1970-01-01 10:00:00.000000000 +1000
+++ 622-swapwriter.patch-new/kernel/power/suspend_swap.c	2005-07-05 23:48:59.000000000 +1000
@@ -0,0 +1,1713 @@
+/*
+ * Swapwriter.c
+ *
+ * Copyright 2004-2005 Nigel Cunningham <[email protected]>
+ *
+ * Distributed under GPLv2.
+ * 
+ * This file encapsulates functions for usage of swap space as a
+ * backing store.
+ */
+
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/swapops.h>
+
+#include "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/io.h"
+#include "suspend2_core/ui.h"
+#include "suspend2_core/extent.h"
+#include "suspend2_core/utility.h"
+
+#include "block_io.h"
+
+static struct suspend_plugin_ops swapwriterops;
+
+#define SIGNATURE_VER 6
+#define BYTES_PER_HEADER_PAGE (PAGE_SIZE - sizeof(swp_entry_t))
+
+/* --- Struct of pages stored on disk */
+
+struct swaplink {
+	char dummy[BYTES_PER_HEADER_PAGE];
+	swp_entry_t next;
+};
+
+union diskpage {
+	union swap_header swh;	/* swh.magic is the only member used */
+	struct swaplink link;
+	struct suspend_header sh;
+};
+
+union p_diskpage {
+	union diskpage *pointer;
+	char *ptr;
+        unsigned long address;
+};
+
+// Higher Level
+static int readahead_index = 0, readahead_submit_index = 0;
+static int readahead_allocs = 0, readahead_frees = 0;
+
+static char * swapwriter_buffer = NULL;
+static int swapwriter_buffer_posn = 0;
+static unsigned long * header_link = NULL;
+
+/*
+ * ---------------------------------------------------------------
+ *
+ *     Internal Data Structures
+ *
+ * ---------------------------------------------------------------
+ */
+
+/* header_data contains data that is needed to reload pagedir1, and
+ * is therefore saved in the suspend header.
+ *
+ * Pagedir2 swap comes before pagedir1 swap (save order), and the first swap
+ * entry for pagedir1 to use is set when pagedir2 is written (when we know how
+ * much swap 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 swap (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.
+ */
+
+struct {
+	/* Extent chains for swap & blocks */
+	struct extentchain swapextents;
+	struct extentchain block_chain[MAX_SWAPFILES];
+	
+	/* Location of start of pagedir 1 */
+	struct extent * pd1start_block_extent;
+	int pd1start_chain;
+	int pd1start_extent_number;
+	unsigned long pd1start_block_offset;
+
+	/* Devices used for swap */
+	dev_t swapdevs[MAX_SWAPFILES];
+	char blocksizes[MAX_SWAPFILES];
+
+} header_data;
+
+static dev_t header_device = 0;
+static struct block_device * header_block_device = NULL;
+static int headerblocksize = PAGE_SIZE;
+static int headerblock;
+
+/* For swapfile automatically swapon/off'd. */
+static char swapfilename[256] = "";
+extern asmlinkage long sys_swapon(const char * specialfile, int swap_flags);
+extern asmlinkage long sys_swapoff(const char * specialfile);
+static int suspend_swapon_status = 0;
+
+/*
+ * ---------------------------------------------------------------
+ *
+ *     Current state.
+ *
+ * ---------------------------------------------------------------
+ */
+
+/* Which pagedir are we saving/reloading? Needed so we can know whether to
+ * remember the last swap entry used at the end of writing pageset2, and
+ * get that location when saving or reloading pageset1.*/
+static int current_stream = 0;
+
+/* Pointer to current swap entry being loaded/saved. */
+static struct extent * currentblockextent = NULL;
+static unsigned long currentblockoffset = 0;
+static int currentblockchain = 0;
+static int currentblocksperpage = 0;
+
+/* Header Page Information */
+static int header_pages_allocated = 0;
+static struct submit_params * first_header_submit_info = NULL,
+ * last_header_submit_info = NULL, * current_header_submit_info = NULL;
+
+/*
+ * ---------------------------------------------------------------
+ *
+ *     User Specified Parameters
+ *
+ * ---------------------------------------------------------------
+ */
+
+static int resume_firstblock = 0;
+static int resume_firstblocksize = PAGE_SIZE;
+static dev_t resume_device = 0;
+static struct block_device * resume_block_device = NULL;
+
+struct sysinfo swapinfo;
+static int swapwriter_invalidate_image(void);
+
+static void cleanup_opened_devices(int including_swapdevices)
+{
+	int i;
+
+	/* Cleanup our open_by_devnums.
+	 *
+	 * Other swap devices, opened when reading pageset1, are
+	 * either cleaned up by noresume_reset
+	 */
+
+	if (header_block_device && header_block_device != resume_block_device)
+		blkdev_put(header_block_device);
+
+	if (resume_block_device)
+		blkdev_put(resume_block_device);
+
+	/* When reading pageset1 header */
+	if (including_swapdevices) {
+		for (i = 0; i < MAX_SWAPFILES; i++) {
+			struct block_device * this_dev = swap_info[i].bdev;
+			if (this_dev && this_dev != resume_block_device &&
+					this_dev != header_block_device) {
+				blkdev_put(swap_info[i].bdev);
+				swap_info[i].bdev = NULL;
+			}
+		}
+	}
+	
+	header_block_device = resume_block_device = NULL;
+}
+/* Must be silent - might be called from cat /proc/suspend/debug_info
+ * Returns 0 if was off, -EBUSY if was on, error value otherwise.
+ */
+static int enable_swapfile(void)
+{
+	int activateswapresult = -EINVAL;
+
+	if (suspend_swapon_status)
+		return 0;
+
+	if (swapfilename[0]) {
+		/* Attempt to swap on with maximum priority */
+		activateswapresult = sys_swapon(swapfilename, 0xFFFF);
+		if ((activateswapresult) && (activateswapresult != -EBUSY))
+			printk(name_suspend
+				"The swapfile/partition specified by "
+				"/proc/suspend/swapfile (%s) could not"
+				" be turned on (error %d). Attempting "
+				"to continue.\n",
+				swapfilename, activateswapresult);
+		if (!activateswapresult)
+			suspend_swapon_status = 1;
+	}
+	return activateswapresult;
+}
+
+/* Returns 0 if was on, -EINVAL if was off, error value otherwise */
+static int disable_swapfile(void)
+{
+	int result = -EINVAL;
+	
+	if (!suspend_swapon_status)
+		return 0;
+
+	if (swapfilename[0]) {
+		result = sys_swapoff(swapfilename);
+		if (result == -EINVAL)
+	 		return 0;	/* Wasn't on */
+		if (!result)
+			suspend_swapon_status = 0;
+	}
+
+	return result;
+}
+
+static int manage_swapfile(int enable)
+{
+	static int result;
+
+	if (enable)
+		result = enable_swapfile();
+	else
+		result = disable_swapfile();
+
+	return result;
+}
+
+static void get_header_params(struct submit_params * headerpage)
+{
+	swp_entry_t entry = headerpage->swap_address;
+	int swapfilenum = swp_type(entry);
+	unsigned long offset = swp_offset(entry);
+	struct swap_info_struct * sis = get_swap_info_struct(swapfilenum);
+	sector_t sector = map_swap_page(sis, offset);
+
+	headerpage->dev = sis->bdev,
+	headerpage->block[0] = sector;
+	//headerpage->blocks_used = 1;
+	headerpage->readahead_index = -1;
+}
+
+static inline int get_blocks_per_page(int chain)
+{
+	int result = PAGE_SIZE /
+		suspend_bio_ops.get_block_size(swap_info[chain].bdev);
+	return result;
+}
+
+static int try_to_parse_header_device(void)
+{
+	header_block_device = open_by_devnum(header_device, FMODE_READ);
+
+	if (IS_ERR(header_block_device) || (!header_block_device)) {
+		if (suspend_early_boot_message(1,SUSPEND_CONTINUE_REQ,  
+				"Failed to get access to the "
+				"resume header device.\nYou could be "
+				"booting with a 2.6 kernel when you "
+				"suspended a 2.4 kernel."))
+			swapwriter_invalidate_image();
+		return -EINVAL;
+	}
+
+	if (set_blocksize(header_block_device, PAGE_SIZE) < 0) {
+		if (suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ,
+			"Failed to set the blocksize for a swap device."))
+				do { } while(0);
+		swapwriter_invalidate_image();
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int try_to_open_resume_device(void)
+{
+	resume_block_device = open_by_devnum(resume_device, FMODE_READ);
+
+	if (IS_ERR(resume_block_device) || (!resume_block_device))
+		return 1;
+
+	return 0;
+}
+
+static int try_to_parse_resume_device(char * commandline)
+{
+	struct kstat stat;
+	int error;
+
+	resume_device = name_to_dev_t(commandline);
+
+	if (!resume_device) {
+		error = vfs_stat(commandline, &stat);
+		if (!error)
+			resume_device = stat.rdev;
+	}
+
+	if (!resume_device) {
+		if (test_suspend_state(SUSPEND_TRYING_TO_RESUME))
+			suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ,
+				"Failed to translate \"%s\" into a device id.\n",
+				commandline);
+		else
+			printk(name_suspend "Can't translate \"%s\" into a device id yet.\n",
+					commandline);
+		return 1;
+	}
+
+	if (try_to_open_resume_device()) {
+		suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ,
+			"Failed to get access to \"%s\", where"
+			" the swap header should be found.",
+			commandline);
+		return 1;
+	}
+
+	return 0;
+}
+
+static void open_other_swap_device(int i, dev_t thisdevice)
+{
+	if ((swap_info[i].bdev = open_by_devnum(thisdevice, FMODE_READ)))
+		set_blocksize(swap_info[i].bdev, PAGE_SIZE);
+}
+
+static inline char * get_path_for_swapfile(int which, char * path_page)
+{
+	return d_path(	swap_info[which].swap_file->f_dentry,
+			swap_info[which].swap_file->f_vfsmnt,
+			path_page,
+			PAGE_SIZE);
+}
+
+/* 
+ * If we have read part of the image, we might have filled header_data with
+ * data that should be zeroed out.
+ */
+static void swapwriter_noresume_reset(void)
+{
+	memset((char *) &header_data, 0, sizeof(header_data));
+	cleanup_opened_devices(1);
+}
+
+static int parse_signature(char * header, int restore)
+{
+	int type = -1;
+	
+	if (!memcmp("SWAP-SPACE",header,10))
+		return 0;
+	else if (!memcmp("SWAPSPACE2",header,10))
+		return 1;
+
+	else if (!memcmp("pmdisk", header,6))
+		type = 2;
+	
+	else if (!memcmp("S1SUSP",header,6))
+		type = 4;
+	else if (!memcmp("S2SUSP",header,6))
+		type = 5;
+	
+	else if (!memcmp("z",header,1))
+		type = 12;
+	else if (!memcmp("Z",header,1))
+		type = 13;
+	
+	/* 
+	 * Put bdev of suspend header in last byte of swap header
+	 * (unsigned short)
+	 */
+	if (type > 11) {
+		dev_t * header_ptr = (dev_t *) &header[1];
+		unsigned char * headerblocksize_ptr =
+			(unsigned char *) &header[5];
+		unsigned long * headerblock_ptr = (unsigned long *) &header[6];
+		header_device = *header_ptr;
+		/* 
+		 * We are now using the highest bit of the char to indicate
+		 * whether we have attempted to resume from this image before.
+		 */
+		clear_suspend_state(SUSPEND_RESUMED_BEFORE);
+		if (((int) *headerblocksize_ptr) & 0x80)
+			set_suspend_state(SUSPEND_RESUMED_BEFORE);
+		headerblocksize = 512 * (((int) *headerblocksize_ptr) & 0xf);
+		headerblock = *headerblock_ptr;
+	}
+
+	if ((restore) && (type > 5)) {
+		/* We only reset our own signatures */
+		if (type & 1)
+			memcpy(header,"SWAPSPACE2",10);
+		else
+			memcpy(header,"SWAP-SPACE",10);
+	}
+
+	return type;
+}
+
+/*
+ * prepare_signature
+ */
+
+static int prepare_signature(struct submit_params * header_page_info,
+		char * current_header)
+{
+	int current_type = parse_signature(current_header, 0);
+	dev_t * header_ptr = (dev_t *) (&current_header[1]);
+	unsigned char * headerblocksize_ptr =
+		(unsigned char *) (&current_header[5]);
+	unsigned long * headerblock_ptr =
+		(unsigned long *) (&current_header[6]);
+
+	if ((current_type > 1) && (current_type < 6))
+		return 1;
+
+	if (current_type & 1)
+		current_header[0] = 'Z';
+	else
+		current_header[0] = 'z';
+	*header_ptr = header_page_info->dev->bd_dev;
+	*headerblocksize_ptr =
+		(unsigned char) (header_page_info->dev->bd_block_size >> 9);
+	/* prev is the first/last swap page of the resume area */
+	*headerblock_ptr = (unsigned long) header_page_info->block[0]; 
+	return 0;
+}
+
+extern int signature_check(char * header, int fix);
+
+static int free_swap_pages_for_header(void)
+{
+	if (!first_header_submit_info)
+		return 1;
+
+	while (first_header_submit_info) {
+		struct submit_params * next = first_header_submit_info->next;
+		if (first_header_submit_info->swap_address.val)
+			swap_free(first_header_submit_info->swap_address);
+		kfree(first_header_submit_info);
+		first_header_submit_info = next;
+	}
+	
+	first_header_submit_info = last_header_submit_info = NULL;
+	header_pages_allocated = 0;
+	return 0;
+}
+
+static void get_main_pool_phys_params(void)
+{
+	struct extent * extentpointer = NULL;
+	unsigned long address;
+	int i;
+	int extent_min = -1, extent_max = -1;
+	int last_chain = -1;
+
+	for (i = 0; i < MAX_SWAPFILES; i++)
+		if (header_data.block_chain[i].first)
+			put_extent_chain(&header_data.block_chain[i]);
+
+	extent_for_each(&header_data.swapextents, extentpointer, address) {
+		swp_entry_t swap_address = extent_val_to_swap_entry(address);
+		int swapfilenum = swp_type(swap_address);
+		unsigned long offset = swp_offset(swap_address);
+		struct swap_info_struct * sis = get_swap_info_struct(swapfilenum);
+		sector_t new_sector = map_swap_page(sis, offset);
+
+		if ((new_sector == extent_max + 1) &&
+		    (last_chain == swapfilenum))
+			extent_max++;
+		else {
+			if (extent_min > 0)
+				append_extent_to_extent_chain(
+					&header_data.block_chain[last_chain],
+					extent_min, extent_max);
+			extent_min = extent_max = new_sector;
+			last_chain = swapfilenum;
+		}
+	}
+	if (extent_min > -1)
+		append_extent_to_extent_chain(
+			&header_data.block_chain[last_chain],
+			extent_min, extent_max);
+}
+
+static int swapwriter_storage_allocated(void)
+{
+	int result;
+	result = header_data.swapextents.size + header_pages_allocated;
+	return result;
+}
+
+static int swapwriter_storage_available(void)
+{
+	int result;
+	si_swapinfo(&swapinfo);
+	result = swapinfo.freeswap + swapwriter_storage_allocated();
+	return result;
+}
+
+static int swapwriter_initialise(int starting_cycle)
+{
+	if (starting_cycle)
+		manage_swapfile(1);
+
+	if (!resume_block_device && try_to_open_resume_device())
+		return 1;
+	
+	return 0;
+}
+
+static void swapwriter_cleanup(int ending_cycle)
+{
+	if (ending_cycle)
+		manage_swapfile(0);
+	
+	cleanup_opened_devices(0);
+}
+
+static int swapwriter_release_storage(void)
+{
+	int i = 0;
+
+	if ((TEST_ACTION_STATE(SUSPEND_KEEP_IMAGE)) && test_suspend_state(SUSPEND_NOW_RESUMING))
+		return 0;
+
+	free_swap_pages_for_header();
+	
+	if (header_data.swapextents.first) {
+		/* Free swap entries */
+		struct extent * extentpointer;
+		unsigned long extentvalue;
+		swp_entry_t entry;
+		extent_for_each(&header_data.swapextents, extentpointer, 
+				extentvalue) {
+			entry = extent_val_to_swap_entry(extentvalue);
+			swap_free(entry);
+		}
+		put_extent_chain(&header_data.swapextents);
+		
+		for (i = 0; i < MAX_SWAPFILES; i++)
+			if (header_data.block_chain[i].first)
+				put_extent_chain(&header_data.block_chain[i]);
+	}
+	
+	return 0;
+}
+
+static int swapwriter_allocate_header_space(int space_really_requested)
+{
+	/* space_requested was going to be in bytes... not yet */
+	int i;
+	int ret = 0;
+	long space_requested;
+
+	/* 
+	 * Up to here in the process, we haven't taken account of the fact
+	 * that we need an extra four bytes per 4092 bytes written for link
+	 * to the next page on which the header will be written. We factor
+	 * that in here.
+	 */
+	space_requested = ((long) space_really_requested) << PAGE_SHIFT;
+	space_requested = ((space_really_requested + 4091) / 4092);
+	space_requested = (space_requested * 4 + 4091) / 4092;
+	space_requested += space_really_requested;
+	
+	for (i=(header_pages_allocated+1); 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) {
+			header_pages_allocated = i - 1;
+			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;
+
+		/* Get swap entry */
+		new_submit_param->swap_address = get_swap_page();
+		
+		if ((!new_submit_param->swap_address.val) &&
+			       (header_data.swapextents.first)) {
+			/*
+			 *  Steal one from pageset swap chain. If, as a result,
+			 *  it is too small, more swap will be allocated or
+			 *  memory eaten.
+			 */
+
+			new_submit_param->swap_address =
+				extent_val_to_swap_entry(
+					header_data.swapextents.first->minimum);
+			if (header_data.swapextents.first->minimum <
+					header_data.swapextents.first->maximum)
+				header_data.swapextents.first->minimum++;
+			else {
+				struct extent * oldfirst =
+					header_data.swapextents.first;
+				header_data.swapextents.first = oldfirst->next;
+				header_data.swapextents.frees++;
+				if (header_data.swapextents.last == oldfirst)
+					header_data.swapextents.last = NULL;
+				put_extent(oldfirst);
+			}
+			
+			header_data.swapextents.size--;
+
+			/*
+			 * Recalculate block chains for main pool.
+			 * We don't assume blocks are at start of a chain and
+			 * don't know how many blocks per swap entry.
+			 */
+			get_main_pool_phys_params();
+		}
+		if (!new_submit_param->swap_address.val) {
+			free_swap_pages_for_header();
+			printk("Unable to allocate swap page for header.\n");
+			ret = -ENOMEM;
+			goto out;
+		}
+		
+		get_header_params(new_submit_param);
+	}
+	header_pages_allocated = space_requested;
+out:
+	return ret;
+}
+
+static int swapwriter_allocate_storage(int space_requested)
+{
+	int i, result = 0;
+	int pages_to_get = space_requested - header_data.swapextents.size;
+	int extent_min = -1, extent_max = -1;
+	
+	if (pages_to_get < 1)
+		return 0;
+
+	for(i=0; i < pages_to_get; i++) {
+		swp_entry_t entry;
+		int new_value;
+
+		entry = get_swap_page();
+		if (!entry.val) {
+			result = -ENOSPC;
+			goto out;
+		}
+		
+		new_value = swap_entry_to_extent_val(entry);
+		if (new_value == extent_max + 1)
+			extent_max++;
+		else {
+			if (extent_min > -1)
+				append_extent_to_extent_chain(
+					&header_data.swapextents,
+					extent_min, extent_max);
+			extent_min = extent_max = new_value;
+		}
+	}
+
+	if (extent_min > -1)
+		append_extent_to_extent_chain(
+			&header_data.swapextents,
+		       extent_min, extent_max);
+
+out:
+	get_main_pool_phys_params();
+	return result;
+}
+
+static int swapwriter_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.
+	 */
+
+	while (bytes_left) {
+		char * source_start = buffer + buffer_size - bytes_left;
+		char * dest_start = swapwriter_buffer + swapwriter_buffer_posn;
+		int dest_capacity = BYTES_PER_HEADER_PAGE - swapwriter_buffer_posn;
+		swp_entry_t next_header_page;
+		if (bytes_left <= dest_capacity) {
+			memcpy(dest_start, source_start, bytes_left);
+			swapwriter_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 =
+				swp_entry(swp_type(
+				current_header_submit_info->next->swap_address),
+				current_header_submit_info->next->block[0]);
+
+			*header_link = next_header_page.val;
+		}
+
+		current_header_submit_info->page =
+			virt_to_page(swapwriter_buffer);
+		check_shift_keys(0, NULL);
+		suspend_bio_ops.submit_io(WRITE, current_header_submit_info, 0);
+
+		swapwriter_buffer_posn = 0;
+		current_header_submit_info = current_header_submit_info->next;
+	}
+
+	return 0;
+}
+
+static int swapwriter_write_header_init(void)
+{
+	int i;
+	struct extent * extent;
+
+	for (i = 0; i < MAX_SWAPFILES; i++)
+		if (swap_info[i].swap_file) {
+			header_data.swapdevs[i] = swap_info[i].bdev->bd_dev;
+			header_data.blocksizes[i] =
+				block_size(swap_info[i].bdev);
+		}
+
+	swapwriter_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+	header_link =
+		(unsigned long *) (swapwriter_buffer + BYTES_PER_HEADER_PAGE);
+
+	current_header_submit_info = first_header_submit_info;
+	
+	header_data.pd1start_extent_number = 1;
+	extent = header_data.block_chain[header_data.pd1start_chain].first;
+
+	while (extent != header_data.pd1start_block_extent) {
+		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.
+	 */
+	return swapwriter_write_header_chunk((char *) &header_data, 
+			sizeof(header_data));
+}
+
+static int swapwriter_write_header_cleanup(void)
+{
+	/* Write any unsaved data */
+	if (swapwriter_buffer_posn) {
+		*header_link = 0;
+
+		current_header_submit_info->page =
+			virt_to_page(swapwriter_buffer);
+		suspend_bio_ops.submit_io(WRITE, 
+				current_header_submit_info, 0);
+	}
+
+	/* Adjust swap header */
+	suspend_bio_ops.bdev_page_io(READ, resume_block_device, resume_firstblock,
+			virt_to_page(swapwriter_buffer));
+
+	prepare_signature(first_header_submit_info,
+		((union swap_header *) swapwriter_buffer)->magic.magic);
+		
+	suspend_bio_ops.bdev_page_io(WRITE, resume_block_device, resume_firstblock,
+			virt_to_page(swapwriter_buffer));
+
+	free_pages((unsigned long) swapwriter_buffer, 0);
+	swapwriter_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 swapwriter configuration section
+ *    of the header and set up block device info so we can read
+ *    the rest of the header & image.
+ *
+ * Returns:
+ * May not return if user choose to reboot at a warning.
+ * -EINVAL if cannot resume at this time. Booting should continue
+ * normally.
+ */
+
+static int swapwriter_read_header_init(void)
+{
+	int i;
+	
+	BUG_ON(!resume_block_device);
+
+	swapwriter_buffer = (char *) get_zeroed_page(GFP_ATOMIC);
+
+	if (!header_device) {
+		printk("read_header_init called when we haven't "
+				"verified there is an image!\n");
+		return -EINVAL;
+	}
+
+	/* 
+	 * If the header is not on the resume_device, get the resume device first.
+	 */
+	if (header_device != resume_device) {
+		int result = try_to_parse_header_device();
+
+		if (result)
+			return result;
+	} else
+		header_block_device = resume_block_device;
+
+	/* Read swapwriter configuration */
+	suspend_bio_ops.bdev_page_io(READ, header_block_device, headerblock,
+			virt_to_page((unsigned long) swapwriter_buffer));
+	
+	memcpy(&header_data, swapwriter_buffer, sizeof(header_data));
+	
+	/* Restore device info */
+	for (i = 0; i < MAX_SWAPFILES; i++) {
+		dev_t thisdevice = header_data.swapdevs[i];
+		
+		swap_info[i].bdev = NULL;
+
+		if (!thisdevice)
+			continue;
+
+		if (thisdevice == resume_device) {
+			swap_info[i].bdev = resume_block_device;
+			/* Mark as used so the device doesn't get suspended. */
+			swap_info[i].swap_file = (struct file *) 0xffffff;
+			continue;
+		}
+
+		if (thisdevice == header_device) {
+			swap_info[i].bdev = header_block_device;
+			/* Mark as used so the device doesn't get suspended. */
+			swap_info[i].swap_file = (struct file *) 0xffffff;
+			continue;
+		}
+
+		open_other_swap_device(i, thisdevice);
+		swap_info[i].swap_file = (struct file *) 0xffffff;
+	}
+
+	swapwriter_buffer_posn = sizeof(header_data);
+
+	return 0;
+}
+
+static int swapwriter_read_header_chunk(char * buffer, int buffer_size)
+{
+	int bytes_left = buffer_size, ret = 0;
+	
+	check_shift_keys(0, "");
+
+	/* Read a chunk of the header */
+	while ((bytes_left) && (!ret)) {
+		swp_entry_t next =
+		   ((union p_diskpage) swapwriter_buffer).pointer->link.next;
+		struct block_device * dev = swap_info[swp_type(next)].bdev;
+		int pos = swp_offset(next);
+		char * dest_start = buffer + buffer_size - bytes_left;
+		char * source_start =
+			swapwriter_buffer + swapwriter_buffer_posn;
+		int source_capacity =
+			BYTES_PER_HEADER_PAGE - swapwriter_buffer_posn;
+
+		if (bytes_left <= source_capacity) {
+			memcpy(dest_start, source_start, bytes_left);
+			swapwriter_buffer_posn += bytes_left;
+			return buffer_size;
+		}
+
+		/* Next to read the next page */
+		memcpy(dest_start, source_start, source_capacity);
+		bytes_left -= source_capacity;
+
+		suspend_bio_ops.bdev_page_io(READ, dev, pos, virt_to_page(swapwriter_buffer));
+
+		swapwriter_buffer_posn = 0;
+	}
+
+	return buffer_size - bytes_left;
+}
+
+static int swapwriter_read_header_cleanup(void)
+{
+	free_pages((unsigned long) swapwriter_buffer, 0);
+	return 0;
+}
+
+static int swapwriter_serialise_extents(void)
+{
+	int i;
+
+	serialise_extent_chain(&header_data.swapextents);
+	
+	for (i = 0; i < MAX_SWAPFILES; i++)
+		serialise_extent_chain(&header_data.block_chain[i]);
+	
+	return 0;
+}
+
+static int swapwriter_load_extents(void)
+{
+	int i;
+	struct extent * extent;
+	
+	load_extent_chain(&header_data.swapextents);
+
+	for (i = 0; i < MAX_SWAPFILES; i++)
+		load_extent_chain(&header_data.block_chain[i]);
+
+	extent = header_data.block_chain[header_data.pd1start_chain].first;
+
+	i = 1;
+	while (i < header_data.pd1start_extent_number) {
+		extent = extent->next;
+		i++;
+	}
+
+	header_data.pd1start_block_extent = extent;
+
+	return 0;
+}
+
+static int swapwriter_write_init(int stream_number)
+{
+	current_stream = stream_number;
+
+	if (current_stream == 1) {
+		currentblockextent = header_data.pd1start_block_extent;
+		currentblockoffset = header_data.pd1start_block_offset;
+		currentblockchain = header_data.pd1start_chain;
+	} else
+		for (currentblockchain = 0; currentblockchain < MAX_SWAPFILES;
+				currentblockchain++)
+			if (header_data.block_chain[currentblockchain].first) {
+				currentblockextent =
+					header_data.
+					 block_chain[currentblockchain].first;
+				currentblockoffset = currentblockextent->minimum;
+				break;
+			}
+
+	BUG_ON(!currentblockextent);
+
+	currentblocksperpage = get_blocks_per_page(currentblockchain);
+
+	suspend_bio_ops.reset_io_stats();
+
+	return 0;
+}
+
+static int swapwriter_write_chunk(struct page * buffer_page)
+{
+	int i;
+	struct submit_params submit_params;
+
+	if (TEST_ACTION_STATE(SUSPEND_TEST_FILTER_SPEED))
+		return 0;
+		
+	if (currentblockchain == MAX_SWAPFILES) {
+		printk("Error! We have run out of blocks for writing data.\n");
+		return -ENOSPC;
+	}
+	
+	if (!currentblockextent) {
+		do {
+			currentblockchain++;
+		} while ((currentblockchain < MAX_SWAPFILES) &&
+		   (!header_data.block_chain[currentblockchain].first));
+
+		/* We can validly not have a new blockextent. We
+		 * might be compressing data and the user was
+		 * too optimistic in setting the compression
+		 * ratio or we're just copying the pageset. */
+
+		if (currentblockchain == MAX_SWAPFILES) {
+			printk("Argh. Ran out of block chains.\n");
+			return -ENOSPC;
+		}
+				
+		currentblockextent = 
+		 header_data.block_chain[currentblockchain].first;
+		currentblockoffset = currentblockextent->minimum;
+		currentblocksperpage = get_blocks_per_page(currentblockchain);
+	}
+
+	submit_params.readahead_index = -1;
+	submit_params.page = buffer_page;
+	submit_params.dev = swap_info[currentblockchain].bdev;
+		
+	/* Get the blocks */
+	submit_params.block[0] = currentblockoffset;
+	for (i = 0; i < currentblocksperpage; i++)
+		GET_EXTENT_NEXT(currentblockextent, currentblockoffset);
+
+	suspend_bio_ops.submit_io(WRITE, &submit_params, 0);
+
+	check_shift_keys(0, NULL);
+
+	return 0;
+}
+
+static int swapwriter_write_cleanup(void)
+{
+	if (current_stream == 2) {
+		header_data.pd1start_block_extent = currentblockextent;
+		header_data.pd1start_block_offset = currentblockoffset;
+		header_data.pd1start_chain = currentblockchain;
+	}
+	
+	suspend_bio_ops.finish_all_io();
+	
+	suspend_bio_ops.check_io_stats();
+
+	return 0;
+}
+
+static int swapwriter_read_init(int stream_number)
+{
+	current_stream = stream_number;
+
+	if (current_stream == 1) {
+		currentblockextent = header_data.pd1start_block_extent;
+		currentblockoffset = header_data.pd1start_block_offset;
+		currentblockchain = header_data.pd1start_chain;
+	} else {
+		currentblockextent = NULL;
+		currentblockoffset = 0;
+		currentblockchain = 0;
+		for (currentblockchain = 0; currentblockchain < MAX_SWAPFILES;
+			       currentblockchain++)
+			if (header_data.block_chain[currentblockchain].first) {
+				currentblockextent =
+					header_data.block_chain[currentblockchain].first;
+				currentblockoffset = currentblockextent->minimum;
+				break;
+			}
+
+		BUG_ON(!currentblockextent);
+	}
+	
+	currentblocksperpage = get_blocks_per_page(currentblockchain);
+
+	suspend_bio_ops.reset_io_stats();
+
+	readahead_index = readahead_submit_index = -1;
+	readahead_allocs = readahead_frees = 0;
+
+	return 0;
+}
+
+static int swapwriter_begin_read_chunk(struct page * page, 
+		int readahead_index, int sync)
+{
+	int i;
+	struct submit_params submit_params;
+
+	if (currentblockchain == MAX_SWAPFILES) {
+		/* Readahead might ask us to read too many blocks */
+		printk("Currentblockchain == MAX_SWAPFILES. Begin_read_chunk returning -ENODATA.\n");
+		return -ENODATA;
+	}
+
+	if (!currentblockextent) {
+		do {
+			currentblockchain++;
+		} while ((currentblockchain < MAX_SWAPFILES) &&
+			 (!header_data.block_chain[currentblockchain].first));
+
+		/* We can validly not have a new blockextent. We
+		 * might have allocated exactly the right amount
+		 * of swap for the image and be reading the last
+		 * block now.
+		 */
+
+		/* Readahead might ask us to read too many blocks */
+		if (currentblockchain == MAX_SWAPFILES)
+			return -ENODATA;
+
+		currentblockextent =
+		  header_data.block_chain[currentblockchain].first;
+		currentblockoffset = currentblockextent->minimum;
+		currentblocksperpage = get_blocks_per_page(currentblockchain);
+	}
+	
+	submit_params.readahead_index = readahead_index;
+	submit_params.page = page;
+	submit_params.dev = swap_info[currentblockchain].bdev;
+		
+	/* Get the blocks. There is no chance that they span chains. */
+	submit_params.block[0] = currentblockoffset;
+	for (i = 0; i < currentblocksperpage; i++)
+		GET_EXTENT_NEXT(currentblockextent, currentblockoffset);
+
+	if ((i = suspend_bio_ops.submit_io(READ, &submit_params, sync)))
+		return -EPERM;
+
+	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 swapwriter_read_chunk(struct page * buffer_page, int sync)
+{
+	static int last_result;
+	unsigned long * virt;
+
+	/* If we only use readahead when reading synchronously. */
+	if (sync == SUSPEND_ASYNC)
+		return swapwriter_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 -EIO;
+		goto wait;
+	}
+	
+	do {
+		if (suspend_bio_ops.prepare_readahead(readahead_submit_index))
+			break;
+
+		readahead_allocs++;
+
+		last_result = swapwriter_begin_read_chunk(
+			suspend_bio_ops.readahead_pages[readahead_submit_index], 
+			readahead_submit_index, SUSPEND_ASYNC);
+		if (last_result) {
+			suspend_bio_ops.cleanup_readahead(readahead_submit_index);
+			readahead_frees++;
+			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 swapwriter_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;
+}
+
+/* swapwriter_invalidate_image
+ * 
+ */
+static int swapwriter_invalidate_image(void)
+{
+	union p_diskpage cur;
+	int result = 0;
+	char newsig[11];
+	
+	cur.address = get_zeroed_page(GFP_ATOMIC);
+	if (!cur.address) {
+		printk("Unable to allocate a page for restoring the swap signature.\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * If nr_suspends == 0, we must be booting, so no swap pages
+	 * will be recorded as used yet.
+	 */
+
+	if (nr_suspends > 0)
+		swapwriter_release_storage();
+
+	/* 
+	 * We don't do a sanity check here: we want to restore the swap 
+	 * whatever version of kernel made the suspend image.
+	 * 
+	 * We need to write swap, but swap may not be enabled so
+	 * we write the device directly
+	 */
+	
+	suspend_bio_ops.bdev_page_io(READ, resume_block_device,
+			resume_firstblock, virt_to_page(cur.pointer));
+
+	result = parse_signature(cur.pointer->swh.magic.magic, 1);
+		
+	if (result < 4)
+		goto out;
+
+	strncpy(newsig, cur.pointer->swh.magic.magic, 10);
+	newsig[10] = 0;
+
+	suspend_bio_ops.bdev_page_io(WRITE, resume_block_device, resume_firstblock,
+			virt_to_page(cur.pointer));
+
+	if (!nr_suspends)
+		printk(KERN_WARNING name_suspend "Image invalidated.\n");
+out:
+	suspend_bio_ops.finish_all_io();
+	free_pages(cur.address, 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 swapwriter_memory_needed(void)
+{
+	return 1;
+}
+
+/* Print debug info
+ *
+ * Description:
+ */
+
+static int swapwriter_print_debug_stats(char * buffer, int size)
+{
+	int len = 0;
+	struct sysinfo sysinfo;
+	
+	if (active_writer != &swapwriterops) {
+		len = suspend_snprintf(buffer, size, "- Swapwriter inactive.\n");
+		return len;
+	}
+
+	len = suspend_snprintf(buffer, size, "- Swapwriter active.\n");
+	if (swapfilename[0])
+		len+= suspend_snprintf(buffer+len, size-len,
+			"  Attempting to automatically swapon: %s.\n", swapfilename);
+
+	si_swapinfo(&sysinfo);
+	
+	len+= suspend_snprintf(buffer+len, size-len, "  Swap available for image: %ld pages.\n",
+			sysinfo.freeswap + swapwriter_storage_allocated());
+
+	return len;
+}
+
+/*
+ * Storage needed
+ *
+ * Returns amount of space in the swap header required
+ * for the swapwriter's data. This ignores the links between
+ * pages, which we factor in when allocating the space.
+ *
+ * We ensure the space is allocated, but actually save the
+ * data from write_header_init and therefore don't also define a
+ * save_config_info routine.
+ */
+static unsigned long swapwriter_storage_needed(void)
+{
+	return sizeof(header_data);
+}
+
+/*
+ * Image_exists
+ *
+ */
+
+static int swapwriter_image_exists(void)
+{
+	int signature_found;
+	union p_diskpage diskpage;
+	
+	if (!resume_device) {
+		printk("Not even trying to read header "
+				"because resume_device is not set.\n");
+		return 0;
+	}
+	
+	if (!resume_block_device && !try_to_open_resume_device())
+		return 0;
+
+	diskpage.address = get_zeroed_page(GFP_ATOMIC);
+
+	suspend_bio_ops.bdev_page_io(READ, resume_block_device,
+			resume_firstblock, virt_to_page(diskpage.ptr));
+	suspend_bio_ops.finish_all_io();
+
+	signature_found = parse_signature(diskpage.pointer->swh.magic.magic, 0);
+	free_pages(diskpage.address, 0);
+
+	if (signature_found < 2) {
+		return 0;	/* Normal swap space */
+	} else if (signature_found == -1) {
+		printk(KERN_ERR name_suspend
+			"Unable to find a signature. Could you have moved "
+			"a swap file?\n");
+		return 0;
+	} else if (signature_found < 6) {
+		if ((!(test_suspend_state(SUSPEND_NORESUME_SPECIFIED)))
+				&& suspend_early_boot_message(1,
+				SUSPEND_CONTINUE_REQ,
+				"Detected the signature of an alternate "
+				"implementation.\n"))
+			set_suspend_state(SUSPEND_NORESUME_SPECIFIED);
+		return 0;
+	} else if ((signature_found >> 1) != SIGNATURE_VER) {
+		if ((!(test_suspend_state(SUSPEND_NORESUME_SPECIFIED))) &&
+			suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ,
+			 "Found a different style suspend image signature."))
+			set_suspend_state(SUSPEND_NORESUME_SPECIFIED);
+	}
+
+	return 1;
+}
+
+/*
+ * Mark resume attempted.
+ *
+ * Record that we tried to resume from this image.
+ */
+
+static void swapwriter_mark_resume_attempted(void)
+{
+	union p_diskpage diskpage;
+	int signature_found;
+	
+	if (!resume_device) {
+		printk("Not even trying to record attempt at resuming"
+				" because resume_device is not set.\n");
+		return;
+	}
+	
+	diskpage.address = get_zeroed_page(GFP_ATOMIC);
+
+	/* FIXME: Make sure bdev_page_io handles wrong parameters */
+	suspend_bio_ops.bdev_page_io(READ, resume_block_device, resume_firstblock, virt_to_page(diskpage.ptr));
+	signature_found = parse_signature(diskpage.pointer->swh.magic.magic, 0);
+
+	switch (signature_found) {
+		case 12:
+		case 13:
+			diskpage.pointer->swh.magic.magic[5] |= 0x80;
+			break;
+	}
+	
+	suspend_bio_ops.bdev_page_io(WRITE, resume_block_device, resume_firstblock,
+			virt_to_page(diskpage.ptr));
+	suspend_bio_ops.finish_all_io();
+	free_pages(diskpage.address, 0);
+	
+	cleanup_opened_devices(1);
+	return;
+}
+
+/*
+ * Parse Image Location
+ *
+ * Attempt to parse a resume2= parameter.
+ * Swap Writer accepts:
+ * resume2=swap:DEVNAME[:FIRSTBLOCK][@BLOCKSIZE]
+ *
+ * Where:
+ * DEVNAME is convertable to a dev_t by name_to_dev_t
+ * FIRSTBLOCK is the location of the first block in the swap file
+ * (specifying for a swap partition is nonsensical but not prohibited).
+ * 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 swapwriter refusing to
+ * save an image, and a reboot with correct parameters will be
+ * necessary.
+ */
+
+static int swapwriter_parse_image_location(char * commandline, int only_writer)
+{
+	char *thischar, *devstart = NULL, *colon = NULL, *at_symbol = NULL;
+	union p_diskpage diskpage;
+	int signature_found, result = -EINVAL, temp_result;
+
+	if (strncmp(commandline, "swap:", 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)
+		resume_firstblock = (int) simple_strtoul(colon + 1, NULL, 0);
+	else
+		resume_firstblock = 0;
+
+	if (at_symbol) {
+		resume_firstblocksize = (int) simple_strtoul(at_symbol + 1, NULL, 0);
+		if (resume_firstblocksize & 0x1FF)
+			printk("Swapwriter: Blocksizes are usually a multiple of 512. Don't expect this to work!\n");
+	} else
+		resume_firstblocksize = 4096;
+	
+	temp_result = try_to_parse_resume_device(devstart);
+
+	if (colon)
+		*colon = ':';
+	if (at_symbol)
+		*at_symbol = '@';
+
+	if (temp_result)
+		return -EINVAL;
+
+	diskpage.address = get_zeroed_page(GFP_ATOMIC);
+	if (!diskpage.address) {
+		printk(KERN_ERR name_suspend "Swapwriter: Failed to allocate a diskpage for I/O.\n");
+		return -ENOMEM;
+	}
+
+	if ((suspend_bio_ops.get_block_size(resume_block_device) 
+				!= resume_firstblocksize) &&
+	     (suspend_bio_ops.set_block_size(resume_block_device, resume_firstblocksize)
+	    			 == -EINVAL)) {
+		printk(KERN_ERR name_suspend "Swapwriter: Failed to set requested block size.\n");
+		goto invalid;
+	}
+
+	temp_result = suspend_bio_ops.bdev_page_io(READ, resume_block_device, resume_firstblock, virt_to_page(diskpage.ptr));
+	suspend_bio_ops.finish_all_io();
+	
+	if (temp_result) {
+		printk(KERN_ERR name_suspend "Swapwriter: Failed to submit I/O.\n");
+		goto invalid;
+	}
+	
+	signature_found = parse_signature(diskpage.pointer->swh.magic.magic, 0);
+
+	if (signature_found != -1) {
+		printk(KERN_ERR name_suspend "Swapwriter: Signature found.\n");
+		result = 0;
+	} else
+		printk(KERN_ERR name_suspend "Swapwriter: No swap signature found at specified location.\n");
+invalid:
+	free_page((unsigned long) diskpage.address);
+	return result;
+
+}
+
+static int header_locations_read_proc(char * page, char ** start, off_t off, int count,
+		int *eof, void *data)
+{
+	int i, printedpartitionsmessage = 0, len = 0, haveswap = 0, device_block_size;
+	struct inode *swapf = 0;
+	int zone;
+	char * path_page = (char *) __get_free_page(GFP_KERNEL);
+	char * path;
+	int path_len;
+	
+	*eof = 1;
+	if (!page)
+		return 0;
+
+	for (i = 0; i < MAX_SWAPFILES; i++) {
+		if (!swap_info[i].swap_file)
+			continue;
+		
+		if (S_ISBLK(swap_info[i].swap_file->f_dentry->d_inode->i_mode)) {
+			haveswap = 1;
+			if (!printedpartitionsmessage) {
+				len += sprintf(page + len, 
+					"For swap partitions, simply use the format: resume2=swap:/dev/hda1.\n");
+				printedpartitionsmessage = 1;
+			}
+		} else {
+			path_len = 0;
+			
+			path = get_path_for_swapfile(i, path_page);
+			path_len = sprintf(path_page, "%-31s ", path);
+			
+			haveswap = 1;
+			swapf = swap_info[i].swap_file->f_dentry->d_inode;
+			device_block_size = block_size(swap_info[i].bdev);
+			if (!(zone = bmap(swapf,0))) {
+				len+= sprintf(page + len, 
+					"Swapfile %-31s has been corrupted. Reuse mkswap on it and try again.\n",
+					path_page);
+			} else {
+				len+= sprintf(page + len, "For swapfile `%s`, use resume2=swap:/dev/<partition name>:0x%x@%d.\n",
+						path_page,
+						zone, device_block_size);
+			}
+
+		}
+	}
+	
+	if (!haveswap)
+		len = sprintf(page, "You need to turn on swap partitions before examining this file.\n");
+
+	free_pages((unsigned long) path_page, 0);
+	return len;
+}
+
+static struct suspend_proc_data swapwriter_proc_data[] = {
+	{
+	 .filename			= "swapfilename",
+	 .permissions			= PROC_RW,
+	 .type				= SUSPEND_PROC_DATA_STRING,
+	 .data = {
+		.string = {
+			.variable	= swapfilename,
+			.max_length	= 255,
+		}
+	 }
+	},
+
+	{
+	 .filename			= "headerlocations",
+	 .permissions			= PROC_READONLY,
+	 .type				= SUSPEND_PROC_DATA_CUSTOM,
+	 .data = {
+		 .special = {
+			.read_proc 	= header_locations_read_proc,
+		}
+	 }
+	},
+
+	{ .filename			= "disable_swapwriter",
+	  .permissions			= PROC_RW,
+	  .type				= SUSPEND_PROC_DATA_INTEGER,
+	  .data = {
+		.integer = {
+			.variable	= &swapwriterops.disabled,
+			.minimum	= 0,
+			.maximum	= 1,
+		}
+	  },
+	  .write_proc			= attempt_to_parse_resume_device,
+	}
+};
+
+static struct suspend_plugin_ops swapwriterops = {
+	.type					= WRITER_PLUGIN,
+	.name					= "Swap Writer",
+	.module					= THIS_MODULE,
+	.memory_needed				= swapwriter_memory_needed,
+	.print_debug_info			= swapwriter_print_debug_stats,
+	.storage_needed				= swapwriter_storage_needed,
+	.initialise				= swapwriter_initialise,
+	.cleanup				= swapwriter_cleanup,
+
+	.write_init				= swapwriter_write_init,
+	.write_cleanup				= swapwriter_write_cleanup,
+	.read_init				= swapwriter_read_init,
+	.read_cleanup				= swapwriter_read_cleanup,
+
+	.ops = {
+		.writer = {
+		 .write_chunk		= swapwriter_write_chunk,
+		 .read_chunk		= swapwriter_read_chunk,
+		 .noresume_reset	= swapwriter_noresume_reset,
+		 .storage_available 	= swapwriter_storage_available,
+		 .storage_allocated	= swapwriter_storage_allocated,
+		 .release_storage	= swapwriter_release_storage,
+		 .allocate_header_space	= swapwriter_allocate_header_space,
+		 .allocate_storage	= swapwriter_allocate_storage,
+		 .image_exists		= swapwriter_image_exists,
+		 .mark_resume_attempted	= swapwriter_mark_resume_attempted,
+		 .write_header_init	= swapwriter_write_header_init,
+		 .write_header_chunk	= swapwriter_write_header_chunk,
+		 .write_header_cleanup	= swapwriter_write_header_cleanup,
+		 .read_header_init	= swapwriter_read_header_init,
+		 .read_header_chunk	= swapwriter_read_header_chunk,
+		 .read_header_cleanup	= swapwriter_read_header_cleanup,
+		 .serialise_extents	= swapwriter_serialise_extents,
+		 .load_extents		= swapwriter_load_extents,
+		 .invalidate_image	= swapwriter_invalidate_image,
+		 .parse_image_location	= swapwriter_parse_image_location,
+		}
+	}
+};
+
+/* ---- Registration ---- */
+static __init int swapwriter_load(void)
+{
+	int result;
+	int i, numfiles = sizeof(swapwriter_proc_data) / sizeof(struct suspend_proc_data);
+	
+	printk("Software Suspend Swap Writer loading.\n");
+	if (!(result = suspend_register_plugin(&swapwriterops))) {
+
+		for (i=0; i< numfiles; i++)
+			suspend_register_procfile(&swapwriter_proc_data[i]);
+	} else
+		printk("Software Suspend Swap Writer unable to register!\n");
+	return result;
+}
+
+#ifdef MODULE
+static __exit void swapwriter_unload(void)
+{
+	int i, numfiles = sizeof(swapwriter_proc_data) / sizeof(struct suspend_proc_data);
+
+	printk("Software Suspend Swap Writer unloading.\n");
+
+	for (i=0; i< numfiles; i++)
+		suspend_unregister_procfile(&swapwriter_proc_data[i]);
+	suspend_unregister_plugin(&swapwriterops);
+}
+
+module_init(swapwriter_load);
+module_exit(swapwriter_unload);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nigel Cunningham");
+MODULE_DESCRIPTION("Suspend2 swap writer");
+#else
+late_initcall(swapwriter_load);
+#endif

-
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/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]
  Powered by Linux