summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/power/swsusp.c222
-rw-r--r--kernel/sys.c2
2 files changed, 160 insertions, 64 deletions
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c
index 7ae04d8eefac..c2c33c0cf76c 100644
--- a/kernel/power/swsusp.c
+++ b/kernel/power/swsusp.c
@@ -35,11 +35,11 @@
*/
#include <linux/mm.h>
+#include <linux/bio.h>
#include <linux/suspend.h>
#include <linux/version.h>
#include <linux/reboot.h>
#include <linux/device.h>
-#include <linux/buffer_head.h>
#include <linux/swapops.h>
#include <linux/bootmem.h>
@@ -673,61 +673,146 @@ static int __init sanity_check(struct suspend_header *sh)
return 0;
}
-static int __init bdev_read_page(struct block_device *bdev,
- long pos, void *buf)
+static struct block_device * resume_bdev;
+
+
+/**
+ * Using bio to read from swap.
+ * This code requires a bit more work than just using buffer heads
+ * but, it is the recommended way for 2.5/2.6.
+ * The following are to signal the beginning and end of I/O. Bios
+ * finish asynchronously, while we want them to happen synchronously.
+ * A simple atomic_t, and a wait loop take care of this problem.
+ */
+
+static atomic_t io_done = ATOMIC_INIT(0);
+
+static void start_io(void)
{
- struct buffer_head *bh;
- BUG_ON (pos%PAGE_SIZE);
- bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
- if (!bh || (!bh->b_data)) {
- return -1;
- }
- memcpy(buf, bh->b_data, PAGE_SIZE); /* FIXME: may need kmap() */
- BUG_ON(!buffer_uptodate(bh));
- brelse(bh);
+ atomic_set(&io_done,1);
+}
+
+static int end_io(struct bio * bio, unsigned int num, int err)
+{
+ atomic_set(&io_done,0);
return 0;
-}
+}
+
+static void wait_io(void)
+{
+ blk_run_queues();
+ while(atomic_read(&io_done))
+ io_schedule();
+}
+
+
+/**
+ * submit - submit BIO request.
+ * @rw: READ or WRITE.
+ * @off physical offset of page.
+ * @page: page we're reading or writing.
+ *
+ * Straight from the textbook - allocate and initialize the bio.
+ * If we're writing, make sure the page is marked as dirty.
+ * Then submit it and wait.
+ */
+
+static int submit(int rw, pgoff_t page_off, void * page)
+{
+ int error = 0;
+ struct bio * bio;
+
+ bio = bio_alloc(GFP_ATOMIC,1);
+ if (!bio)
+ return -ENOMEM;
+ bio->bi_sector = page_off * (PAGE_SIZE >> 9);
+ bio_get(bio);
+ bio->bi_bdev = resume_bdev;
+ bio->bi_end_io = end_io;
+
+ if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) {
+ printk("ERROR: adding page to bio at %ld\n",page_off);
+ error = -EFAULT;
+ goto Done;
+ }
+
+ if (rw == WRITE)
+ bio_set_pages_dirty(bio);
+ start_io();
+ submit_bio(rw,bio);
+ wait_io();
+ Done:
+ bio_put(bio);
+ return error;
+}
+
+static int
+read_page(pgoff_t page_off, void * page)
+{
+ return submit(READ,page_off,page);
+}
+
+static int
+write_page(pgoff_t page_off, void * page)
+{
+ return submit(WRITE,page_off,page);
+}
+
extern dev_t __init name_to_dev_t(const char *line);
-static int __init read_suspend_image(struct block_device *bdev,
- union diskpage *cur)
+
+#define next_entry(diskpage) diskpage->link.next
+
+static int __init read_suspend_image(void)
{
swp_entry_t next;
int i, nr_pgdir_pages;
+ union diskpage *cur;
+ int error = 0;
-#define PREPARENEXT \
- { next = cur->link.next; \
- next.val = swp_offset(next) * PAGE_SIZE; \
- }
-
- if (bdev_read_page(bdev, 0, cur)) return -EIO;
+ cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
+ if (!cur)
+ return -ENOMEM;
- if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) ||
- (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) {
- printk(KERN_ERR "%sThis is normal swap space\n", name_resume );
- return -EINVAL;
- }
+ if ((error = read_page(0, cur)))
+ goto Done;
- PREPARENEXT; /* We have to read next position before we overwrite it */
+ /*
+ * We have to read next position before we overwrite it
+ */
+ next = next_entry(cur);
if (!memcmp("S1",cur->swh.magic.magic,2))
memcpy(cur->swh.magic.magic,"SWAP-SPACE",10);
else if (!memcmp("S2",cur->swh.magic.magic,2))
memcpy(cur->swh.magic.magic,"SWAPSPACE2",10);
- else {
- printk("swsusp: %s: Unable to find suspended-data signature (%.10s - misspelled?\n",
- name_resume, cur->swh.magic.magic);
- return -EFAULT;
+ else if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) ||
+ (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) {
+ printk(KERN_ERR "swsusp: Partition is normal swap space\n");
+ error = -EINVAL;
+ goto Done;
+ } else {
+ printk(KERN_ERR "swsusp: Invalid partition type.\n");
+ error = -EINVAL;
+ goto Done;
}
+ /*
+ * Reset swap signature now.
+ */
+ if ((error = write_page(0,cur)))
+ goto Done;
+
printk( "%sSignature found, resuming\n", name_resume );
MDELAY(1000);
- if (bdev_read_page(bdev, next.val, cur)) return -EIO;
- if (sanity_check(&cur->sh)) /* Is this same machine? */
- return -EPERM;
- PREPARENEXT;
+ if ((error = read_page(swp_offset(next), cur)))
+ goto Done;
+ /* Is this same machine? */
+ if ((error = sanity_check(&cur->sh)))
+ goto Done;
+ next = next_entry(cur);
pagedir_save = cur->sh.suspend_pagedir;
nr_copy_pages = cur->sh.num_pbes;
@@ -735,8 +820,10 @@ static int __init read_suspend_image(struct block_device *bdev,
pagedir_order = get_bitmask_order(nr_pgdir_pages);
pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order);
- if (!pagedir_nosave)
- return -ENOMEM;
+ if (!pagedir_nosave) {
+ error = -ENOMEM;
+ goto Done;
+ }
PRINTK( "%sReading pagedir, ", name_resume );
@@ -744,15 +831,17 @@ static int __init read_suspend_image(struct block_device *bdev,
for (i=nr_pgdir_pages-1; i>=0; i--) {
BUG_ON (!next.val);
cur = (union diskpage *)((char *) pagedir_nosave)+i;
- if (bdev_read_page(bdev, next.val, cur)) return -EIO;
- PREPARENEXT;
+ error = read_page(swp_offset(next), cur);
+ if (error)
+ goto FreePagedir;
+ next = next_entry(cur);
}
BUG_ON (next.val);
- if (relocate_pagedir())
- return -ENOMEM;
- if (check_pagedir())
- return -ENOMEM;
+ if ((error = relocate_pagedir()))
+ goto FreePagedir;
+ if ((error = check_pagedir()))
+ goto FreePagedir;
printk( "Reading image data (%d pages): ", nr_copy_pages );
for(i=0; i < nr_copy_pages; i++) {
@@ -761,11 +850,18 @@ static int __init read_suspend_image(struct block_device *bdev,
printk( "." );
/* You do not need to check for overlaps...
... check_pagedir already did this work */
- if (bdev_read_page(bdev, swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address)))
- return -EIO;
+ error = read_page(swp_offset(swap_address),
+ (char *)((pagedir_nosave+i)->address));
+ if (error)
+ goto FreePagedir;
}
printk( "|\n" );
- return 0;
+ Done:
+ free_page((unsigned long)cur);
+ return error;
+ FreePagedir:
+ free_pages((unsigned long)pagedir_nosave,pagedir_order);
+ goto Done;
}
/**
@@ -804,7 +900,6 @@ int swsusp_write(void)
int __init swsusp_read(void)
{
- union diskpage *cur;
int error;
char b[BDEVNAME_SIZE];
@@ -815,19 +910,13 @@ int __init swsusp_read(void)
printk("swsusp: Resume From Partition: %s, Device: %s\n",
resume_file, __bdevname(resume_device, b));
- cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
- if (cur) {
- struct block_device *bdev;
- bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW);
- if (!IS_ERR(bdev)) {
- set_blocksize(bdev, PAGE_SIZE);
- error = read_suspend_image(bdev, cur);
- blkdev_put(bdev, BDEV_RAW);
- } else
- error = PTR_ERR(bdev);
- free_page((unsigned long)cur);
+ resume_bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW);
+ if (!IS_ERR(resume_bdev)) {
+ set_blocksize(resume_bdev, PAGE_SIZE);
+ error = read_suspend_image();
+ blkdev_put(resume_bdev, BDEV_RAW);
} else
- error = -ENOMEM;
+ error = PTR_ERR(resume_bdev);
if (!error)
PRINTK("Reading resume file was successful\n");
@@ -856,13 +945,20 @@ int swsusp_free(void)
{
PRINTK( "Freeing prev allocated pagedir\n" );
free_suspend_pagedir((unsigned long) pagedir_save);
-
- PRINTK( "Fixing swap signatures... " );
- mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
- PRINTK( "ok\n" );
return 0;
}
+
+int software_suspend(void)
+{
+ struct pm_ops swsusp_ops = {
+ .pm_disk_mode = PM_DISK_SHUTDOWN,
+ };
+
+ pm_set_ops(&swsusp_ops);
+ return pm_suspend(PM_SUSPEND_DISK);
+}
+
static int __init resume_setup(char *str)
{
if (strlen(str))
diff --git a/kernel/sys.c b/kernel/sys.c
index 02b5a12dfd59..d77453173d29 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -456,7 +456,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user
#ifdef CONFIG_SOFTWARE_SUSPEND
case LINUX_REBOOT_CMD_SW_SUSPEND:
- if (!pm_suspend(PM_SUSPEND_DISK))
+ if (!software_suspend())
break;
do_exit(0);
break;