From 7431d7eb143a53440d29c6dbfeda69b2045896be Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 29 Aug 2003 02:36:54 -0700 Subject: [swsusp] Minor cleanups in read_suspend_image() - Make resume_bdev global to file, so we don't have to pass it around (we always use the same one, so it shouldn't make a difference). - Allocate cur in read_suspend_image(), since it's the only function that uses it. - Check all errors and make sure we free cur if any happen. - Make sure to return errors from the functions called, not our own. - Free the pagedir if we hit an error after we allocate it. --- kernel/power/swsusp.c | 96 +++++++++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 42 deletions(-) (limited to 'kernel') diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 7ae04d8eefac..62690f4a1c48 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -673,12 +673,13 @@ 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; + +static int __init bdev_read_page(long pos, void *buf) { struct buffer_head *bh; BUG_ON (pos%PAGE_SIZE); - bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE); + bh = __bread(resume_bdev, pos/PAGE_SIZE, PAGE_SIZE); if (!bh || (!bh->b_data)) { return -1; } @@ -690,24 +691,24 @@ static int __init bdev_read_page(struct block_device *bdev, extern dev_t __init name_to_dev_t(const char *line); -static int __init read_suspend_image(struct block_device *bdev, - union diskpage *cur) +static int __init read_suspend_image(void) { swp_entry_t next; int i, nr_pgdir_pages; + union diskpage *cur; + int error = 0; + + cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC); + if (!cur) + return -ENOMEM; #define PREPARENEXT \ { next = cur->link.next; \ next.val = swp_offset(next) * PAGE_SIZE; \ } - if (bdev_read_page(bdev, 0, cur)) return -EIO; - - 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 = bdev_read_page(0, cur))) + goto Done; PREPARENEXT; /* We have to read next position before we overwrite it */ @@ -715,18 +716,25 @@ static int __init read_suspend_image(struct block_device *bdev, 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; } 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; + if ((error = bdev_read_page(next.val, cur))) + goto Done; + /* Is this same machine? */ + if ((error = sanity_check(&cur->sh))) + goto Done; PREPARENEXT; pagedir_save = cur->sh.suspend_pagedir; @@ -735,8 +743,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 +754,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; + error = bdev_read_page(next.val, cur); + if (error) + goto FreePagedir; PREPARENEXT; } 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 +773,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 = bdev_read_page(swp_offset(swap_address) * PAGE_SIZE, + (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 +823,6 @@ int swsusp_write(void) int __init swsusp_read(void) { - union diskpage *cur; int error; char b[BDEVNAME_SIZE]; @@ -815,19 +833,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"); -- cgit v1.2.3 From 774bc11ff35f6008c6bbcc3371bcbfd55b7d406a Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 29 Aug 2003 22:25:33 -0700 Subject: [swsusp] Use BIO interface when reading from swap. - bios are the preferred method for doing this type of stuff in 2.6. The __bread() uses bio's in the end anyway. - bios make it really easy to implement write functionality, so we are able to reset the swap signature immediately after checking it during resume. So, if something happens while resuming, we will still have valid swap to use. - Thanks to Jens for some help in getting it working several months ago. --- kernel/power/swsusp.c | 131 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 102 insertions(+), 29 deletions(-) (limited to 'kernel') diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 62690f4a1c48..3b9e59b3aaef 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -35,11 +35,11 @@ */ #include +#include #include #include #include #include -#include #include #include @@ -675,22 +675,95 @@ static int __init sanity_check(struct suspend_header *sh) static struct block_device * resume_bdev; -static int __init bdev_read_page(long pos, void *buf) + +/** + * 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(resume_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); + +#define next_entry(diskpage) diskpage->link.next + static int __init read_suspend_image(void) { swp_entry_t next; @@ -702,15 +775,13 @@ static int __init read_suspend_image(void) if (!cur) return -ENOMEM; -#define PREPARENEXT \ - { next = cur->link.next; \ - next.val = swp_offset(next) * PAGE_SIZE; \ - } - - if ((error = bdev_read_page(0, cur))) + 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); @@ -727,15 +798,21 @@ static int __init read_suspend_image(void) goto Done; } + /* + * Reset swap signature now. + */ + if ((error = write_page(0,cur))) + goto Done; + printk( "%sSignature found, resuming\n", name_resume ); MDELAY(1000); - if ((error = bdev_read_page(next.val, cur))) + if ((error = read_page(swp_offset(next), cur))) goto Done; /* Is this same machine? */ if ((error = sanity_check(&cur->sh))) goto Done; - PREPARENEXT; + next = next_entry(cur); pagedir_save = cur->sh.suspend_pagedir; nr_copy_pages = cur->sh.num_pbes; @@ -754,10 +831,10 @@ static int __init read_suspend_image(void) for (i=nr_pgdir_pages-1; i>=0; i--) { BUG_ON (!next.val); cur = (union diskpage *)((char *) pagedir_nosave)+i; - error = bdev_read_page(next.val, cur); + error = read_page(swp_offset(next), cur); if (error) goto FreePagedir; - PREPARENEXT; + next = next_entry(cur); } BUG_ON (next.val); @@ -773,8 +850,8 @@ static int __init read_suspend_image(void) printk( "." ); /* You do not need to check for overlaps... ... check_pagedir already did this work */ - error = bdev_read_page(swp_offset(swap_address) * PAGE_SIZE, - (char *)((pagedir_nosave+i)->address)); + error = read_page(swp_offset(swap_address), + (char *)((pagedir_nosave+i)->address)); if (error) goto FreePagedir; } @@ -868,10 +945,6 @@ 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; } -- cgit v1.2.3 From 36d1f8a3fb4e6a7b39727250515a5a6af23b1a54 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 29 Aug 2003 23:23:34 -0700 Subject: [swsusp] Restore software_suspend() call. - Allows 'backdoor' interface to swsusp, as requested by Pavel. - Simply a wrapper to pm_suspend(), though guaranteeing that swsusp is used, and system is shutdown (and put into low-power state). - Call in sys_reboot() changed back to call to software_suspend(). --- include/linux/suspend.h | 1 + kernel/power/swsusp.c | 11 +++++++++++ kernel/sys.c | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'kernel') diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 132db86c961a..ed8d796f1849 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -53,6 +53,7 @@ extern suspend_pagedir_t *pagedir_nosave __nosavedata; extern void do_suspend_lowlevel(int resume); extern void do_suspend_lowlevel_s4bios(int resume); +extern int software_suspend(void); #else /* CONFIG_SOFTWARE_SUSPEND */ static inline int software_suspend(void) { diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 3b9e59b3aaef..c2c33c0cf76c 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -948,6 +948,17 @@ int swsusp_free(void) 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 27c19703a1ea..ded6182fcff8 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; -- cgit v1.2.3