/* * kernel/power/disk.c - Suspend-to-disk support. * * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab * Copyright (c) 2004 Pavel Machek <pavel@suse.cz> * * This file is released under the GPLv2. * */ #include <linux/suspend.h>#include <linux/syscalls.h>#include <linux/reboot.h>#include <linux/string.h>#include <linux/device.h>#include <linux/kmod.h>#include <linux/delay.h>#include <linux/fs.h>#include <linux/mount.h>#include <linux/pm.h>#include <linux/console.h>#include <linux/cpu.h>#include <linux/freezer.h>#include <scsi/scsi_scan.h>#include <asm/suspend.h> #include "power.h" static int noresume = 0;static char resume_file[256] = CONFIG_PM_STD_PARTITION;dev_t swsusp_resume_device;sector_t swsusp_resume_block; enum { HIBERNATION_INVALID, HIBERNATION_PLATFORM, HIBERNATION_TEST, HIBERNATION_TESTPROC, HIBERNATION_SHUTDOWN, HIBERNATION_REBOOT, /* keep last */ __HIBERNATION_AFTER_LAST};#define HIBERNATION_MAX (__HIBERNATION_AFTER_LAST-1)#define HIBERNATION_FIRST (HIBERNATION_INVALID + 1) static int hibernation_mode = HIBERNATION_SHUTDOWN; static struct platform_hibernation_ops *hibernation_ops; /** * hibernation_set_ops - set the global hibernate operations * @ops: the hibernation operations to use in subsequent hibernation transitions */ void hibernation_set_ops(struct platform_hibernation_ops *ops){ if (ops && !(ops->begin && ops->end && ops->pre_snapshot && ops->prepare && ops->finish && ops->enter && ops->pre_restore && ops->restore_cleanup)) { WARN_ON(1); return; } mutex_lock(&pm_mutex); hibernation_ops = ops; if (ops) hibernation_mode = HIBERNATION_PLATFORM; else if (hibernation_mode == HIBERNATION_PLATFORM) hibernation_mode = HIBERNATION_SHUTDOWN; mutex_unlock(&pm_mutex);} static bool entering_platform_hibernation; bool system_entering_hibernation(void){ return entering_platform_hibernation;}EXPORT_SYMBOL(system_entering_hibernation); #ifdef CONFIG_PM_DEBUGstatic void hibernation_debug_sleep(void){ printk(KERN_INFO "hibernation debug: Waiting for 5 seconds.\n"); mdelay(5000);} static int hibernation_testmode(int mode){ if (hibernation_mode == mode) { hibernation_debug_sleep(); return 1; } return 0;} static int hibernation_test(int level){ if (pm_test_level == level) { hibernation_debug_sleep(); return 1; } return 0;}#else /* !CONFIG_PM_DEBUG */static int hibernation_testmode(int mode) { return 0; }static int hibernation_test(int level) { return 0; }#endif /* !CONFIG_PM_DEBUG */ /** * platform_begin - tell the platform driver that we're starting * hibernation */ static int platform_begin(int platform_mode){ return (platform_mode && hibernation_ops) ? hibernation_ops->begin() : 0;} /** * platform_end - tell the platform driver that we've entered the * working state */ static void platform_end(int platform_mode){ if (platform_mode && hibernation_ops) hibernation_ops->end();} /** * platform_pre_snapshot - prepare the machine for hibernation using the * platform driver if so configured and return an error code if it fails */ static int platform_pre_snapshot(int platform_mode){ return (platform_mode && hibernation_ops) ? hibernation_ops->pre_snapshot() : 0;} /** * platform_leave - prepare the machine for switching to the normal mode * of operation using the platform driver (called with interrupts disabled) */ static void platform_leave(int platform_mode){ if (platform_mode && hibernation_ops) hibernation_ops->leave();} /** * platform_finish - switch the machine to the normal mode of operation * using the platform driver (must be called after platform_prepare()) */ static void platform_finish(int platform_mode){ if (platform_mode && hibernation_ops) 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();} /** * platform_recover - recover the platform from a failure to suspend * devices. */ static void platform_recover(int platform_mode){ if (platform_mode && hibernation_ops && hibernation_ops->recover) hibernation_ops->recover();} /** * create_image - freeze devices that need to be frozen with interrupts * off, create the hibernation image and thaw those devices. Control * reappears in this routine after a restore. */ static int create_image(int platform_mode){ int error; error = arch_prepare_suspend(); if (error) return error; /* At this point, device_suspend() has been called, but *not* * device_power_down(). We *must* call device_power_down() now. * Otherwise, drivers for some devices (e.g. interrupt controllers) * become desynchronized with the actual state of the hardware * at resume time, and evil weirdness ensues. */ error = device_power_down(PMSG_FREEZE); if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting hibernation\n"); return error; } error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) goto Platform_finish; error = disable_nonboot_cpus(); if (error || hibernation_test(TEST_CPUS) || hibernation_testmode(HIBERNATION_TEST)) goto Enable_cpus; local_irq_disable(); error = sysdev_suspend(PMSG_FREEZE); if (error) { printk(KERN_ERR "PM: Some system devices failed to power down, " "aborting hibernation\n"); goto Enable_irqs; } if (hibernation_test(TEST_CORE)) goto Power_up; in_suspend = 1; save_processor_state(); error = swsusp_arch_suspend(); if (error) printk(KERN_ERR "PM: Error %d creating hibernation image\n", error); /* Restore control flow magically appears here */ restore_processor_state(); if (!in_suspend) platform_leave(platform_mode); Power_up: sysdev_resume(); /* NOTE: device_power_up() is just a resume() for devices * that suspended with irqs off ... no overall powerup. */ Enable_irqs: local_irq_enable(); Enable_cpus: enable_nonboot_cpus(); Platform_finish: platform_finish(platform_mode); device_power_up(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); return error;} /** * hibernation_snapshot - quiesce devices and create the hibernation * snapshot image. * @platform_mode - if set, use the platform driver, if available, to * prepare the platform firmware for the power transition. * * Must be called with pm_mutex held */ int hibernation_snapshot(int platform_mode){ int error; error = platform_begin(platform_mode); if (error) return error; /* Free memory before shutting down devices. */ error = swsusp_shrink_memory(); if (error) goto Close; suspend_console(); error = device_suspend(PMSG_FREEZE); if (error) goto Recover_platform; if (hibernation_test(TEST_DEVICES)) goto Recover_platform; error = create_image(platform_mode); /* Control returns here after successful restore */ Resume_devices: device_resume(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); resume_console(); Close: platform_end(platform_mode); return error; Recover_platform: platform_recover(platform_mode); goto Resume_devices;} /** * resume_target_kernel - prepare devices that need to be suspended with * interrupts off, restore the contents of highmem that have not been * restored yet from the image and run the low level code that will restore * the remaining contents of memory and switch to the just restored target * kernel. */ static int resume_target_kernel(bool platform_mode){ int error; error = device_power_down(PMSG_QUIESCE); if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting resume\n"); return error; } error = platform_pre_restore(platform_mode); if (error) goto Cleanup; error = disable_nonboot_cpus(); if (error) goto Enable_cpus; local_irq_disable(); error = sysdev_suspend(PMSG_QUIESCE); if (error) goto Enable_irqs; /* We'll ignore saved state, but this gets preempt count (etc) right */ save_processor_state(); error = restore_highmem(); if (!error) { error = swsusp_arch_resume(); /* * The code below is only ever reached in case of a failure. * Otherwise execution continues at place where * swsusp_arch_suspend() was called */ BUG_ON(!error); /* This call to restore_highmem() undos the previous one */ restore_highmem(); } /* * The only reason why swsusp_arch_resume() can fail is memory being * very tight, so we have to free it as soon as we can to avoid * subsequent failures */ swsusp_free(); restore_processor_state(); touch_softlockup_watchdog(); sysdev_resume(); Enable_irqs: local_irq_enable(); Enable_cpus: enable_nonboot_cpus(); Cleanup: platform_restore_cleanup(platform_mode); device_power_up(PMSG_RECOVER); return error;} /** * 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 firmware for the transition. * * Must be called with pm_mutex held */ int hibernation_restore(int platform_mode){ int error; pm_prepare_console(); suspend_console(); error = device_suspend(PMSG_QUIESCE); if (!error) { error = resume_target_kernel(platform_mode); device_resume(PMSG_RECOVER); } resume_console(); pm_restore_console(); return error;} /** * hibernation_platform_enter - enter the hibernation state using the * platform driver (if available) */ int hibernation_platform_enter(void){ int error; if (!hibernation_ops) return -ENOSYS; /* * We have cancelled the power transition by running * hibernation_ops->finish() before saving the image, so we should let * the firmware know that we're going to enter the sleep state after all */ error = hibernation_ops->begin(); if (error) goto Close; entering_platform_hibernation = true; suspend_console(); error = device_suspend(PMSG_HIBERNATE); if (error) { if (hibernation_ops->recover) hibernation_ops->recover(); goto Resume_devices; } error = device_power_down(PMSG_HIBERNATE); if (error) goto Resume_devices; error = hibernation_ops->prepare(); if (error) goto Platofrm_finish; error = disable_nonboot_cpus(); if (error) goto Platofrm_finish; local_irq_disable(); sysdev_suspend(PMSG_HIBERNATE); hibernation_ops->enter(); /* We should never get here */ while (1); /* * We don't need to reenable the nonboot CPUs or resume consoles, since * the system is going to be halted anyway. */ Platofrm_finish: hibernation_ops->finish(); device_power_up(PMSG_RESTORE); Resume_devices: entering_platform_hibernation = false; device_resume(PMSG_RESTORE); resume_console(); Close: hibernation_ops->end(); return error;} /** * power_down - Shut the machine down for hibernation. * * Use the platform driver, if configured so; otherwise try * to power off or reboot. */Lines 1–500 of 955. Load more
/* * kernel/power/disk.c - Suspend-to-disk support. * * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab * Copyright (c) 2004 Pavel Machek <pavel@suse.cz> * * This file is released under the GPLv2. * */ #include <linux/suspend.h> #include <linux/syscalls.h> #include <linux/reboot.h> #include <linux/string.h> #include <linux/device.h> #include <linux/kmod.h> #include <linux/delay.h> #include <linux/fs.h> #include <linux/mount.h> #include <linux/pm.h> #include <linux/console.h> #include <linux/cpu.h> #include <linux/freezer.h> #include <scsi/scsi_scan.h> #include <asm/suspend.h> #include "power.h" static int noresume = 0; static char resume_file[256] = CONFIG_PM_STD_PARTITION; dev_t swsusp_resume_device; sector_t swsusp_resume_block; enum { HIBERNATION_INVALID, HIBERNATION_PLATFORM, HIBERNATION_TEST, HIBERNATION_TESTPROC, HIBERNATION_SHUTDOWN, HIBERNATION_REBOOT, /* keep last */ __HIBERNATION_AFTER_LAST }; #define HIBERNATION_MAX (__HIBERNATION_AFTER_LAST-1) #define HIBERNATION_FIRST (HIBERNATION_INVALID + 1) static int hibernation_mode = HIBERNATION_SHUTDOWN; static struct platform_hibernation_ops *hibernation_ops; /** * hibernation_set_ops - set the global hibernate operations * @ops: the hibernation operations to use in subsequent hibernation transitions */ void hibernation_set_ops(struct platform_hibernation_ops *ops) { if (ops && !(ops->begin && ops->end && ops->pre_snapshot && ops->prepare && ops->finish && ops->enter && ops->pre_restore && ops->restore_cleanup)) { WARN_ON(1); return; } mutex_lock(&pm_mutex); hibernation_ops = ops; if (ops) hibernation_mode = HIBERNATION_PLATFORM; else if (hibernation_mode == HIBERNATION_PLATFORM) hibernation_mode = HIBERNATION_SHUTDOWN; mutex_unlock(&pm_mutex); } static bool entering_platform_hibernation; bool system_entering_hibernation(void) { return entering_platform_hibernation; } EXPORT_SYMBOL(system_entering_hibernation); #ifdef CONFIG_PM_DEBUG static void hibernation_debug_sleep(void) { printk(KERN_INFO "hibernation debug: Waiting for 5 seconds.\n"); mdelay(5000); } static int hibernation_testmode(int mode) { if (hibernation_mode == mode) { hibernation_debug_sleep(); return 1; } return 0; } static int hibernation_test(int level) { if (pm_test_level == level) { hibernation_debug_sleep(); return 1; } return 0; } #else /* !CONFIG_PM_DEBUG */ static int hibernation_testmode(int mode) { return 0; } static int hibernation_test(int level) { return 0; } #endif /* !CONFIG_PM_DEBUG */ /** * platform_begin - tell the platform driver that we're starting * hibernation */ static int platform_begin(int platform_mode) { return (platform_mode && hibernation_ops) ? hibernation_ops->begin() : 0; } /** * platform_end - tell the platform driver that we've entered the * working state */ static void platform_end(int platform_mode) { if (platform_mode && hibernation_ops) hibernation_ops->end(); } /** * platform_pre_snapshot - prepare the machine for hibernation using the * platform driver if so configured and return an error code if it fails */ static int platform_pre_snapshot(int platform_mode) { return (platform_mode && hibernation_ops) ? hibernation_ops->pre_snapshot() : 0; } /** * platform_leave - prepare the machine for switching to the normal mode * of operation using the platform driver (called with interrupts disabled) */ static void platform_leave(int platform_mode) { if (platform_mode && hibernation_ops) hibernation_ops->leave(); } /** * platform_finish - switch the machine to the normal mode of operation * using the platform driver (must be called after platform_prepare()) */ static void platform_finish(int platform_mode) { if (platform_mode && hibernation_ops) 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(); } /** * platform_recover - recover the platform from a failure to suspend * devices. */ static void platform_recover(int platform_mode) { if (platform_mode && hibernation_ops && hibernation_ops->recover) hibernation_ops->recover(); } /** * create_image - freeze devices that need to be frozen with interrupts * off, create the hibernation image and thaw those devices. Control * reappears in this routine after a restore. */ static int create_image(int platform_mode) { int error; error = arch_prepare_suspend(); if (error) return error; /* At this point, device_suspend() has been called, but *not* * device_power_down(). We *must* call device_power_down() now. * Otherwise, drivers for some devices (e.g. interrupt controllers) * become desynchronized with the actual state of the hardware * at resume time, and evil weirdness ensues. */ error = device_power_down(PMSG_FREEZE); if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting hibernation\n"); return error; } error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) goto Platform_finish; error = disable_nonboot_cpus(); if (error || hibernation_test(TEST_CPUS) || hibernation_testmode(HIBERNATION_TEST)) goto Enable_cpus; local_irq_disable(); error = sysdev_suspend(PMSG_FREEZE); if (error) { printk(KERN_ERR "PM: Some system devices failed to power down, " "aborting hibernation\n"); goto Enable_irqs; } if (hibernation_test(TEST_CORE)) goto Power_up; in_suspend = 1; save_processor_state(); error = swsusp_arch_suspend(); if (error) printk(KERN_ERR "PM: Error %d creating hibernation image\n", error); /* Restore control flow magically appears here */ restore_processor_state(); if (!in_suspend) platform_leave(platform_mode); Power_up: sysdev_resume(); /* NOTE: device_power_up() is just a resume() for devices * that suspended with irqs off ... no overall powerup. */ Enable_irqs: local_irq_enable(); Enable_cpus: enable_nonboot_cpus(); Platform_finish: platform_finish(platform_mode); device_power_up(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); return error; } /** * hibernation_snapshot - quiesce devices and create the hibernation * snapshot image. * @platform_mode - if set, use the platform driver, if available, to * prepare the platform firmware for the power transition. * * Must be called with pm_mutex held */ int hibernation_snapshot(int platform_mode) { int error; error = platform_begin(platform_mode); if (error) return error; /* Free memory before shutting down devices. */ error = swsusp_shrink_memory(); if (error) goto Close; suspend_console(); error = device_suspend(PMSG_FREEZE); if (error) goto Recover_platform; if (hibernation_test(TEST_DEVICES)) goto Recover_platform; error = create_image(platform_mode); /* Control returns here after successful restore */ Resume_devices: device_resume(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); resume_console(); Close: platform_end(platform_mode); return error; Recover_platform: platform_recover(platform_mode); goto Resume_devices; } /** * resume_target_kernel - prepare devices that need to be suspended with * interrupts off, restore the contents of highmem that have not been * restored yet from the image and run the low level code that will restore * the remaining contents of memory and switch to the just restored target * kernel. */ static int resume_target_kernel(bool platform_mode) { int error; error = device_power_down(PMSG_QUIESCE); if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting resume\n"); return error; } error = platform_pre_restore(platform_mode); if (error) goto Cleanup; error = disable_nonboot_cpus(); if (error) goto Enable_cpus; local_irq_disable(); error = sysdev_suspend(PMSG_QUIESCE); if (error) goto Enable_irqs; /* We'll ignore saved state, but this gets preempt count (etc) right */ save_processor_state(); error = restore_highmem(); if (!error) { error = swsusp_arch_resume(); /* * The code below is only ever reached in case of a failure. * Otherwise execution continues at place where * swsusp_arch_suspend() was called */ BUG_ON(!error); /* This call to restore_highmem() undos the previous one */ restore_highmem(); } /* * The only reason why swsusp_arch_resume() can fail is memory being * very tight, so we have to free it as soon as we can to avoid * subsequent failures */ swsusp_free(); restore_processor_state(); touch_softlockup_watchdog(); sysdev_resume(); Enable_irqs: local_irq_enable(); Enable_cpus: enable_nonboot_cpus(); Cleanup: platform_restore_cleanup(platform_mode); device_power_up(PMSG_RECOVER); return error; } /** * 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 firmware for the transition. * * Must be called with pm_mutex held */ int hibernation_restore(int platform_mode) { int error; pm_prepare_console(); suspend_console(); error = device_suspend(PMSG_QUIESCE); if (!error) { error = resume_target_kernel(platform_mode); device_resume(PMSG_RECOVER); } resume_console(); pm_restore_console(); return error; } /** * hibernation_platform_enter - enter the hibernation state using the * platform driver (if available) */ int hibernation_platform_enter(void) { int error; if (!hibernation_ops) return -ENOSYS; /* * We have cancelled the power transition by running * hibernation_ops->finish() before saving the image, so we should let * the firmware know that we're going to enter the sleep state after all */ error = hibernation_ops->begin(); if (error) goto Close; entering_platform_hibernation = true; suspend_console(); error = device_suspend(PMSG_HIBERNATE); if (error) { if (hibernation_ops->recover) hibernation_ops->recover(); goto Resume_devices; } error = device_power_down(PMSG_HIBERNATE); if (error) goto Resume_devices; error = hibernation_ops->prepare(); if (error) goto Platofrm_finish; error = disable_nonboot_cpus(); if (error) goto Platofrm_finish; local_irq_disable(); sysdev_suspend(PMSG_HIBERNATE); hibernation_ops->enter(); /* We should never get here */ while (1); /* * We don't need to reenable the nonboot CPUs or resume consoles, since * the system is going to be halted anyway. */ Platofrm_finish: hibernation_ops->finish(); device_power_up(PMSG_RESTORE); Resume_devices: entering_platform_hibernation = false; device_resume(PMSG_RESTORE); resume_console(); Close: hibernation_ops->end(); return error; } /** * power_down - Shut the machine down for hibernation. * * Use the platform driver, if configured so; otherwise try * to power off or reboot. */