Merge branch 'suspend' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6

* 'suspend' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6: (38 commits)
  suspend: cleanup reference to swsusp_pg_dir[]
  PM: Remove obsolete /sys/devices/.../power/state docs
  Hibernation: Invoke suspend notifications after console switch
  Suspend: Invoke suspend notifications after console switch
  Suspend: Clean up suspend_64.c
  Suspend: Add config option to disable the freezer if architecture wants that
  ACPI: Print message before calling _PTS
  ACPI hibernation: Call _PTS before suspending devices
  Hibernation: Introduce begin() and end() callbacks
  ACPI suspend: Call _PTS before suspending devices
  ACPI: Separate disabling of GPEs from _PTS
  ACPI: Separate invocations of _GTS and _BFS from _PTS and _WAK
  Suspend: Introduce begin() and end() callbacks
  suspend: fix ia64 allmodconfig build
  ACPI: clear GPE earily in resume to avoid warning
  Suspend: Clean up Kconfig (V2)
  Hibernation: Clean up Kconfig (V2)
  Hibernation: Update messages
  Suspend: Use common prefix in messages
  Hibernation: Remove unnecessary variable declaration
  ...
This commit is contained in:
Linus Torvalds
2008-02-02 14:29:57 +11:00
34 changed files with 1032 additions and 505 deletions
+35 -30
View File
@@ -44,9 +44,30 @@ config PM_VERBOSE
---help---
This option enables verbose messages from the Power Management code.
config CAN_PM_TRACE
def_bool y
depends on PM_DEBUG && PM_SLEEP && EXPERIMENTAL
config PM_TRACE
bool
help
This enables code to save the last PM event point across
reboot. The architecture needs to support this, x86 for
example does by saving things in the RTC, see below.
The architecture specific code must provide the extern
functions from <linux/resume-trace.h> as well as the
<asm/resume-trace.h> header with a TRACE_RESUME() macro.
The way the information is presented is architecture-
dependent, x86 will print the information during a
late_initcall.
config PM_TRACE_RTC
bool "Suspend/resume event tracing"
depends on PM_DEBUG && X86 && PM_SLEEP && EXPERIMENTAL
depends on CAN_PM_TRACE
depends on X86
select PM_TRACE
default n
---help---
This enables some cheesy code to save the last PM event point in the
@@ -63,7 +84,8 @@ config PM_TRACE
config PM_SLEEP_SMP
bool
depends on SUSPEND_SMP_POSSIBLE || HIBERNATION_SMP_POSSIBLE
depends on SMP
depends on ARCH_SUSPEND_POSSIBLE || ARCH_HIBERNATION_POSSIBLE
depends on PM_SLEEP
select HOTPLUG_CPU
default y
@@ -73,46 +95,29 @@ config PM_SLEEP
depends on SUSPEND || HIBERNATION
default y
config SUSPEND_UP_POSSIBLE
bool
depends on (X86 && !X86_VOYAGER) || PPC || ARM || BLACKFIN || MIPS \
|| SUPERH || FRV
depends on !SMP
default y
config SUSPEND_SMP_POSSIBLE
bool
depends on (X86 && !X86_VOYAGER) \
|| (PPC && (PPC_PSERIES || PPC_PMAC)) || ARM
depends on SMP
default y
config SUSPEND
bool "Suspend to RAM and standby"
depends on PM
depends on SUSPEND_UP_POSSIBLE || SUSPEND_SMP_POSSIBLE
depends on PM && ARCH_SUSPEND_POSSIBLE
default y
---help---
Allow the system to enter sleep states in which main memory is
powered and thus its contents are preserved, such as the
suspend-to-RAM state (i.e. the ACPI S3 state).
suspend-to-RAM state (e.g. the ACPI S3 state).
config HIBERNATION_UP_POSSIBLE
bool
depends on X86 || PPC64_SWSUSP || PPC32
depends on !SMP
config SUSPEND_FREEZER
bool "Enable freezer for suspend to RAM/standby" \
if ARCH_WANTS_FREEZER_CONTROL || BROKEN
depends on SUSPEND
default y
help
This allows you to turn off the freezer for suspend. If this is
done, no tasks are frozen for suspend to RAM/standby.
config HIBERNATION_SMP_POSSIBLE
bool
depends on (X86 && !X86_VOYAGER) || PPC64_SWSUSP
depends on SMP
default y
Turning OFF this setting is NOT recommended! If in doubt, say Y.
config HIBERNATION
bool "Hibernation (aka 'suspend to disk')"
depends on PM && SWAP
depends on HIBERNATION_UP_POSSIBLE || HIBERNATION_SMP_POSSIBLE
depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
---help---
Enable the suspend to disk (STD) functionality, which is usually
called "hibernation" in user interfaces. STD checkpoints the
+155 -49
View File
@@ -54,8 +54,8 @@ static struct platform_hibernation_ops *hibernation_ops;
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
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;
@@ -70,15 +70,55 @@ void hibernation_set_ops(struct platform_hibernation_ops *ops)
mutex_unlock(&pm_mutex);
}
#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_start - tell the platform driver that we're starting
* platform_begin - tell the platform driver that we're starting
* hibernation
*/
static int platform_start(int platform_mode)
static int platform_begin(int platform_mode)
{
return (platform_mode && hibernation_ops) ?
hibernation_ops->start() : 0;
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();
}
/**
@@ -162,19 +202,25 @@ int create_image(int platform_mode)
*/
error = device_power_down(PMSG_FREEZE);
if (error) {
printk(KERN_ERR "Some devices failed to power down, "
KERN_ERR "aborting suspend\n");
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 "Error %d while creating the image\n", 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.
*/
@@ -202,36 +248,90 @@ int hibernation_snapshot(int platform_mode)
if (error)
return error;
error = platform_start(platform_mode);
error = platform_begin(platform_mode);
if (error)
return error;
goto Close;
suspend_console();
error = device_suspend(PMSG_FREEZE);
if (error)
goto Resume_console;
error = platform_pre_snapshot(platform_mode);
if (error)
if (hibernation_test(TEST_DEVICES))
goto Resume_devices;
error = platform_pre_snapshot(platform_mode);
if (error || hibernation_test(TEST_PLATFORM))
goto Finish;
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);
}
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();
Resume_devices:
Finish:
platform_finish(platform_mode);
Resume_devices:
device_resume();
Resume_console:
resume_console();
Close:
platform_end(platform_mode);
return error;
}
/**
* 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;
local_irq_disable();
error = device_power_down(PMSG_PRETHAW);
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();
Enable_irqs:
local_irq_enable();
return error;
}
@@ -258,7 +358,7 @@ int hibernation_restore(int platform_mode)
if (!error) {
error = disable_nonboot_cpus();
if (!error)
error = swsusp_resume();
error = resume_target_kernel();
enable_nonboot_cpus();
}
platform_restore_cleanup(platform_mode);
@@ -286,9 +386,9 @@ int hibernation_platform_enter(void)
* 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();
error = hibernation_ops->begin();
if (error)
return error;
goto Close;
suspend_console();
error = device_suspend(PMSG_SUSPEND);
@@ -322,6 +422,8 @@ int hibernation_platform_enter(void)
device_resume();
Resume_console:
resume_console();
Close:
hibernation_ops->end();
return error;
}
@@ -352,24 +454,17 @@ static void power_down(void)
* 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");
printk(KERN_CRIT "PM: Please power 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();
thaw_processes();
}
return error;
}
@@ -389,6 +484,7 @@ int hibernate(void)
goto Unlock;
}
pm_prepare_console();
error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
if (error)
goto Exit;
@@ -398,7 +494,7 @@ int hibernate(void)
if (error)
goto Exit;
printk("Syncing filesystems ... ");
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
printk("done.\n");
@@ -406,11 +502,12 @@ int hibernate(void)
if (error)
goto Finish;
if (hibernation_mode == HIBERNATION_TESTPROC) {
printk("swsusp debug: Waiting for 5 seconds.\n");
mdelay(5000);
if (hibernation_test(TEST_FREEZER))
goto Thaw;
}
if (hibernation_testmode(HIBERNATION_TESTPROC))
goto Thaw;
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
if (in_suspend && !error) {
unsigned int flags = 0;
@@ -427,11 +524,12 @@ int hibernate(void)
swsusp_free();
}
Thaw:
unprepare_processes();
thaw_processes();
Finish:
free_basic_memory_bitmaps();
Exit:
pm_notifier_call_chain(PM_POST_HIBERNATION);
pm_restore_console();
atomic_inc(&snapshot_device_available);
Unlock:
mutex_unlock(&pm_mutex);
@@ -473,22 +571,23 @@ static int software_resume(void)
return -ENOENT;
}
swsusp_resume_device = name_to_dev_t(resume_file);
pr_debug("swsusp: Resume From Partition %s\n", resume_file);
pr_debug("PM: 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));
pr_debug("PM: 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.
* 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");
pr_debug("PM: Checking hibernation image.\n");
error = swsusp_check();
if (error)
goto Unlock;
@@ -499,6 +598,11 @@ static int software_resume(void)
goto Unlock;
}
pm_prepare_console();
error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
if (error)
goto Finish;
error = create_basic_memory_bitmaps();
if (error)
goto Finish;
@@ -510,7 +614,7 @@ static int software_resume(void)
goto Done;
}
pr_debug("PM: Reading swsusp image.\n");
pr_debug("PM: Reading hibernation image.\n");
error = swsusp_read(&flags);
if (!error)
@@ -518,10 +622,12 @@ static int software_resume(void)
printk(KERN_ERR "PM: Restore failed, recovering.\n");
swsusp_free();
unprepare_processes();
thaw_processes();
Done:
free_basic_memory_bitmaps();
Finish:
pm_notifier_call_chain(PM_POST_RESTORE);
pm_restore_console();
atomic_inc(&snapshot_device_available);
/* For success case, the suspend path will release the lock */
Unlock:
@@ -636,7 +742,7 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
error = -EINVAL;
if (!error)
pr_debug("PM: suspend-to-disk mode set to '%s'\n",
pr_debug("PM: Hibernation mode set to '%s'\n",
hibernation_modes[mode]);
mutex_unlock(&pm_mutex);
return error ? error : n;
@@ -668,7 +774,7 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr,
mutex_lock(&pm_mutex);
swsusp_resume_device = res;
mutex_unlock(&pm_mutex);
printk("Attempting manual resume\n");
printk(KERN_INFO "PM: Starting manual resume from disk\n");
noresume = 0;
software_resume();
ret = n;
+145 -26
View File
@@ -24,13 +24,112 @@
#include "power.h"
BLOCKING_NOTIFIER_HEAD(pm_chain_head);
DEFINE_MUTEX(pm_mutex);
unsigned int pm_flags;
EXPORT_SYMBOL(pm_flags);
#ifdef CONFIG_PM_SLEEP
/* Routines for PM-transition notifications */
static BLOCKING_NOTIFIER_HEAD(pm_chain_head);
int register_pm_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&pm_chain_head, nb);
}
EXPORT_SYMBOL_GPL(register_pm_notifier);
int unregister_pm_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&pm_chain_head, nb);
}
EXPORT_SYMBOL_GPL(unregister_pm_notifier);
int pm_notifier_call_chain(unsigned long val)
{
return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)
== NOTIFY_BAD) ? -EINVAL : 0;
}
#ifdef CONFIG_PM_DEBUG
int pm_test_level = TEST_NONE;
static int suspend_test(int level)
{
if (pm_test_level == level) {
printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
mdelay(5000);
return 1;
}
return 0;
}
static const char * const pm_tests[__TEST_AFTER_LAST] = {
[TEST_NONE] = "none",
[TEST_CORE] = "core",
[TEST_CPUS] = "processors",
[TEST_PLATFORM] = "platform",
[TEST_DEVICES] = "devices",
[TEST_FREEZER] = "freezer",
};
static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
int level;
for (level = TEST_FIRST; level <= TEST_MAX; level++)
if (pm_tests[level]) {
if (level == pm_test_level)
s += sprintf(s, "[%s] ", pm_tests[level]);
else
s += sprintf(s, "%s ", pm_tests[level]);
}
if (s != buf)
/* convert the last space to a newline */
*(s-1) = '\n';
return (s - buf);
}
static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
const char * const *s;
int level;
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
mutex_lock(&pm_mutex);
level = TEST_FIRST;
for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++)
if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
pm_test_level = level;
error = 0;
break;
}
mutex_unlock(&pm_mutex);
return error ? error : n;
}
power_attr(pm_test);
#else /* !CONFIG_PM_DEBUG */
static inline int suspend_test(int level) { return 0; }
#endif /* !CONFIG_PM_DEBUG */
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_SUSPEND
/* This is just an arbitrary number */
@@ -76,13 +175,13 @@ static int suspend_prepare(void)
if (!suspend_ops || !suspend_ops->enter)
return -EPERM;
pm_prepare_console();
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
goto Finish;
pm_prepare_console();
if (freeze_processes()) {
if (suspend_freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
@@ -100,10 +199,10 @@ static int suspend_prepare(void)
return 0;
Thaw:
thaw_processes();
pm_restore_console();
suspend_thaw_processes();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
@@ -133,10 +232,13 @@ static int suspend_enter(suspend_state_t state)
BUG_ON(!irqs_disabled());
if ((error = device_power_down(PMSG_SUSPEND))) {
printk(KERN_ERR "Some devices failed to power down\n");
printk(KERN_ERR "PM: Some devices failed to power down\n");
goto Done;
}
error = suspend_ops->enter(state);
if (!suspend_test(TEST_CORE))
error = suspend_ops->enter(state);
device_power_up();
Done:
arch_suspend_enable_irqs();
@@ -145,8 +247,8 @@ static int suspend_enter(suspend_state_t state)
}
/**
* suspend_devices_and_enter - suspend devices and enter the desired system sleep
* state.
* suspend_devices_and_enter - suspend devices and enter the desired system
* sleep state.
* @state: state to enter
*/
int suspend_devices_and_enter(suspend_state_t state)
@@ -156,33 +258,45 @@ int suspend_devices_and_enter(suspend_state_t state)
if (!suspend_ops)
return -ENOSYS;
if (suspend_ops->set_target) {
error = suspend_ops->set_target(state);
if (suspend_ops->begin) {
error = suspend_ops->begin(state);
if (error)
return error;
goto Close;
}
suspend_console();
error = device_suspend(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "Some devices failed to suspend\n");
printk(KERN_ERR "PM: Some devices failed to suspend\n");
goto Resume_console;
}
if (suspend_test(TEST_DEVICES))
goto Resume_devices;
if (suspend_ops->prepare) {
error = suspend_ops->prepare();
if (error)
goto Resume_devices;
}
if (suspend_test(TEST_PLATFORM))
goto Finish;
error = disable_nonboot_cpus();
if (!error)
if (!error && !suspend_test(TEST_CPUS))
suspend_enter(state);
enable_nonboot_cpus();
Finish:
if (suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
device_resume();
Resume_console:
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
return error;
}
@@ -194,9 +308,9 @@ int suspend_devices_and_enter(suspend_state_t state)
*/
static void suspend_finish(void)
{
thaw_processes();
pm_restore_console();
suspend_thaw_processes();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
}
@@ -238,17 +352,22 @@ static int enter_state(suspend_state_t state)
if (!mutex_trylock(&pm_mutex))
return -EBUSY;
printk("Syncing filesystems ... ");
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
printk("done.\n");
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
if ((error = suspend_prepare()))
error = suspend_prepare();
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM: Entering %s sleep\n", pm_states[state]);
error = suspend_devices_and_enter(state);
Finish:
pr_debug("PM: Finishing wakeup.\n");
suspend_finish();
Unlock:
@@ -369,18 +488,18 @@ pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr,
}
power_attr(pm_trace);
#endif /* CONFIG_PM_TRACE */
static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
#endif
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
&pm_test_attr.attr,
#endif
NULL,
};
#else
static struct attribute * g[] = {
&state_attr.attr,
NULL,
};
#endif /* CONFIG_PM_TRACE */
static struct attribute_group attr_group = {
.attrs = g,
+53 -37
View File
@@ -1,5 +1,7 @@
#include <linux/suspend.h>
#include <linux/suspend_ioctls.h>
#include <linux/utsname.h>
#include <linux/freezer.h>
struct swsusp_info {
struct new_utsname uts;
@@ -128,42 +130,12 @@ struct snapshot_handle {
#define data_of(handle) ((handle).buffer + (handle).buf_offset)
extern unsigned int snapshot_additional_pages(struct zone *zone);
extern unsigned long snapshot_get_image_size(void);
extern int snapshot_read_next(struct snapshot_handle *handle, size_t count);
extern int snapshot_write_next(struct snapshot_handle *handle, size_t count);
extern void snapshot_write_finalize(struct snapshot_handle *handle);
extern int snapshot_image_loaded(struct snapshot_handle *handle);
/*
* This structure is used to pass the values needed for the identification
* of the resume swap area from a user space to the kernel via the
* SNAPSHOT_SET_SWAP_AREA ioctl
*/
struct resume_swap_area {
loff_t offset;
u_int32_t dev;
} __attribute__((packed));
#define SNAPSHOT_IOC_MAGIC '3'
#define SNAPSHOT_FREEZE _IO(SNAPSHOT_IOC_MAGIC, 1)
#define SNAPSHOT_UNFREEZE _IO(SNAPSHOT_IOC_MAGIC, 2)
#define SNAPSHOT_ATOMIC_SNAPSHOT _IOW(SNAPSHOT_IOC_MAGIC, 3, void *)
#define SNAPSHOT_ATOMIC_RESTORE _IO(SNAPSHOT_IOC_MAGIC, 4)
#define SNAPSHOT_FREE _IO(SNAPSHOT_IOC_MAGIC, 5)
#define SNAPSHOT_SET_IMAGE_SIZE _IOW(SNAPSHOT_IOC_MAGIC, 6, unsigned long)
#define SNAPSHOT_AVAIL_SWAP _IOR(SNAPSHOT_IOC_MAGIC, 7, void *)
#define SNAPSHOT_GET_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 8, void *)
#define SNAPSHOT_FREE_SWAP_PAGES _IO(SNAPSHOT_IOC_MAGIC, 9)
#define SNAPSHOT_SET_SWAP_FILE _IOW(SNAPSHOT_IOC_MAGIC, 10, unsigned int)
#define SNAPSHOT_S2RAM _IO(SNAPSHOT_IOC_MAGIC, 11)
#define SNAPSHOT_PMOPS _IOW(SNAPSHOT_IOC_MAGIC, 12, unsigned int)
#define SNAPSHOT_SET_SWAP_AREA _IOW(SNAPSHOT_IOC_MAGIC, 13, \
struct resume_swap_area)
#define SNAPSHOT_IOC_MAXNR 13
#define PMOPS_PREPARE 1
#define PMOPS_ENTER 2
#define PMOPS_FINISH 3
/* If unset, the snapshot device cannot be open. */
extern atomic_t snapshot_device_available;
@@ -181,7 +153,6 @@ extern int swsusp_swap_in_use(void);
extern int swsusp_check(void);
extern int swsusp_shrink_memory(void);
extern void swsusp_free(void);
extern int swsusp_resume(void);
extern int swsusp_read(unsigned int *flags_p);
extern int swsusp_write(unsigned int flags);
extern void swsusp_close(void);
@@ -201,11 +172,56 @@ static inline int suspend_devices_and_enter(suspend_state_t state)
}
#endif /* !CONFIG_SUSPEND */
/* kernel/power/common.c */
extern struct blocking_notifier_head pm_chain_head;
#ifdef CONFIG_PM_SLEEP
/* kernel/power/main.c */
extern int pm_notifier_call_chain(unsigned long val);
#endif
static inline int pm_notifier_call_chain(unsigned long val)
#ifdef CONFIG_HIGHMEM
unsigned int count_highmem_pages(void);
int restore_highmem(void);
#else
static inline unsigned int count_highmem_pages(void) { return 0; }
static inline int restore_highmem(void) { return 0; }
#endif
/*
* Suspend test levels
*/
enum {
/* keep first */
TEST_NONE,
TEST_CORE,
TEST_CPUS,
TEST_PLATFORM,
TEST_DEVICES,
TEST_FREEZER,
/* keep last */
__TEST_AFTER_LAST
};
#define TEST_FIRST TEST_NONE
#define TEST_MAX (__TEST_AFTER_LAST - 1)
extern int pm_test_level;
#ifdef CONFIG_SUSPEND_FREEZER
static inline int suspend_freeze_processes(void)
{
return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)
== NOTIFY_BAD) ? -EINVAL : 0;
return freeze_processes();
}
static inline void suspend_thaw_processes(void)
{
thaw_processes();
}
#else
static inline int suspend_freeze_processes(void)
{
return 0;
}
static inline void suspend_thaw_processes(void)
{
}
#endif
+18 -13
View File
@@ -635,7 +635,7 @@ __register_nosave_region(unsigned long start_pfn, unsigned long end_pfn,
region->end_pfn = end_pfn;
list_add_tail(&region->list, &nosave_regions);
Report:
printk("swsusp: Registered nosave memory region: %016lx - %016lx\n",
printk(KERN_INFO "PM: Registered nosave memory: %016lx - %016lx\n",
start_pfn << PAGE_SHIFT, end_pfn << PAGE_SHIFT);
}
@@ -704,7 +704,7 @@ static void mark_nosave_pages(struct memory_bitmap *bm)
list_for_each_entry(region, &nosave_regions, list) {
unsigned long pfn;
printk("swsusp: Marking nosave pages: %016lx - %016lx\n",
pr_debug("PM: Marking nosave pages: %016lx - %016lx\n",
region->start_pfn << PAGE_SHIFT,
region->end_pfn << PAGE_SHIFT);
@@ -749,7 +749,7 @@ int create_basic_memory_bitmaps(void)
free_pages_map = bm2;
mark_nosave_pages(forbidden_pages_map);
printk("swsusp: Basic memory bitmaps created\n");
pr_debug("PM: Basic memory bitmaps created\n");
return 0;
@@ -784,7 +784,7 @@ void free_basic_memory_bitmaps(void)
memory_bm_free(bm2, PG_UNSAFE_CLEAR);
kfree(bm2);
printk("swsusp: Basic memory bitmaps freed\n");
pr_debug("PM: Basic memory bitmaps freed\n");
}
/**
@@ -872,7 +872,6 @@ unsigned int count_highmem_pages(void)
}
#else
static inline void *saveable_highmem_page(unsigned long pfn) { return NULL; }
static inline unsigned int count_highmem_pages(void) { return 0; }
#endif /* CONFIG_HIGHMEM */
/**
@@ -1089,7 +1088,7 @@ static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem)
}
nr_pages += count_pages_for_highmem(nr_highmem);
pr_debug("swsusp: Normal pages needed: %u + %u + %u, available pages: %u\n",
pr_debug("PM: Normal pages needed: %u + %u + %u, available pages: %u\n",
nr_pages, PAGES_FOR_IO, meta, free);
return free > nr_pages + PAGES_FOR_IO + meta;
@@ -1202,20 +1201,20 @@ asmlinkage int swsusp_save(void)
{
unsigned int nr_pages, nr_highmem;
printk("swsusp: critical section: \n");
printk(KERN_INFO "PM: Creating hibernation image: \n");
drain_local_pages();
nr_pages = count_data_pages();
nr_highmem = count_highmem_pages();
printk("swsusp: Need to copy %u pages\n", nr_pages + nr_highmem);
printk(KERN_INFO "PM: Need to copy %u pages\n", nr_pages + nr_highmem);
if (!enough_free_mem(nr_pages, nr_highmem)) {
printk(KERN_ERR "swsusp: Not enough free memory\n");
printk(KERN_ERR "PM: Not enough free memory\n");
return -ENOMEM;
}
if (swsusp_alloc(&orig_bm, &copy_bm, nr_pages, nr_highmem)) {
printk(KERN_ERR "swsusp: Memory allocation failed\n");
printk(KERN_ERR "PM: Memory allocation failed\n");
return -ENOMEM;
}
@@ -1235,7 +1234,8 @@ asmlinkage int swsusp_save(void)
nr_copy_pages = nr_pages;
nr_meta_pages = DIV_ROUND_UP(nr_pages * sizeof(long), PAGE_SIZE);
printk("swsusp: critical section: done (%d pages copied)\n", nr_pages);
printk(KERN_INFO "PM: Hibernation image created (%d pages copied)\n",
nr_pages);
return 0;
}
@@ -1264,12 +1264,17 @@ static char *check_image_kernel(struct swsusp_info *info)
}
#endif /* CONFIG_ARCH_HIBERNATION_HEADER */
unsigned long snapshot_get_image_size(void)
{
return nr_copy_pages + nr_meta_pages + 1;
}
static int init_header(struct swsusp_info *info)
{
memset(info, 0, sizeof(struct swsusp_info));
info->num_physpages = num_physpages;
info->image_pages = nr_copy_pages;
info->pages = nr_copy_pages + nr_meta_pages + 1;
info->pages = snapshot_get_image_size();
info->size = info->pages;
info->size <<= PAGE_SHIFT;
return init_header_complete(info);
@@ -1429,7 +1434,7 @@ static int check_header(struct swsusp_info *info)
if (!reason && info->num_physpages != num_physpages)
reason = "memory size";
if (reason) {
printk(KERN_ERR "swsusp: Resume mismatch: %s\n", reason);
printk(KERN_ERR "PM: Image mismatch: %s\n", reason);
return -EPERM;
}
return 0;
+17 -16
View File
@@ -28,8 +28,6 @@
#include "power.h"
extern char resume_file[];
#define SWSUSP_SIG "S1SUSPEND"
struct swsusp_header {
@@ -73,7 +71,8 @@ static int submit(int rw, pgoff_t page_off, struct page *page,
bio->bi_end_io = end_swap_bio_read;
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
printk("swsusp: ERROR: adding page to bio at %ld\n", page_off);
printk(KERN_ERR "PM: Adding page to bio failed at %ld\n",
page_off);
bio_put(bio);
return -EFAULT;
}
@@ -153,7 +152,7 @@ static int mark_swapfiles(sector_t start, unsigned int flags)
error = bio_write_page(swsusp_resume_block,
swsusp_header, NULL);
} else {
printk(KERN_ERR "swsusp: Swap header not found!\n");
printk(KERN_ERR "PM: Swap header not found!\n");
error = -ENODEV;
}
return error;
@@ -325,7 +324,8 @@ static int save_image(struct swap_map_handle *handle,
struct timeval start;
struct timeval stop;
printk("Saving image data pages (%u pages) ... ", nr_to_write);
printk(KERN_INFO "PM: Saving image data pages (%u pages) ... ",
nr_to_write);
m = nr_to_write / 100;
if (!m)
m = 1;
@@ -365,7 +365,7 @@ static int enough_swap(unsigned int nr_pages)
{
unsigned int free_swap = count_swap_pages(root_swap, 1);
pr_debug("swsusp: free swap pages: %u\n", free_swap);
pr_debug("PM: Free swap pages: %u\n", free_swap);
return free_swap > nr_pages + PAGES_FOR_IO;
}
@@ -388,7 +388,7 @@ int swsusp_write(unsigned int flags)
error = swsusp_swap_check();
if (error) {
printk(KERN_ERR "swsusp: Cannot find swap device, try "
printk(KERN_ERR "PM: Cannot find swap device, try "
"swapon -a.\n");
return error;
}
@@ -402,7 +402,7 @@ int swsusp_write(unsigned int flags)
}
header = (struct swsusp_info *)data_of(snapshot);
if (!enough_swap(header->pages)) {
printk(KERN_ERR "swsusp: Not enough free swap\n");
printk(KERN_ERR "PM: Not enough free swap\n");
error = -ENOSPC;
goto out;
}
@@ -417,7 +417,7 @@ int swsusp_write(unsigned int flags)
if (!error) {
flush_swap_writer(&handle);
printk("S");
printk(KERN_INFO "PM: S");
error = mark_swapfiles(start, flags);
printk("|\n");
}
@@ -507,7 +507,8 @@ static int load_image(struct swap_map_handle *handle,
int err2;
unsigned nr_pages;
printk("Loading image data pages (%u pages) ... ", nr_to_read);
printk(KERN_INFO "PM: Loading image data pages (%u pages) ... ",
nr_to_read);
m = nr_to_read / 100;
if (!m)
m = 1;
@@ -558,7 +559,7 @@ int swsusp_read(unsigned int *flags_p)
*flags_p = swsusp_header->flags;
if (IS_ERR(resume_bdev)) {
pr_debug("swsusp: block device not initialised\n");
pr_debug("PM: Image device not initialised\n");
return PTR_ERR(resume_bdev);
}
@@ -577,9 +578,9 @@ int swsusp_read(unsigned int *flags_p)
blkdev_put(resume_bdev);
if (!error)
pr_debug("swsusp: Reading resume file was successful\n");
pr_debug("PM: Image successfully loaded\n");
else
pr_debug("swsusp: Error %d resuming\n", error);
pr_debug("PM: Error %d resuming\n", error);
return error;
}
@@ -611,13 +612,13 @@ int swsusp_check(void)
if (error)
blkdev_put(resume_bdev);
else
pr_debug("swsusp: Signature found, resuming\n");
pr_debug("PM: Signature found, resuming\n");
} else {
error = PTR_ERR(resume_bdev);
}
if (error)
pr_debug("swsusp: Error %d check for resume file\n", error);
pr_debug("PM: Error %d checking image file\n", error);
return error;
}
@@ -629,7 +630,7 @@ int swsusp_check(void)
void swsusp_close(void)
{
if (IS_ERR(resume_bdev)) {
pr_debug("swsusp: block device not initialised\n");
pr_debug("PM: Image device not initialised\n");
return;
}
+3 -45
View File
@@ -64,14 +64,6 @@ unsigned long image_size = 500 * 1024 * 1024;
int in_suspend __nosavedata = 0;
#ifdef CONFIG_HIGHMEM
unsigned int count_highmem_pages(void);
int restore_highmem(void);
#else
static inline int restore_highmem(void) { return 0; }
static inline unsigned int count_highmem_pages(void) { return 0; }
#endif
/**
* The following functions are used for tracing the allocated
* swap pages, so that they can be freed in case of an error.
@@ -196,7 +188,8 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop,
centisecs = 1; /* avoid div-by-zero */
k = nr_pages * (PAGE_SIZE / 1024);
kps = (k * 100) / centisecs;
printk("%s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n", msg, k,
printk(KERN_INFO "PM: %s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n",
msg, k,
centisecs / 100, centisecs % 100,
kps / 1000, (kps % 1000) / 10);
}
@@ -227,7 +220,7 @@ int swsusp_shrink_memory(void)
char *p = "-\\|/";
struct timeval start, stop;
printk("Shrinking memory... ");
printk(KERN_INFO "PM: Shrinking memory... ");
do_gettimeofday(&start);
do {
long size, highmem_size;
@@ -269,38 +262,3 @@ int swsusp_shrink_memory(void)
return 0;
}
int swsusp_resume(void)
{
int error;
local_irq_disable();
/* NOTE: device_power_down() is just a suspend() with irqs off;
* it has no special "power things down" semantics
*/
if (device_power_down(PMSG_PRETHAW))
printk(KERN_ERR "Some devices failed to power down, very bad\n");
/* 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();
local_irq_enable();
return error;
}
+80 -29
View File
@@ -28,6 +28,29 @@
#include "power.h"
/*
* NOTE: The SNAPSHOT_SET_SWAP_FILE and SNAPSHOT_PMOPS ioctls are obsolete and
* will be removed in the future. They are only preserved here for
* compatibility with existing userland utilities.
*/
#define SNAPSHOT_SET_SWAP_FILE _IOW(SNAPSHOT_IOC_MAGIC, 10, unsigned int)
#define SNAPSHOT_PMOPS _IOW(SNAPSHOT_IOC_MAGIC, 12, unsigned int)
#define PMOPS_PREPARE 1
#define PMOPS_ENTER 2
#define PMOPS_FINISH 3
/*
* NOTE: The following ioctl definitions are wrong and have been replaced with
* correct ones. They are only preserved here for compatibility with existing
* userland utilities and will be removed in the future.
*/
#define SNAPSHOT_ATOMIC_SNAPSHOT _IOW(SNAPSHOT_IOC_MAGIC, 3, void *)
#define SNAPSHOT_SET_IMAGE_SIZE _IOW(SNAPSHOT_IOC_MAGIC, 6, unsigned long)
#define SNAPSHOT_AVAIL_SWAP _IOR(SNAPSHOT_IOC_MAGIC, 7, void *)
#define SNAPSHOT_GET_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 8, void *)
#define SNAPSHOT_MINOR 231
static struct snapshot_data {
@@ -36,7 +59,7 @@ static struct snapshot_data {
int mode;
char frozen;
char ready;
char platform_suspend;
char platform_support;
} snapshot_state;
atomic_t snapshot_device_available = ATOMIC_INIT(1);
@@ -44,6 +67,7 @@ atomic_t snapshot_device_available = ATOMIC_INIT(1);
static int snapshot_open(struct inode *inode, struct file *filp)
{
struct snapshot_data *data;
int error;
if (!atomic_add_unless(&snapshot_device_available, -1, 0))
return -EBUSY;
@@ -64,13 +88,23 @@ static int snapshot_open(struct inode *inode, struct file *filp)
data->swap = swsusp_resume_device ?
swap_type_of(swsusp_resume_device, 0, NULL) : -1;
data->mode = O_RDONLY;
error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
if (error)
pm_notifier_call_chain(PM_POST_RESTORE);
} else {
data->swap = -1;
data->mode = O_WRONLY;
error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
if (error)
pm_notifier_call_chain(PM_POST_HIBERNATION);
}
if (error) {
atomic_inc(&snapshot_device_available);
return error;
}
data->frozen = 0;
data->ready = 0;
data->platform_suspend = 0;
data->platform_support = 0;
return 0;
}
@@ -88,6 +122,8 @@ static int snapshot_release(struct inode *inode, struct file *filp)
thaw_processes();
mutex_unlock(&pm_mutex);
}
pm_notifier_call_chain(data->mode == O_WRONLY ?
PM_POST_HIBERNATION : PM_POST_RESTORE);
atomic_inc(&snapshot_device_available);
return 0;
}
@@ -133,7 +169,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
{
int error = 0;
struct snapshot_data *data;
loff_t avail;
loff_t size;
sector_t offset;
if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
@@ -151,18 +187,13 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
if (data->frozen)
break;
mutex_lock(&pm_mutex);
error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
if (!error) {
printk("Syncing filesystems ... ");
sys_sync();
printk("done.\n");
printk("Syncing filesystems ... ");
sys_sync();
printk("done.\n");
error = freeze_processes();
if (error)
thaw_processes();
}
error = freeze_processes();
if (error)
pm_notifier_call_chain(PM_POST_HIBERNATION);
thaw_processes();
mutex_unlock(&pm_mutex);
if (!error)
data->frozen = 1;
@@ -173,19 +204,19 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
break;
mutex_lock(&pm_mutex);
thaw_processes();
pm_notifier_call_chain(PM_POST_HIBERNATION);
mutex_unlock(&pm_mutex);
data->frozen = 0;
break;
case SNAPSHOT_CREATE_IMAGE:
case SNAPSHOT_ATOMIC_SNAPSHOT:
if (data->mode != O_RDONLY || !data->frozen || data->ready) {
error = -EPERM;
break;
}
error = hibernation_snapshot(data->platform_suspend);
error = hibernation_snapshot(data->platform_support);
if (!error)
error = put_user(in_suspend, (unsigned int __user *)arg);
error = put_user(in_suspend, (int __user *)arg);
if (!error)
data->ready = 1;
break;
@@ -197,7 +228,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
error = -EPERM;
break;
}
error = hibernation_restore(data->platform_suspend);
error = hibernation_restore(data->platform_support);
break;
case SNAPSHOT_FREE:
@@ -206,16 +237,29 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
data->ready = 0;
break;
case SNAPSHOT_PREF_IMAGE_SIZE:
case SNAPSHOT_SET_IMAGE_SIZE:
image_size = arg;
break;
case SNAPSHOT_AVAIL_SWAP:
avail = count_swap_pages(data->swap, 1);
avail <<= PAGE_SHIFT;
error = put_user(avail, (loff_t __user *)arg);
case SNAPSHOT_GET_IMAGE_SIZE:
if (!data->ready) {
error = -ENODATA;
break;
}
size = snapshot_get_image_size();
size <<= PAGE_SHIFT;
error = put_user(size, (loff_t __user *)arg);
break;
case SNAPSHOT_AVAIL_SWAP_SIZE:
case SNAPSHOT_AVAIL_SWAP:
size = count_swap_pages(data->swap, 1);
size <<= PAGE_SHIFT;
error = put_user(size, (loff_t __user *)arg);
break;
case SNAPSHOT_ALLOC_SWAP_PAGE:
case SNAPSHOT_GET_SWAP_PAGE:
if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
error = -ENODEV;
@@ -224,7 +268,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
offset = alloc_swapdev_block(data->swap);
if (offset) {
offset <<= PAGE_SHIFT;
error = put_user(offset, (sector_t __user *)arg);
error = put_user(offset, (loff_t __user *)arg);
} else {
error = -ENOSPC;
}
@@ -238,7 +282,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
free_all_swap_pages(data->swap);
break;
case SNAPSHOT_SET_SWAP_FILE:
case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */
if (!swsusp_swap_in_use()) {
/*
* User space encodes device types as two-byte values,
@@ -275,26 +319,33 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
mutex_unlock(&pm_mutex);
break;
case SNAPSHOT_PMOPS:
case SNAPSHOT_PLATFORM_SUPPORT:
data->platform_support = !!arg;
break;
case SNAPSHOT_POWER_OFF:
if (data->platform_support)
error = hibernation_platform_enter();
break;
case SNAPSHOT_PMOPS: /* This ioctl is deprecated */
error = -EINVAL;
switch (arg) {
case PMOPS_PREPARE:
data->platform_suspend = 1;
data->platform_support = 1;
error = 0;
break;
case PMOPS_ENTER:
if (data->platform_suspend)
if (data->platform_support)
error = hibernation_platform_enter();
break;
case PMOPS_FINISH:
if (data->platform_suspend)
if (data->platform_support)
error = 0;
break;
default: