/* * 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/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 "power.h" static int noresume = 0;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->start && ops->pre_snapshot && ops->finish && ops->prepare && 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);} /** * platform_start - tell the platform driver that we're starting * hibernation */ static int platform_start(int platform_mode){ return (platform_mode && hibernation_ops) ? hibernation_ops->start() : 0;} /** * 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();} /** * 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. */ int create_image(int platform_mode){ int error; error = arch_prepare_suspend(); if (error) return error; local_irq_disable(); /* 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 "Some devices failed to power down, " KERN_ERR "aborting suspend\n"); goto Enable_irqs; } save_processor_state(); error = swsusp_arch_suspend(); if (error) printk(KERN_ERR "Error %d while creating the image\n", error); /* Restore control flow magically appears here */ restore_processor_state(); if (!in_suspend) platform_leave(platform_mode); /* NOTE: device_power_up() is just a resume() for devices * that suspended with irqs off ... no overall powerup. */ device_power_up(); Enable_irqs: local_irq_enable(); 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 frimware for the power transition. * * Must be called with pm_mutex held */ int hibernation_snapshot(int platform_mode){ int error; /* Free memory before shutting down devices. */ error = swsusp_shrink_memory(); if (error) return error; error = platform_start(platform_mode); if (error) return error; suspend_console(); error = device_suspend(PMSG_FREEZE); if (error) goto Resume_console; error = platform_pre_snapshot(platform_mode); if (error) goto Resume_devices; error = disable_nonboot_cpus(); if (!error) { if (hibernation_mode != HIBERNATION_TEST) { in_suspend = 1; error = create_image(platform_mode); /* Control returns here after successful restore */ } else { printk("swsusp debug: Waiting for 5 seconds.\n"); mdelay(5000); } } enable_nonboot_cpus(); Resume_devices: platform_finish(platform_mode); device_resume(); Resume_console: resume_console(); 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 frimware 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_PRETHAW); if (error) goto Finish; 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); device_resume(); Finish: 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->start(); if (error) return error; suspend_console(); error = device_suspend(PMSG_SUSPEND); if (error) goto Resume_console; error = hibernation_ops->prepare(); if (error) goto Resume_devices; error = disable_nonboot_cpus(); if (error) goto Finish; local_irq_disable(); error = device_power_down(PMSG_SUSPEND); if (!error) { hibernation_ops->enter(); /* We should never get here */ while (1); } local_irq_enable(); /* * We don't need to reenable the nonboot CPUs or resume consoles, since * the system is going to be halted anyway. */ Finish: hibernation_ops->finish(); Resume_devices: device_resume(); Resume_console: resume_console(); return error;} /** * power_down - Shut the machine down for hibernation. * * Use the platform driver, if configured so; otherwise try * to power off or reboot. */ static void power_down(void){ switch (hibernation_mode) { case HIBERNATION_TEST: case HIBERNATION_TESTPROC: break; case HIBERNATION_REBOOT: kernel_restart(NULL); break; case HIBERNATION_PLATFORM: hibernation_platform_enter(); case HIBERNATION_SHUTDOWN: kernel_power_off(); break; } kernel_halt(); /* * Valid image is on the disk, if we continue we risk serious data * corruption after resume. */ printk(KERN_CRIT "Please power me down manually\n"); while(1);} static void unprepare_processes(void){ thaw_processes(); pm_restore_console();} static int prepare_processes(void){ int error = 0; pm_prepare_console(); if (freeze_processes()) { error = -EBUSY; unprepare_processes(); } return error;} /** * hibernate - The granpappy of the built-in hibernation management */ int hibernate(void){ int error; mutex_lock(&pm_mutex); /* The snapshot device should not be opened while we're running */ if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { error = -EBUSY; goto Unlock; } error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); if (error) goto Exit; /* Allocate memory management structures */ error = create_basic_memory_bitmaps(); if (error) goto Exit; printk("Syncing filesystems ... "); sys_sync(); printk("done.\n"); error = prepare_processes(); if (error) goto Finish; if (hibernation_mode == HIBERNATION_TESTPROC) { printk("swsusp debug: Waiting for 5 seconds.\n"); mdelay(5000); goto Thaw; } 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(flags); swsusp_free(); if (!error) power_down(); } else { pr_debug("PM: Image restored successfully.\n"); swsusp_free(); } Thaw: unprepare_processes(); Finish: free_basic_memory_bitmaps(); Exit: pm_notifier_call_chain(PM_POST_HIBERNATION); atomic_inc(&snapshot_device_available); Unlock: mutex_unlock(&pm_mutex); return error;} /** * software_resume - Resume from a saved image. * * Called as a late_initcall (so all devices are discovered and * initialized), we call swsusp to see if we have a saved image or not. * If so, we quiesce devices, the restore the saved image. We will * return above (in hibernate() ) if everything goes well. * Otherwise, we fail gracefully and return to the normally * scheduled program. * */ static int software_resume(void){ int error; unsigned int flags; /* * name_to_dev_t() below takes a sysfs buffer mutex when sysfs * is configured into the kernel. Since the regular hibernate * trigger path is via sysfs which takes a buffer mutex before * calling hibernate functions (which take pm_mutex) this can * cause lockdep to complain about a possible ABBA deadlock * which cannot happen since we're in the boot code here and * sysfs can't be invoked yet. Therefore, we use a subclass * here to avoid lockdep complaining. */ mutex_lock_nested(&pm_mutex, SINGLE_DEPTH_NESTING); if (!swsusp_resume_device) { if (!strlen(resume_file)) { mutex_unlock(&pm_mutex); return -ENOENT; } swsusp_resume_device = name_to_dev_t(resume_file); pr_debug("swsusp: Resume From Partition %s\n", resume_file); } else { pr_debug("swsusp: Resume From Partition %d:%d\n", MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device)); } if (noresume) { /** * FIXME: If noresume is specified, we need to find the partition * and reset it back to normal swap space. */ mutex_unlock(&pm_mutex); return 0; } pr_debug("PM: Checking swsusp image.\n"); error = swsusp_check(); if (error) goto Unlock; /* The snapshot device should not be opened while we're running */ if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { error = -EBUSY; goto Unlock; }Lines 1–500 of 747. 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/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 "power.h" static int noresume = 0; 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->start && ops->pre_snapshot && ops->finish && ops->prepare && 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); } /** * platform_start - tell the platform driver that we're starting * hibernation */ static int platform_start(int platform_mode) { return (platform_mode && hibernation_ops) ? hibernation_ops->start() : 0; } /** * 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(); } /** * 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. */ int create_image(int platform_mode) { int error; error = arch_prepare_suspend(); if (error) return error; local_irq_disable(); /* 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 "Some devices failed to power down, " KERN_ERR "aborting suspend\n"); goto Enable_irqs; } save_processor_state(); error = swsusp_arch_suspend(); if (error) printk(KERN_ERR "Error %d while creating the image\n", error); /* Restore control flow magically appears here */ restore_processor_state(); if (!in_suspend) platform_leave(platform_mode); /* NOTE: device_power_up() is just a resume() for devices * that suspended with irqs off ... no overall powerup. */ device_power_up(); Enable_irqs: local_irq_enable(); 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 frimware for the power transition. * * Must be called with pm_mutex held */ int hibernation_snapshot(int platform_mode) { int error; /* Free memory before shutting down devices. */ error = swsusp_shrink_memory(); if (error) return error; error = platform_start(platform_mode); if (error) return error; suspend_console(); error = device_suspend(PMSG_FREEZE); if (error) goto Resume_console; error = platform_pre_snapshot(platform_mode); if (error) goto Resume_devices; error = disable_nonboot_cpus(); if (!error) { if (hibernation_mode != HIBERNATION_TEST) { in_suspend = 1; error = create_image(platform_mode); /* Control returns here after successful restore */ } else { printk("swsusp debug: Waiting for 5 seconds.\n"); mdelay(5000); } } enable_nonboot_cpus(); Resume_devices: platform_finish(platform_mode); device_resume(); Resume_console: resume_console(); 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 frimware 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_PRETHAW); if (error) goto Finish; 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); device_resume(); Finish: 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->start(); if (error) return error; suspend_console(); error = device_suspend(PMSG_SUSPEND); if (error) goto Resume_console; error = hibernation_ops->prepare(); if (error) goto Resume_devices; error = disable_nonboot_cpus(); if (error) goto Finish; local_irq_disable(); error = device_power_down(PMSG_SUSPEND); if (!error) { hibernation_ops->enter(); /* We should never get here */ while (1); } local_irq_enable(); /* * We don't need to reenable the nonboot CPUs or resume consoles, since * the system is going to be halted anyway. */ Finish: hibernation_ops->finish(); Resume_devices: device_resume(); Resume_console: resume_console(); return error; } /** * power_down - Shut the machine down for hibernation. * * Use the platform driver, if configured so; otherwise try * to power off or reboot. */ static void power_down(void) { switch (hibernation_mode) { case HIBERNATION_TEST: case HIBERNATION_TESTPROC: break; case HIBERNATION_REBOOT: kernel_restart(NULL); break; case HIBERNATION_PLATFORM: hibernation_platform_enter(); case HIBERNATION_SHUTDOWN: kernel_power_off(); break; } kernel_halt(); /* * Valid image is on the disk, if we continue we risk serious data * corruption after resume. */ printk(KERN_CRIT "Please power me down manually\n"); while(1); } static void unprepare_processes(void) { thaw_processes(); pm_restore_console(); } static int prepare_processes(void) { int error = 0; pm_prepare_console(); if (freeze_processes()) { error = -EBUSY; unprepare_processes(); } return error; } /** * hibernate - The granpappy of the built-in hibernation management */ int hibernate(void) { int error; mutex_lock(&pm_mutex); /* The snapshot device should not be opened while we're running */ if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { error = -EBUSY; goto Unlock; } error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); if (error) goto Exit; /* Allocate memory management structures */ error = create_basic_memory_bitmaps(); if (error) goto Exit; printk("Syncing filesystems ... "); sys_sync(); printk("done.\n"); error = prepare_processes(); if (error) goto Finish; if (hibernation_mode == HIBERNATION_TESTPROC) { printk("swsusp debug: Waiting for 5 seconds.\n"); mdelay(5000); goto Thaw; } 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(flags); swsusp_free(); if (!error) power_down(); } else { pr_debug("PM: Image restored successfully.\n"); swsusp_free(); } Thaw: unprepare_processes(); Finish: free_basic_memory_bitmaps(); Exit: pm_notifier_call_chain(PM_POST_HIBERNATION); atomic_inc(&snapshot_device_available); Unlock: mutex_unlock(&pm_mutex); return error; } /** * software_resume - Resume from a saved image. * * Called as a late_initcall (so all devices are discovered and * initialized), we call swsusp to see if we have a saved image or not. * If so, we quiesce devices, the restore the saved image. We will * return above (in hibernate() ) if everything goes well. * Otherwise, we fail gracefully and return to the normally * scheduled program. * */ static int software_resume(void) { int error; unsigned int flags; /* * name_to_dev_t() below takes a sysfs buffer mutex when sysfs * is configured into the kernel. Since the regular hibernate * trigger path is via sysfs which takes a buffer mutex before * calling hibernate functions (which take pm_mutex) this can * cause lockdep to complain about a possible ABBA deadlock * which cannot happen since we're in the boot code here and * sysfs can't be invoked yet. Therefore, we use a subclass * here to avoid lockdep complaining. */ mutex_lock_nested(&pm_mutex, SINGLE_DEPTH_NESTING); if (!swsusp_resume_device) { if (!strlen(resume_file)) { mutex_unlock(&pm_mutex); return -ENOENT; } swsusp_resume_device = name_to_dev_t(resume_file); pr_debug("swsusp: Resume From Partition %s\n", resume_file); } else { pr_debug("swsusp: Resume From Partition %d:%d\n", MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device)); } if (noresume) { /** * FIXME: If noresume is specified, we need to find the partition * and reset it back to normal swap space. */ mutex_unlock(&pm_mutex); return 0; } pr_debug("PM: Checking swsusp image.\n"); error = swsusp_check(); if (error) goto Unlock; /* The snapshot device should not be opened while we're running */ if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { error = -EBUSY; goto Unlock; }