📄 disk.c
字号:
/* * 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 <linux/ftrace.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);}#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; device_pm_lock(); 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 "PM: Some 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: /* NOTE: device_power_up() is just a resume() for devices * that suspended with irqs off ... no overall powerup. */ device_power_up(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); Enable_irqs: local_irq_enable(); device_pm_unlock(); 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, ftrace_save; /* Free memory before shutting down devices. */ error = swsusp_shrink_memory(); if (error) return error; error = platform_begin(platform_mode); if (error) goto Close; suspend_console(); ftrace_save = __ftrace_enabled_save(); error = device_suspend(PMSG_FREEZE); if (error) goto Recover_platform; if (hibernation_test(TEST_DEVICES)) goto Recover_platform; error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) goto Finish; error = disable_nonboot_cpus(); if (!error) { if (hibernation_test(TEST_CPUS)) goto Enable_cpus; if (hibernation_testmode(HIBERNATION_TEST)) goto Enable_cpus; error = create_image(platform_mode); /* Control returns here after successful restore */ } Enable_cpus: enable_nonboot_cpus(); Finish: platform_finish(platform_mode); Resume_devices: device_resume(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); __ftrace_enabled_restore(ftrace_save); 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(void){ int error; device_pm_lock(); local_irq_disable(); error = device_power_down(PMSG_QUIESCE); if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting resume\n"); 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(); device_power_up(PMSG_RECOVER); Enable_irqs: local_irq_enable(); device_pm_unlock(); 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, ftrace_save; pm_prepare_console(); suspend_console(); ftrace_save = __ftrace_enabled_save(); error = device_suspend(PMSG_QUIESCE); if (error) goto Finish; error = platform_pre_restore(platform_mode); if (!error) { error = disable_nonboot_cpus(); if (!error) error = resume_target_kernel(); enable_nonboot_cpus(); } platform_restore_cleanup(platform_mode); device_resume(PMSG_RECOVER); Finish: __ftrace_enabled_restore(ftrace_save); 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, ftrace_save; 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; suspend_console(); ftrace_save = __ftrace_enabled_save(); error = device_suspend(PMSG_HIBERNATE); if (error) { if (hibernation_ops->recover) hibernation_ops->recover(); goto Resume_devices; } error = hibernation_ops->prepare(); if (error) goto Resume_devices; error = disable_nonboot_cpus(); if (error) goto Finish; device_pm_lock(); local_irq_disable(); error = device_power_down(PMSG_HIBERNATE); if (!error) { hibernation_ops->enter(); /* We should never get here */ while (1); } local_irq_enable(); device_pm_unlock(); /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -