diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index bc7e16ec8393..42127c0d612c 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -217,10 +217,26 @@ static void acpi_hibernation_finish(void) } } +static int acpi_hibernation_pre_restore(void) +{ + acpi_status status; + + status = acpi_hw_disable_all_gpes(); + + return ACPI_SUCCESS(status) ? 0 : -EFAULT; +} + +static void acpi_hibernation_restore_cleanup(void) +{ + acpi_hw_enable_all_runtime_gpes(); +} + static struct hibernation_ops acpi_hibernation_ops = { .prepare = acpi_hibernation_prepare, .enter = acpi_hibernation_enter, .finish = acpi_hibernation_finish, + .pre_restore = acpi_hibernation_pre_restore, + .restore_cleanup = acpi_hibernation_restore_cleanup, }; #endif /* CONFIG_SOFTWARE_SUSPEND */ diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 9c7cb6430666..d235c146da2b 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -43,11 +43,15 @@ static inline void pm_restore_console(void) {} * @prepare: prepare system for hibernation * @enter: shut down system after state has been saved to disk * @finish: finish/clean up after state has been reloaded + * @pre_restore: prepare system for the restoration from a hibernation image + * @restore_cleanup: clean up after a failing image restoration */ struct hibernation_ops { int (*prepare)(void); int (*enter)(void); void (*finish)(void); + int (*pre_restore)(void); + void (*restore_cleanup)(void); }; #if defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND) diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 47882bfa610e..fa3b43b7206d 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -54,7 +54,8 @@ static struct hibernation_ops *hibernation_ops; void hibernation_set_ops(struct hibernation_ops *ops) { - if (ops && !(ops->prepare && ops->enter && ops->finish)) { + if (ops && !(ops->prepare && ops->enter && ops->finish + && ops->pre_restore && ops->restore_cleanup)) { WARN_ON(1); return; } @@ -91,6 +92,31 @@ static void platform_finish(int platform_mode) hibernation_ops->finish(); } +/** + * platform_pre_restore - prepare the platform for the restoration from a + * hibernation image. If the restore fails after this function has been + * called, platform_restore_cleanup() must be called. + */ + +static int platform_pre_restore(int platform_mode) +{ + return (platform_mode && hibernation_ops) ? + hibernation_ops->pre_restore() : 0; +} + +/** + * platform_restore_cleanup - switch the platform to the normal mode of + * operation after a failing restore. If platform_pre_restore() has been + * called before the failing restore, this function must be called too, + * regardless of the result of platform_pre_restore(). + */ + +static void platform_restore_cleanup(int platform_mode) +{ + if (platform_mode && hibernation_ops) + hibernation_ops->restore_cleanup(); +} + /** * hibernation_snapshot - quiesce devices and create the hibernation * snapshot image. @@ -141,11 +167,13 @@ int hibernation_snapshot(int platform_mode) /** * hibernation_restore - quiesce devices and restore the hibernation * snapshot image. If successful, control returns in hibernation_snaphot() + * @platform_mode - if set, use the platform driver, if available, to + * prepare the platform frimware for the transition. * * Must be called with pm_mutex held */ -int hibernation_restore(void) +int hibernation_restore(int platform_mode) { int error; @@ -155,11 +183,14 @@ int hibernation_restore(void) if (error) goto Finish; - error = disable_nonboot_cpus(); - if (!error) - error = swsusp_resume(); - - enable_nonboot_cpus(); + error = platform_pre_restore(platform_mode); + if (!error) { + error = disable_nonboot_cpus(); + if (!error) + error = swsusp_resume(); + enable_nonboot_cpus(); + } + platform_restore_cleanup(platform_mode); Finish: device_resume(); resume_console(); @@ -260,8 +291,12 @@ int hibernate(void) } error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); if (in_suspend && !error) { + unsigned int flags = 0; + + if (hibernation_mode == HIBERNATION_PLATFORM) + flags |= SF_PLATFORM_MODE; pr_debug("PM: writing image.\n"); - error = swsusp_write(); + error = swsusp_write(flags); swsusp_free(); if (!error) power_down(); @@ -295,6 +330,7 @@ int hibernate(void) static int software_resume(void) { int error; + unsigned int flags; mutex_lock(&pm_mutex); if (!swsusp_resume_device) { @@ -342,9 +378,9 @@ static int software_resume(void) pr_debug("PM: Reading swsusp image.\n"); - error = swsusp_read(); + error = swsusp_read(&flags); if (!error) - hibernation_restore(); + hibernation_restore(flags & SF_PLATFORM_MODE); printk(KERN_ERR "PM: Restore failed, recovering.\n"); swsusp_free(); diff --git a/kernel/power/power.h b/kernel/power/power.h index 70c378b3f85a..eab3603b7caf 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -27,7 +27,7 @@ struct swsusp_info { /* kernel/power/disk.c */ extern int hibernation_snapshot(int platform_mode); -extern int hibernation_restore(void); +extern int hibernation_restore(int platform_mode); extern int hibernation_platform_enter(void); #endif @@ -155,13 +155,20 @@ extern sector_t alloc_swapdev_block(int swap); extern void free_all_swap_pages(int swap); extern int swsusp_swap_in_use(void); +/* + * Flags that can be passed from the hibernatig hernel to the "boot" kernel in + * the image header. + */ +#define SF_PLATFORM_MODE 1 + +/* kernel/power/disk.c */ extern int swsusp_check(void); extern int swsusp_shrink_memory(void); extern void swsusp_free(void); extern int swsusp_suspend(void); extern int swsusp_resume(void); -extern int swsusp_read(void); -extern int swsusp_write(void); +extern int swsusp_read(unsigned int *flags_p); +extern int swsusp_write(unsigned int flags); extern void swsusp_close(void); extern int suspend_enter(suspend_state_t state); diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 8b1a1b837145..917aba100575 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -33,8 +33,9 @@ extern char resume_file[]; #define SWSUSP_SIG "S1SUSPEND" struct swsusp_header { - char reserved[PAGE_SIZE - 20 - sizeof(sector_t)]; + char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int)]; sector_t image; + unsigned int flags; /* Flags to pass to the "boot" kernel */ char orig_sig[10]; char sig[10]; } __attribute__((packed)); @@ -138,7 +139,7 @@ static int wait_on_bio_chain(struct bio **bio_chain) * Saving part */ -static int mark_swapfiles(sector_t start) +static int mark_swapfiles(sector_t start, unsigned int flags) { int error; @@ -148,6 +149,7 @@ static int mark_swapfiles(sector_t start) memcpy(swsusp_header->orig_sig,swsusp_header->sig, 10); memcpy(swsusp_header->sig,SWSUSP_SIG, 10); swsusp_header->image = start; + swsusp_header->flags = flags; error = bio_write_page(swsusp_resume_block, swsusp_header, NULL); } else { @@ -369,6 +371,7 @@ static int enough_swap(unsigned int nr_pages) /** * swsusp_write - Write entire image and metadata. + * @flags: flags to pass to the "boot" kernel in the image header * * It is important _NOT_ to umount filesystems at this point. We want * them synced (in case something goes wrong) but we DO not want to mark @@ -376,7 +379,7 @@ static int enough_swap(unsigned int nr_pages) * correctly, we'll mark system clean, anyway.) */ -int swsusp_write(void) +int swsusp_write(unsigned int flags) { struct swap_map_handle handle; struct snapshot_handle snapshot; @@ -415,7 +418,7 @@ int swsusp_write(void) if (!error) { flush_swap_writer(&handle); printk("S"); - error = mark_swapfiles(start); + error = mark_swapfiles(start, flags); printk("|\n"); } } @@ -540,13 +543,20 @@ static int load_image(struct swap_map_handle *handle, return error; } -int swsusp_read(void) +/** + * swsusp_read - read the hibernation image. + * @flags_p: flags passed by the "frozen" kernel in the image header should + * be written into this memeory location + */ + +int swsusp_read(unsigned int *flags_p) { int error; struct swap_map_handle handle; struct snapshot_handle snapshot; struct swsusp_info *header; + *flags_p = swsusp_header->flags; if (IS_ERR(resume_bdev)) { pr_debug("swsusp: block device not initialised\n"); return PTR_ERR(resume_bdev); diff --git a/kernel/power/user.c b/kernel/power/user.c index bfed3b924093..1f24f30b951b 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -188,7 +188,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp, error = -EPERM; break; } - error = hibernation_restore(); + error = hibernation_restore(data->platform_suspend); break; case SNAPSHOT_FREE: