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(-) 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(-) 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(-) 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 From d5bd8347829ab21cdbce604c904350b42168a81a Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 29 Aug 2003 23:27:09 -0700 Subject: [acpi] Replace /proc/acpi/sleep - Bad to remove proc file now, even though it's nearly useless. Reinstated in the name of compatibility. - Restored original semantics - if software_suspend() is enabled, then just call that (and never go into low-power state). Otherwise, call acpi_suspend(). - acpi_suspend() is simply a wrapper for pm_suspend(), passing down the right argument. This is so we don't have to do everything manually anymore. - Fixed long-standing bug by checking for "4b" in string written in to determine if we want to enter S4bios. --- drivers/acpi/sleep/main.c | 21 ++++++++----- drivers/acpi/sleep/proc.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/sleep/sleep.h | 3 +- 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 2fbbd80edaf7..1338cc96b724 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -41,7 +41,6 @@ static u32 acpi_suspend_states[] = { static int acpi_pm_prepare(u32 state) { - int error = 0; u32 acpi_state = acpi_suspend_states[state]; if (!sleep_states[acpi_state]) @@ -56,15 +55,9 @@ static int acpi_pm_prepare(u32 state) acpi_set_firmware_waking_vector( (acpi_physical_address) acpi_wakeup_address); } - ACPI_FLUSH_CPU_CACHE(); - acpi_enter_sleep_state_prep(acpi_state); - return 0; - Err: - acpi_set_firmware_waking_vector(0); - return error; } @@ -153,6 +146,20 @@ static int acpi_pm_finish(u32 state) } +int acpi_suspend(u32 acpi_state) +{ + u32 states[] = { + [1] = PM_SUSPEND_STANDBY, + [3] = PM_SUSPEND_MEM, + [4] = PM_SUSPEND_DISK, + }; + + if (acpi_state <= 4 && states[acpi_state]) + return pm_suspend(states[acpi_state]); + return -EINVAL; +} + + static struct pm_ops acpi_pm_ops = { .prepare = acpi_pm_prepare, .enter = acpi_pm_enter, diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 41cbde00b785..4a15b0000aa8 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -13,12 +13,71 @@ #include "sleep.h" +#define ACPI_SYSTEM_FILE_SLEEP "sleep" #define ACPI_SYSTEM_FILE_ALARM "alarm" #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME ("sleep") +static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset) +{ + int i; + + ACPI_FUNCTION_TRACE("acpi_system_sleep_seq_show"); + + for (i = 0; i <= ACPI_STATE_S5; i++) { + if (sleep_states[i]) { + seq_printf(seq,"S%d ", i); + if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f) + seq_printf(seq, "S4bios "); + } + } + + seq_puts(seq, "\n"); + + return 0; +} + +static int acpi_system_sleep_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_system_sleep_seq_show, PDE(inode)->data); +} + +static int +acpi_system_write_sleep ( + struct file *file, + const char *buffer, + size_t count, + loff_t *ppos) +{ + char str[12]; + u32 state = 0; + int error = 0; + + if (count > sizeof(str) - 1) + goto Done; + memset(str,0,sizeof(str)); + if (copy_from_user(str, buffer, count)) + return -EFAULT; + + /* Check for S4 bios request */ + if (!strcmp(str,"4b")) { + error = acpi_suspend(4); + goto Done; + } + state = simple_strtoul(str, NULL, 0); +#ifdef CONFIG_SOFTWARE_SUSPEND + if (state == 4) { + error = software_suspend(); + goto Done; + } +#endif + error = acpi_suspend(state); + Done: + return error ? error : count; +} + static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset) { u32 sec, min, hr; @@ -294,6 +353,14 @@ end: } +static struct file_operations acpi_system_sleep_fops = { + .open = acpi_system_sleep_open_fs, + .read = seq_read, + .write = acpi_system_write_sleep, + .llseek = seq_lseek, + .release = single_release, +}; + static struct file_operations acpi_system_alarm_fops = { .open = acpi_system_alarm_open_fs, .read = seq_read, @@ -307,6 +374,12 @@ static int acpi_sleep_proc_init(void) { struct proc_dir_entry *entry = NULL; + /* 'sleep' [R/W]*/ + entry = create_proc_entry(ACPI_SYSTEM_FILE_SLEEP, + S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir); + if (entry) + entry->proc_fops = &acpi_system_sleep_fops; + /* 'alarm' [R/W] */ entry = create_proc_entry(ACPI_SYSTEM_FILE_ALARM, S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir); diff --git a/drivers/acpi/sleep/sleep.h b/drivers/acpi/sleep/sleep.h index 97b72323f35e..ad38f4153a34 100644 --- a/drivers/acpi/sleep/sleep.h +++ b/drivers/acpi/sleep/sleep.h @@ -1,5 +1,4 @@ extern u8 sleep_states[]; - -extern acpi_status acpi_suspend (u32 state); +extern int acpi_suspend (u32 state); -- cgit v1.2.3