From: Nigel Cunningham <nigel@suspend2.net>
To: linux-kernel@vger.kernel.org
Subject: [PATCH] [47/48] Suspend2 2.1.9.8 for 2.6.12: 623-generic-block-io.patch
Date: Wed, 6 Jul 2005 12:20:44 +1000 [thread overview]
Message-ID: <1120616444110@foobar.com> (raw)
In-Reply-To: <11206164393426@foobar.com>
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 <nigel@suspend2.net>
+ *
+ * Distributed under GPLv2.
+ *
+ * This file encapsulates functions for usage of a simple file as a
+ * backing store. It is based upon the swapwriter, and shares the
+ * same basic working. Here, though, we have nothing to do with
+ * swapspace, and only one device to worry about.
+ *
+ * The user can just
+ *
+ * echo Suspend2 > /path/to/my_file
+ *
+ * and
+ *
+ * echo /path/to/my_file > /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[: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 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
next prev parent reply other threads:[~2005-07-06 4:17 UTC|newest]
Thread overview: 187+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-07-06 2:20 [0/48] Suspend2 2.1.9.8 for 2.6.12 Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [4/48] Suspend2 2.1.9.8 for 2.6.12: 302-init-hooks.patch Nigel Cunningham
2005-07-06 8:38 ` Shaohua Li
2005-07-06 8:35 ` Nigel Cunningham
2005-07-06 15:38 ` Bernard Blackham
2005-07-06 2:20 ` [PATCH] [5/48] Suspend2 2.1.9.8 for 2.6.12: 350-workthreads.patch Nigel Cunningham
2005-07-10 23:04 ` Christoph Hellwig
2005-07-12 6:40 ` Nigel Cunningham
2005-07-12 10:57 ` Pavel Machek
2005-07-12 11:07 ` Nigel Cunningham
2005-07-12 11:15 ` Pavel Machek
2005-07-12 11:25 ` Nigel Cunningham
2005-07-12 11:25 ` Pavel Machek
2005-07-12 11:45 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [3/48] Suspend2 2.1.9.8 for 2.6.12: 301-proc-acpi-sleep-activate-hook.patch Nigel Cunningham
2005-07-10 23:03 ` Christoph Hellwig
2005-07-12 6:45 ` Nigel Cunningham
[not found] ` <E1DsHMp-00062f-00@chiark.greenend.org.uk>
2005-07-12 10:07 ` Nigel Cunningham
2005-07-12 10:22 ` Matthew Garrett
2005-07-12 10:34 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [2/48] Suspend2 2.1.9.8 for 2.6.12: 300-reboot-handler-hook.patch Nigel Cunningham
2005-07-06 10:08 ` Pekka Enberg
2005-07-06 10:18 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [6/48] Suspend2 2.1.9.8 for 2.6.12: 351-syncthreads.patch Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [1/48] Suspend2 2.1.9.8 for 2.6.12: submit_intro Nigel Cunningham
2005-07-06 6:37 ` Pekka Enberg
2005-07-06 8:12 ` Pavel Machek
2005-07-06 2:20 ` [PATCH] [11/48] Suspend2 2.1.9.8 for 2.6.12: 401-e820-table-support.patch Nigel Cunningham
2005-07-06 3:35 ` Zwane Mwaikambo
2005-07-06 3:44 ` Nigel Cunningham
2005-07-06 8:04 ` Pavel Machek
2005-07-06 13:29 ` Zwane Mwaikambo
2005-07-06 2:20 ` [PATCH] [14/48] Suspend2 2.1.9.8 for 2.6.12: 404-check-mounts-support.patch Nigel Cunningham
2005-07-06 8:15 ` Pavel Machek
2005-07-06 8:30 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [10/48] Suspend2 2.1.9.8 for 2.6.12: 360-reset-kswapd-max-order-after-resume.patch Nigel Cunningham
2005-07-10 23:09 ` Christoph Hellwig
2005-07-12 9:05 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [12/48] Suspend2 2.1.9.8 for 2.6.12: 402-mtrr-remove-sysdev.patch Nigel Cunningham
2005-07-10 23:07 ` Christoph Hellwig
2005-07-12 6:33 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [9/48] Suspend2 2.1.9.8 for 2.6.12: 354-disable-mce-checking-during-suspend-avoid-smp-deadlock.patch Nigel Cunningham
2005-07-09 11:49 ` Pavel Machek
2005-07-09 12:02 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [8/48] Suspend2 2.1.9.8 for 2.6.12: 353-disable-highmem-tlb-flush-for-copyback.patch Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [7/48] Suspend2 2.1.9.8 for 2.6.12: 352-disable-pdflush-during-suspend.patch Nigel Cunningham
2005-07-06 3:34 ` Zwane Mwaikambo
2005-07-06 3:43 ` Nigel Cunningham
2005-07-06 13:27 ` Zwane Mwaikambo
2005-07-06 8:08 ` Pavel Machek
2005-07-06 9:52 ` Russell King
2005-07-10 23:07 ` Christoph Hellwig
2005-07-06 2:20 ` [PATCH] [13/48] Suspend2 2.1.9.8 for 2.6.12: 403-debug-pagealloc-support.patch Nigel Cunningham
2005-07-10 23:02 ` Christoph Hellwig
2005-07-12 6:48 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [16/48] Suspend2 2.1.9.8 for 2.6.12: 406-dynamic-pageflags.patch Nigel Cunningham
2005-07-06 7:46 ` Shaohua Li
2005-07-06 8:13 ` Nigel Cunningham
2005-07-06 8:30 ` Shaohua Li
2005-07-06 2:20 ` [PATCH] [23/48] Suspend2 2.1.9.8 for 2.6.12: 600-suspend-header.patch Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [21/48] Suspend2 2.1.9.8 for 2.6.12: 550-documentation.patch Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [15/48] Suspend2 2.1.9.8 for 2.6.12: 405-clear-swapfile-bdev-in-swapoff.patch Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [22/48] Suspend2 2.1.9.8 for 2.6.12: 560-Kconfig-and-Makefile-for-suspend2.patch Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [19/48] Suspend2 2.1.9.8 for 2.6.12: 510-version-specific-mac.patch Nigel Cunningham
2005-07-06 3:49 ` Nigel Cunningham
2005-07-06 5:30 ` hugang
2005-07-06 6:03 ` Nigel Cunningham
2005-07-06 3:53 ` Zwane Mwaikambo
2005-07-06 3:59 ` Nigel Cunningham
2005-07-06 13:40 ` Zwane Mwaikambo
2005-07-06 5:58 ` Pekka Enberg
2005-07-06 6:21 ` Nigel Cunningham
2005-07-06 10:04 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [17/48] Suspend2 2.1.9.8 for 2.6.12: 500-version-specific-i386.patch Nigel Cunningham
2005-07-06 3:43 ` YOSHIFUJI Hideaki / 吉藤英明
2005-07-06 2:20 ` [PATCH] [18/48] Suspend2 2.1.9.8 for 2.6.12: 501-tlb-flushing-functions.patch Nigel Cunningham
2005-07-09 11:52 ` Pavel Machek
2005-07-06 2:20 ` [PATCH] [20/48] Suspend2 2.1.9.8 for 2.6.12: 520-version-specific-x86_64.patch Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [28/48] Suspend2 2.1.9.8 for 2.6.12: 605-kernel_power_suspend.patch Nigel Cunningham
2005-07-10 17:58 ` Pavel Machek
2005-07-12 8:39 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [31/48] Suspend2 2.1.9.8 for 2.6.12: 608-compression.patch Nigel Cunningham
2005-07-09 11:55 ` Pavel Machek
2005-07-09 12:15 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [25/48] Suspend2 2.1.9.8 for 2.6.12: 602-smp.patch Nigel Cunningham
2005-07-06 12:03 ` Pekka Enberg
2005-07-06 13:21 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [26/48] Suspend2 2.1.9.8 for 2.6.12: 603-suspend2_common-headers.patch Nigel Cunningham
2005-07-06 10:22 ` Pekka Enberg
2005-07-06 11:41 ` Nigel Cunningham
2005-07-06 11:52 ` Pekka J Enberg
2005-07-06 11:58 ` [PATCH] [26/48] " Nigel Cunningham
2005-07-09 11:53 ` Pavel Machek
2005-07-06 2:20 ` [PATCH] [27/48] Suspend2 2.1.9.8 for 2.6.12: 604-utility.patch Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [32/48] Suspend2 2.1.9.8 for 2.6.12: 609-driver-model.patch Nigel Cunningham
2005-07-06 10:10 ` Pekka Enberg
2005-07-06 12:05 ` Nigel Cunningham
2005-07-06 12:14 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [29/48] Suspend2 2.1.9.8 for 2.6.12: 606-all-settings.patch Nigel Cunningham
2005-07-10 18:03 ` Pavel Machek
2005-07-11 9:38 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [24/48] Suspend2 2.1.9.8 for 2.6.12: 601-kernel_power_power-header.patch Nigel Cunningham
2005-07-06 3:42 ` Zwane Mwaikambo
2005-07-06 3:45 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [30/48] Suspend2 2.1.9.8 for 2.6.12: 607-atomic-copy.patch Nigel Cunningham
2005-07-10 18:01 ` Pavel Machek
2005-07-11 8:58 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [38/48] Suspend2 2.1.9.8 for 2.6.12: 614-plugins.patch Nigel Cunningham
2005-07-10 18:08 ` Pavel Machek
2005-07-11 10:05 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [39/48] Suspend2 2.1.9.8 for 2.6.12: 615-poweroff.patch Nigel Cunningham
2005-07-10 18:18 ` Pavel Machek
2005-07-12 8:31 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [37/48] Suspend2 2.1.9.8 for 2.6.12: 613-pageflags.patch Nigel Cunningham
2005-07-06 12:01 ` Pekka Enberg
2005-07-07 9:30 ` Nigel Cunningham
2005-07-09 12:16 ` Pavel Machek
2005-07-09 12:32 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [41/48] Suspend2 2.1.9.8 for 2.6.12: 617-proc.patch Nigel Cunningham
2005-07-06 10:03 ` Pekka Enberg
2005-07-06 2:20 ` [PATCH] [40/48] Suspend2 2.1.9.8 for 2.6.12: 616-prepare_image.patch Nigel Cunningham
2005-07-10 18:13 ` Pavel Machek
2005-07-12 8:34 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [36/48] Suspend2 2.1.9.8 for 2.6.12: 612-pagedir.patch Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [33/48] Suspend2 2.1.9.8 for 2.6.12: 610-encryption.patch Nigel Cunningham
2005-07-10 18:15 ` Pavel Machek
2005-07-12 8:34 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [34/48] Suspend2 2.1.9.8 for 2.6.12: 610-extent.patch Nigel Cunningham
2005-07-06 10:14 ` Pekka Enberg
2005-07-06 2:20 ` [PATCH] [35/48] Suspend2 2.1.9.8 for 2.6.12: 611-io.patch Nigel Cunningham
2005-07-10 18:12 ` Pavel Machek
2005-07-12 8:38 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [46/48] Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch Nigel Cunningham
2005-07-06 6:33 ` Pekka Enberg
2005-07-07 12:40 ` Nigel Cunningham
2005-07-07 13:05 ` Pekka J Enberg
2005-07-08 13:41 ` Nigel Cunningham
2005-07-07 13:32 ` [PATCH] [46/48] " Pekka Enberg
2005-07-07 21:16 ` Nigel Cunningham
2005-07-07 21:26 ` nickpiggin
2005-07-08 13:42 ` Nigel Cunningham
2005-07-08 5:49 ` Pekka J Enberg
2005-07-08 5:53 ` Pekka J Enberg
2005-07-06 2:20 ` [PATCH] [43/48] Suspend2 2.1.9.8 for 2.6.12: 619-userspace-nofreeze.patch Nigel Cunningham
2005-07-06 6:18 ` Pekka Enberg
2005-07-10 23:15 ` Christoph Hellwig
2005-07-12 6:29 ` Nigel Cunningham
2005-07-12 14:21 ` Christoph Hellwig
2005-07-06 2:20 ` [PATCH] [44/48] Suspend2 2.1.9.8 for 2.6.12: 620-userui.patch Nigel Cunningham
2005-07-10 18:22 ` Pavel Machek
2005-07-12 6:58 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [48/48] Suspend2 2.1.9.8 for 2.6.12: 624-filewriter.patch Nigel Cunningham
2005-07-06 10:07 ` Pekka Enberg
2005-07-06 10:13 ` Nigel Cunningham
2005-07-06 10:17 ` Pekka J Enberg
2005-07-09 12:10 ` [PATCH] [48/48] " Pavel Machek
2005-07-09 12:18 ` Nigel Cunningham
2005-07-10 23:14 ` Christoph Hellwig
2005-07-12 6:30 ` Nigel Cunningham
2005-07-06 2:20 ` [PATCH] [45/48] Suspend2 2.1.9.8 for 2.6.12: 621-swsusp-tidy.patch Nigel Cunningham
2005-07-06 9:55 ` Pekka Enberg
2005-07-06 2:20 ` [PATCH] [42/48] Suspend2 2.1.9.8 for 2.6.12: 618-core.patch Nigel Cunningham
2005-07-10 18:21 ` Pavel Machek
2005-07-12 6:59 ` Nigel Cunningham
2005-07-12 7:56 ` Pavel Machek
2005-07-06 2:20 ` Nigel Cunningham [this message]
2005-07-10 18:24 ` [PATCH] [47/48] Suspend2 2.1.9.8 for 2.6.12: 623-generic-block-io.patch Pavel Machek
2005-07-06 6:40 ` [0/48] Suspend2 2.1.9.8 for 2.6.12 Pekka Enberg
2005-07-07 12:19 ` Nigel Cunningham
2005-07-06 8:21 ` Pavel Machek
2005-07-06 8:22 ` Pavel Machek
2005-07-06 8:33 ` Nigel Cunningham
2005-07-07 0:27 ` Nigel Cunningham
2005-07-07 12:04 ` Matthew Garrett
2005-07-07 12:15 ` Nigel Cunningham
2005-07-07 12:49 ` Matthew Garrett
2005-07-07 12:56 ` Nigel Cunningham
2005-07-07 18:54 ` Rafael J. Wysocki
2005-07-07 21:21 ` Nigel Cunningham
2005-07-08 13:30 ` Stefan Seyfried
2005-07-08 22:27 ` Nigel Cunningham
2005-07-07 19:19 ` Pavel Machek
2005-07-10 23:06 ` Christoph Hellwig
2005-07-12 6:36 ` Nigel Cunningham
2005-07-12 6:41 ` Andrew Morton
2005-07-12 6:57 ` Nigel Cunningham
2005-07-12 10:25 ` Nigel Cunningham
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1120616444110@foobar.com \
--to=nigel@suspend2.net \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.