summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@osdl.org>2003-09-07 20:13:39 -0700
committerPatrick Mochel <mochel@osdl.org>2003-09-07 20:13:39 -0700
commitb3b199111fbe794e72d1bd88208e10d3b39d6851 (patch)
tree8f69f5602fe89b54fd4a7cfef61c2acb7bb54dd0 /kernel
parent345741308f680bf496ef90c77687aff1c235dfb7 (diff)
parent73fb7306b1630cbc548363973127560c371930a7 (diff)
Merge osdl.org:/home/mochel/src/kernel/linux-2.5-virgin
into osdl.org:/home/mochel/src/kernel/linux-2.5-power
Diffstat (limited to 'kernel')
-rw-r--r--kernel/power/Makefile2
-rw-r--r--kernel/power/console.c2
-rw-r--r--kernel/power/disk.c337
-rw-r--r--kernel/power/main.c384
-rw-r--r--kernel/power/power.h39
-rw-r--r--kernel/power/swsusp.c341
-rw-r--r--kernel/sys.c2
7 files changed, 615 insertions, 492 deletions
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 9640751c4338..7f127b848827 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -1,4 +1,4 @@
obj-y := main.o process.o console.o pm.o
-obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
+obj-$(CONFIG_SOFTWARE_SUSPEND) += disk.o swsusp.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff --git a/kernel/power/console.c b/kernel/power/console.c
index 35b1f50d97de..c8a48236619b 100644
--- a/kernel/power/console.c
+++ b/kernel/power/console.c
@@ -8,7 +8,7 @@
#include <linux/kbd_kern.h>
#include "power.h"
-static int new_loglevel = 7;
+static int new_loglevel = 10;
static int orig_loglevel;
static int orig_fgconsole, orig_kmsg;
diff --git a/kernel/power/disk.c b/kernel/power/disk.c
new file mode 100644
index 000000000000..e7e40119d917
--- /dev/null
+++ b/kernel/power/disk.c
@@ -0,0 +1,337 @@
+/*
+ * kernel/power/disk.c - Suspend-to-disk support.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ *
+ * This file is release under the GPLv2
+ *
+ */
+
+#define DEBUG
+
+
+#include <linux/suspend.h>
+#include <linux/reboot.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include "power.h"
+
+
+extern u32 pm_disk_mode;
+extern struct pm_ops * pm_ops;
+
+extern int swsusp_save(void);
+extern int swsusp_write(void);
+extern int swsusp_read(void);
+extern int swsusp_restore(void);
+extern int swsusp_free(void);
+
+extern long sys_sync(void);
+
+
+/**
+ * power_down - Shut machine down for hibernate.
+ * @mode: Suspend-to-disk mode
+ *
+ * Use the platform driver, if configured so, and return gracefully if it
+ * fails.
+ * Otherwise, try to power off and reboot. If they fail, halt the machine,
+ * there ain't no turning back.
+ */
+
+static int power_down(u32 mode)
+{
+ unsigned long flags;
+ int error = 0;
+
+ local_irq_save(flags);
+ device_power_down(PM_SUSPEND_DISK);
+ switch(mode) {
+ case PM_DISK_PLATFORM:
+ error = pm_ops->enter(PM_SUSPEND_DISK);
+ break;
+ case PM_DISK_SHUTDOWN:
+ printk("Powering off system\n");
+ machine_power_off();
+ break;
+ case PM_DISK_REBOOT:
+ machine_restart(NULL);
+ break;
+ }
+ machine_halt();
+ device_power_up();
+ local_irq_restore(flags);
+ return 0;
+}
+
+
+static int in_suspend __nosavedata = 0;
+
+
+/**
+ * free_some_memory - Try to free as much memory as possible
+ *
+ * ... but do not OOM-kill anyone
+ *
+ * Notice: all userland should be stopped at this point, or
+ * livelock is possible.
+ */
+
+static void free_some_memory(void)
+{
+ printk("Freeing memory: ");
+ while (shrink_all_memory(10000))
+ printk(".");
+ printk("|\n");
+ blk_run_queues();
+}
+
+
+static inline void platform_finish(void)
+{
+ if (pm_disk_mode == PM_DISK_PLATFORM) {
+ if (pm_ops && pm_ops->finish)
+ pm_ops->finish(PM_SUSPEND_DISK);
+ }
+}
+
+static void finish(void)
+{
+ device_resume();
+ platform_finish();
+ thaw_processes();
+ pm_restore_console();
+}
+
+
+static int prepare(void)
+{
+ int error;
+
+ pm_prepare_console();
+
+ sys_sync();
+ if (freeze_processes()) {
+ error = -EBUSY;
+ goto Thaw;
+ }
+
+ if (pm_disk_mode == PM_DISK_PLATFORM) {
+ if (pm_ops && pm_ops->prepare) {
+ if ((error = pm_ops->prepare(PM_SUSPEND_DISK)))
+ goto Thaw;
+ }
+ }
+
+ /* Free memory before shutting down devices. */
+ free_some_memory();
+
+ if ((error = device_suspend(PM_SUSPEND_DISK)))
+ goto Finish;
+
+ return 0;
+ Finish:
+ platform_finish();
+ Thaw:
+ thaw_processes();
+ pm_restore_console();
+ return error;
+}
+
+
+/**
+ * pm_suspend_disk - The granpappy of power management.
+ *
+ * If we're going through the firmware, then get it over with quickly.
+ *
+ * If not, then call swsusp to do it's thing, then figure out how
+ * to power down the system.
+ */
+
+int pm_suspend_disk(void)
+{
+ int error;
+
+ if ((error = prepare()))
+ return error;
+
+ pr_debug("PM: Attempting to suspend to disk.\n");
+ if (pm_disk_mode == PM_DISK_FIRMWARE)
+ return pm_ops->enter(PM_SUSPEND_DISK);
+
+ pr_debug("PM: snapshotting memory.\n");
+ in_suspend = 1;
+ local_irq_disable();
+ if ((error = swsusp_save()))
+ goto Done;
+
+ pr_debug("PM: writing image.\n");
+
+ /*
+ * FIXME: Leftover from swsusp. Are they necessary?
+ */
+ mb();
+ barrier();
+
+ error = swsusp_write();
+ if (!error && in_suspend) {
+ error = power_down(pm_disk_mode);
+ pr_debug("PM: Power down failed.\n");
+ } else
+ pr_debug("PM: Image restored successfully.\n");
+ swsusp_free();
+ Done:
+ local_irq_enable();
+ finish();
+ return error;
+}
+
+
+/**
+ * pm_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 pm_suspend_disk() ) if everything goes well.
+ * Otherwise, we fail gracefully and return to the normally
+ * scheduled program.
+ *
+ */
+
+static int pm_resume(void)
+{
+ int error;
+
+ pr_debug("PM: Reading swsusp image.\n");
+
+ if ((error = swsusp_read()))
+ goto Done;
+
+ pr_debug("PM: Preparing system for restore.\n");
+
+ if ((error = prepare()))
+ goto Free;
+
+ barrier();
+ mb();
+ local_irq_disable();
+
+ /* FIXME: The following (comment and mdelay()) are from swsusp.
+ * Are they really necessary?
+ *
+ * We do not want some readahead with DMA to corrupt our memory, right?
+ * Do it with disabled interrupts for best effect. That way, if some
+ * driver scheduled DMA, we have good chance for DMA to finish ;-).
+ */
+ pr_debug("PM: Waiting for DMAs to settle down.\n");
+ mdelay(1000);
+
+ pr_debug("PM: Restoring saved image.\n");
+ swsusp_restore();
+ local_irq_enable();
+ pr_debug("PM: Restore failed, recovering.n");
+ finish();
+ Free:
+ swsusp_free();
+ Done:
+ pr_debug("PM: Resume from disk failed.\n");
+ return 0;
+}
+
+late_initcall(pm_resume);
+
+
+static char * pm_disk_modes[] = {
+ [PM_DISK_FIRMWARE] = "firmware",
+ [PM_DISK_PLATFORM] = "platform",
+ [PM_DISK_SHUTDOWN] = "shutdown",
+ [PM_DISK_REBOOT] = "reboot",
+};
+
+/**
+ * disk - Control suspend-to-disk mode
+ *
+ * Suspend-to-disk can be handled in several ways. The greatest
+ * distinction is who writes memory to disk - the firmware or the OS.
+ * If the firmware does it, we assume that it also handles suspending
+ * the system.
+ * If the OS does it, then we have three options for putting the system
+ * to sleep - using the platform driver (e.g. ACPI or other PM registers),
+ * powering off the system or rebooting the system (for testing).
+ *
+ * The system will support either 'firmware' or 'platform', and that is
+ * known a priori (and encoded in pm_ops). But, the user may choose
+ * 'shutdown' or 'reboot' as alternatives.
+ *
+ * show() will display what the mode is currently set to.
+ * store() will accept one of
+ *
+ * 'firmware'
+ * 'platform'
+ * 'shutdown'
+ * 'reboot'
+ *
+ * It will only change to 'firmware' or 'platform' if the system
+ * supports it (as determined from pm_ops->pm_disk_mode).
+ */
+
+static ssize_t disk_show(struct subsystem * subsys, char * buf)
+{
+ return sprintf(buf,"%s\n",pm_disk_modes[pm_disk_mode]);
+}
+
+
+static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n)
+{
+ int error = 0;
+ int i;
+ u32 mode = 0;
+
+ down(&pm_sem);
+ for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) {
+ if (!strcmp(buf,pm_disk_modes[i])) {
+ mode = i;
+ break;
+ }
+ }
+ if (mode) {
+ if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT)
+ pm_disk_mode = mode;
+ else {
+ if (pm_ops && pm_ops->enter &&
+ (mode == pm_ops->pm_disk_mode))
+ pm_disk_mode = mode;
+ else
+ error = -EINVAL;
+ }
+ } else
+ error = -EINVAL;
+
+ pr_debug("PM: suspend-to-disk mode set to '%s'\n",
+ pm_disk_modes[mode]);
+ up(&pm_sem);
+ return error ? error : n;
+}
+
+power_attr(disk);
+
+static struct attribute * g[] = {
+ &disk_attr.attr,
+ NULL,
+};
+
+
+static struct attribute_group attr_group = {
+ .attrs = g,
+};
+
+
+static int __init pm_disk_init(void)
+{
+ return sysfs_create_group(&power_subsys.kset.kobj,&attr_group);
+}
+
+core_initcall(pm_disk_init);
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 1b92f13d9a77..be4473cbf300 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -8,32 +8,23 @@
*
*/
+#define DEBUG
+
#include <linux/suspend.h>
#include <linux/kobject.h>
-#include <linux/reboot.h>
#include <linux/string.h>
+#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/pm.h>
-#include <linux/fs.h>
#include "power.h"
-static DECLARE_MUTEX(pm_sem);
-
-static struct pm_ops * pm_ops = NULL;
-
-static u32 pm_disk_mode = PM_DISK_SHUTDOWN;
-
-#ifdef CONFIG_SOFTWARE_SUSPEND
-static int have_swsusp = 1;
-#else
-static int have_swsusp = 0;
-#endif
-
-extern long sys_sync(void);
+DECLARE_MUTEX(pm_sem);
+struct pm_ops * pm_ops = NULL;
+u32 pm_disk_mode = PM_DISK_SHUTDOWN;
/**
* pm_set_ops - Set the global power method table.
@@ -51,171 +42,6 @@ void pm_set_ops(struct pm_ops * ops)
/**
- * pm_suspend_standby - Enter 'standby' state.
- *
- * 'standby' is also known as 'Power-On Suspend'. Here, we power down
- * devices, disable interrupts, and enter the state.
- */
-
-static int pm_suspend_standby(void)
-{
- int error = 0;
- unsigned long flags;
-
- if (!pm_ops || !pm_ops->enter)
- return -EPERM;
-
- local_irq_save(flags);
- if ((error = device_power_down(PM_SUSPEND_STANDBY)))
- goto Done;
- error = pm_ops->enter(PM_SUSPEND_STANDBY);
- local_irq_restore(flags);
- device_power_up();
- Done:
- return error;
-}
-
-
-/**
- * pm_suspend_mem - Enter suspend-to-RAM state.
- *
- * Identical to pm_suspend_standby() - we power down devices, disable
- * interrupts, and enter the low-power state.
- */
-
-static int pm_suspend_mem(void)
-{
- int error = 0;
- unsigned long flags;
-
- if (!pm_ops || !pm_ops->enter)
- return -EPERM;
-
- local_irq_save(flags);
- if ((error = device_power_down(PM_SUSPEND_STANDBY)))
- goto Done;
- error = pm_ops->enter(PM_SUSPEND_STANDBY);
- local_irq_restore(flags);
- device_power_up();
- Done:
- return error;
-}
-
-
-/**
- * power_down - Shut machine down for hibernate.
- * @mode: Suspend-to-disk mode
- *
- * Use the platform driver, if configured so, and return gracefully if it
- * fails.
- * Otherwise, try to power off and reboot. If they fail, halt the machine,
- * there ain't no turning back.
- */
-
-static int power_down(u32 mode)
-{
- unsigned long flags;
- int error = 0;
-
- local_irq_save(flags);
- device_power_down(PM_SUSPEND_DISK);
- switch(mode) {
- case PM_DISK_PLATFORM:
- error = pm_ops->enter(PM_SUSPEND_DISK);
- if (error) {
- device_power_up();
- local_irq_restore(flags);
- return error;
- }
- case PM_DISK_SHUTDOWN:
- machine_power_off();
- break;
- case PM_DISK_REBOOT:
- machine_restart(NULL);
- break;
- }
- machine_halt();
- return 0;
-}
-
-
-static int in_suspend __nosavedata = 0;
-
-
-/**
- * free_some_memory - Try to free as much memory as possible
- *
- * ... but do not OOM-kill anyone
- *
- * Notice: all userland should be stopped at this point, or
- * livelock is possible.
- */
-
-static void free_some_memory(void)
-{
- printk("Freeing memory: ");
- while (shrink_all_memory(10000))
- printk(".");
- printk("|\n");
- blk_run_queues();
-}
-
-
-/**
- * pm_suspend_disk - The granpappy of power management.
- *
- * If we're going through the firmware, then get it over with quickly.
- *
- * If not, then call swsusp to do it's thing, then figure out how
- * to power down the system.
- */
-
-static int pm_suspend_disk(void)
-{
- int error;
-
- pr_debug("PM: Attempting to suspend to disk.\n");
- if (pm_disk_mode == PM_DISK_FIRMWARE)
- return pm_ops->enter(PM_SUSPEND_DISK);
-
- if (!have_swsusp)
- return -EPERM;
-
- pr_debug("PM: snapshotting memory.\n");
- in_suspend = 1;
- if ((error = swsusp_save()))
- goto Done;
-
- if (in_suspend) {
- pr_debug("PM: writing image.\n");
- error = swsusp_write();
- if (!error)
- error = power_down(pm_disk_mode);
- pr_debug("PM: Power down failed.\n");
- } else
- pr_debug("PM: Image restored successfully.\n");
- swsusp_free();
- Done:
- return error;
-}
-
-
-
-#define decl_state(_name) \
- { .name = __stringify(_name), .fn = pm_suspend_##_name }
-
-struct pm_state {
- char * name;
- int (*fn)(void);
-} pm_states[] = {
- [PM_SUSPEND_STANDBY] = decl_state(standby),
- [PM_SUSPEND_MEM] = decl_state(mem),
- [PM_SUSPEND_DISK] = decl_state(disk),
- { NULL },
-};
-
-
-/**
* suspend_prepare - Do prep work before entering low-power state.
* @state: State we're entering.
*
@@ -228,23 +54,21 @@ static int suspend_prepare(u32 state)
{
int error = 0;
+ if (!pm_ops || !pm_ops->enter)
+ return -EPERM;
+
pm_prepare_console();
- sys_sync();
if (freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
- if (pm_ops && pm_ops->prepare) {
+ if (pm_ops->prepare) {
if ((error = pm_ops->prepare(state)))
goto Thaw;
}
- /* Free memory before shutting down devices. */
- if (state == PM_SUSPEND_DISK)
- free_some_memory();
-
if ((error = device_suspend(state)))
goto Finish;
@@ -253,7 +77,7 @@ static int suspend_prepare(u32 state)
pm_restore_console();
return error;
Finish:
- if (pm_ops && pm_ops->finish)
+ if (pm_ops->finish)
pm_ops->finish(state);
Thaw:
thaw_processes();
@@ -261,6 +85,22 @@ static int suspend_prepare(u32 state)
}
+static int suspend_enter(u32 state)
+{
+ int error = 0;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if ((error = device_power_down(state)))
+ goto Done;
+ error = pm_ops->enter(state);
+ local_irq_restore(flags);
+ device_power_up();
+ Done:
+ return error;
+}
+
+
/**
* suspend_finish - Do final work before exiting suspend sequence.
* @state: State we're coming out of.
@@ -279,6 +119,16 @@ static void suspend_finish(u32 state)
}
+
+
+char * pm_states[] = {
+ [PM_SUSPEND_STANDBY] = "standby",
+ [PM_SUSPEND_MEM] = "mem",
+ [PM_SUSPEND_DISK] = "disk",
+ NULL,
+};
+
+
/**
* enter_state - Do common work of entering low-power state.
* @state: pm_state structure for state we're entering.
@@ -293,7 +143,6 @@ static void suspend_finish(u32 state)
static int enter_state(u32 state)
{
int error;
- struct pm_state * s = &pm_states[state];
if (down_trylock(&pm_sem))
return -EBUSY;
@@ -304,12 +153,17 @@ static int enter_state(u32 state)
goto Unlock;
}
- pr_debug("PM: Preparing system for suspend.\n");
+ if (state == PM_SUSPEND_DISK) {
+ error = pm_suspend_disk();
+ goto Unlock;
+ }
+
+ pr_debug("PM: Preparing system for suspend\n");
if ((error = suspend_prepare(state)))
goto Unlock;
pr_debug("PM: Entering state.\n");
- error = s->fn();
+ error = suspend_enter(state);
pr_debug("PM: Finishing up.\n");
suspend_finish(state);
@@ -335,138 +189,10 @@ int pm_suspend(u32 state)
}
-/**
- * pm_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 pm_suspend_disk() ) if everything goes well.
- * Otherwise, we fail gracefully and return to the normally
- * scheduled program.
- *
- */
-
-static int pm_resume(void)
-{
- int error;
-
- if (!have_swsusp)
- return 0;
-
- pr_debug("PM: Reading swsusp image.\n");
-
- if ((error = swsusp_read()))
- goto Done;
-
- pr_debug("PM: Preparing system for restore.\n");
-
- if ((error = suspend_prepare(PM_SUSPEND_DISK)))
- goto Free;
-
- pr_debug("PM: Restoring saved image.\n");
- swsusp_restore();
-
- pr_debug("PM: Restore failed, recovering.n");
- suspend_finish(PM_SUSPEND_DISK);
- Free:
- swsusp_free();
- Done:
- pr_debug("PM: Resume from disk failed.\n");
- return 0;
-}
-
-late_initcall(pm_resume);
-
decl_subsys(power,NULL,NULL);
-#define power_attr(_name) \
-static struct subsys_attribute _name##_attr = { \
- .attr = { \
- .name = __stringify(_name), \
- .mode = 0644, \
- }, \
- .show = _name##_show, \
- .store = _name##_store, \
-}
-
-
-static char * pm_disk_modes[] = {
- [PM_DISK_FIRMWARE] = "firmware",
- [PM_DISK_PLATFORM] = "platform",
- [PM_DISK_SHUTDOWN] = "shutdown",
- [PM_DISK_REBOOT] = "reboot",
-};
-
-/**
- * disk - Control suspend-to-disk mode
- *
- * Suspend-to-disk can be handled in several ways. The greatest
- * distinction is who writes memory to disk - the firmware or the OS.
- * If the firmware does it, we assume that it also handles suspending
- * the system.
- * If the OS does it, then we have three options for putting the system
- * to sleep - using the platform driver (e.g. ACPI or other PM registers),
- * powering off the system or rebooting the system (for testing).
- *
- * The system will support either 'firmware' or 'platform', and that is
- * known a priori (and encoded in pm_ops). But, the user may choose
- * 'shutdown' or 'reboot' as alternatives.
- *
- * show() will display what the mode is currently set to.
- * store() will accept one of
- *
- * 'firmware'
- * 'platform'
- * 'shutdown'
- * 'reboot'
- *
- * It will only change to 'firmware' or 'platform' if the system
- * supports it (as determined from pm_ops->pm_disk_mode).
- */
-
-static ssize_t disk_show(struct subsystem * subsys, char * buf)
-{
- return sprintf(buf,"%s\n",pm_disk_modes[pm_disk_mode]);
-}
-
-
-static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n)
-{
- int error = 0;
- int i;
- u32 mode = 0;
-
- down(&pm_sem);
- for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) {
- if (!strcmp(buf,pm_disk_modes[i])) {
- mode = i;
- break;
- }
- }
- if (mode) {
- if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT)
- pm_disk_mode = mode;
- else {
- if (pm_ops && pm_ops->enter &&
- (mode == pm_ops->pm_disk_mode))
- pm_disk_mode = mode;
- else
- error = -EINVAL;
- }
- } else
- error = -EINVAL;
-
- pr_debug("PM: suspend-to-disk mode set to '%s'\n",
- pm_disk_modes[mode]);
- up(&pm_sem);
- return error ? error : n;
-}
-
-power_attr(disk);
-
/**
* state - control system power state.
*
@@ -480,27 +206,28 @@ power_attr(disk);
static ssize_t state_show(struct subsystem * subsys, char * buf)
{
- struct pm_state * state;
+ int i;
char * s = buf;
- for (state = &pm_states[0]; state->name; state++)
- s += sprintf(s,"%s ",state->name);
+ for (i = 0; i < PM_SUSPEND_MAX; i++) {
+ if (pm_states[i])
+ s += sprintf(s,"%s ",pm_states[i]);
+ }
s += sprintf(s,"\n");
return (s - buf);
}
static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n)
{
- u32 state;
- struct pm_state * s;
+ u32 state = PM_SUSPEND_STANDBY;
+ char ** s;
int error;
- for (state = 0; state < PM_SUSPEND_MAX; state++) {
- s = &pm_states[state];
- if (s->name && !strcmp(buf,s->name))
+ for (s = &pm_states[state]; *s; s++, state++) {
+ if (!strcmp(buf,*s))
break;
}
- if (s)
+ if (*s)
error = enter_state(state);
else
error = -EINVAL;
@@ -511,7 +238,6 @@ power_attr(state);
static struct attribute * g[] = {
&state_attr.attr,
- &disk_attr.attr,
NULL,
};
@@ -520,7 +246,7 @@ static struct attribute_group attr_group = {
};
-static int pm_init(void)
+static int __init pm_init(void)
{
int error = subsystem_register(&power_subsys);
if (!error)
diff --git a/kernel/power/power.h b/kernel/power/power.h
index e98de640155d..e0874ed266f5 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -10,34 +10,27 @@
#ifdef CONFIG_SOFTWARE_SUSPEND
-extern int swsusp_save(void);
-extern int swsusp_write(void);
-extern int swsusp_read(void);
-extern int swsusp_restore(void);
-extern int swsusp_free(void);
+extern int pm_suspend_disk(void);
+
#else
-static inline int swsusp_save(void)
-{
- return 0;
-}
-static inline int swsusp_write(void)
+static inline int pm_suspend_disk(void)
{
- return 0;
-}
-static inline int swsusp_read(void)
-{
- return 0;
-}
-static inline int swsusp_restore(void)
-{
- return 0;
-}
-static inline int swsusp_free(void)
-{
- return 0;
+ return -EPERM;
}
#endif
+extern struct semaphore pm_sem;
+#define power_attr(_name) \
+static struct subsys_attribute _name##_attr = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = 0644, \
+ }, \
+ .show = _name##_show, \
+ .store = _name##_store, \
+}
+
+extern struct subsystem power_subsys;
extern int freeze_processes(void);
extern void thaw_processes(void);
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c
index c849a18c45f4..e9a1e97a2f2b 100644
--- a/kernel/power/swsusp.c
+++ b/kernel/power/swsusp.c
@@ -34,38 +34,21 @@
* For TODOs,FIXMEs also look in Documentation/swsusp.txt
*/
-#include <linux/module.h>
#include <linux/mm.h>
+#include <linux/bio.h>
#include <linux/suspend.h>
-#include <linux/smp_lock.h>
-#include <linux/file.h>
-#include <linux/utsname.h>
#include <linux/version.h>
-#include <linux/delay.h>
#include <linux/reboot.h>
-#include <linux/bitops.h>
-#include <linux/vt_kern.h>
-#include <linux/kbd_kern.h>
-#include <linux/keyboard.h>
-#include <linux/spinlock.h>
-#include <linux/genhd.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/swap.h>
-#include <linux/pm.h>
#include <linux/device.h>
-#include <linux/buffer_head.h>
#include <linux/swapops.h>
#include <linux/bootmem.h>
-#include <asm/uaccess.h>
#include <asm/mmu_context.h>
-#include <asm/pgtable.h>
-#include <asm/io.h>
#include "power.h"
-unsigned char software_suspend_enabled = 1;
+
+extern int swsusp_arch_suspend(int resume);
#define __ADDRESS(x) ((unsigned long) phys_to_virt(x))
#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT)
@@ -76,9 +59,6 @@ extern char __nosave_begin, __nosave_end;
extern int is_head_of_free_region(struct page *);
-/* Locks */
-spinlock_t suspend_pagedir_lock __nosavedata = SPIN_LOCK_UNLOCKED;
-
/* Variables to be preserved over suspend */
static int pagedir_order_check;
static int nr_copy_pages_check;
@@ -488,21 +468,30 @@ static int suspend_prepare_image(void)
return 0;
}
+
+/**
+ * suspend_save_image - Prepare and write saved image to swap.
+ *
+ * IRQs are re-enabled here so we can resume devices and safely write
+ * to the swap devices. We disable them again before we leave.
+ *
+ * The second lock_swapdevices() will unlock ignored swap devices since
+ * writing is finished.
+ * It is important _NOT_ to umount filesystems at this point. We want
+ * them synced (in case something goes wrong) but we DO not want to mark
+ * filesystem clean: it is not. (And it does not matter, if we resume
+ * correctly, we'll mark system clean, anyway.)
+ */
+
static int suspend_save_image(void)
{
int error;
-
+ local_irq_enable();
device_resume();
-
lock_swapdevices();
error = write_suspend_image();
- lock_swapdevices(); /* This will unlock ignored swap devices since writing is finished */
-
- /* It is important _NOT_ to umount filesystems at this point. We want
- * them synced (in case something goes wrong) but we DO not want to mark
- * filesystem clean: it is not. (And it does not matter, if we resume
- * correctly, we'll mark system clean, anyway.)
- */
+ lock_swapdevices();
+ local_irq_disable();
return error;
}
@@ -510,66 +499,49 @@ static int suspend_save_image(void)
* Magic happens here
*/
-void do_magic_resume_1(void)
-{
- barrier();
- mb();
- spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */
- PRINTK( "Waiting for DMAs to settle down...\n");
- /* We do not want some readahead with DMA to corrupt our memory, right?
- Do it with disabled interrupts for best effect. That way, if some
- driver scheduled DMA, we have good chance for DMA to finish ;-). */
- mdelay(1000);
-}
-
-void do_magic_resume_2(void)
+int swsusp_resume(void)
{
BUG_ON (nr_copy_pages_check != nr_copy_pages);
BUG_ON (pagedir_order_check != pagedir_order);
/* Even mappings of "global" things (vmalloc) need to be fixed */
__flush_tlb_global();
- spin_unlock_irq(&suspend_pagedir_lock);
+ return 0;
}
-/* do_magic() is implemented in arch/?/kernel/suspend_asm.S, and basically does:
+/* swsusp_arch_suspend() is implemented in arch/?/power/swsusp.S,
+ and basically does:
if (!resume) {
- do_magic_suspend_1();
save_processor_state();
SAVE_REGISTERS
- do_magic_suspend_2();
+ swsusp_suspend();
return;
}
GO_TO_SWAPPER_PAGE_TABLES
- do_magic_resume_1();
COPY_PAGES_BACK
RESTORE_REGISTERS
restore_processor_state();
- do_magic_resume_2();
+ swsusp_resume();
*/
-void do_magic_suspend_1(void)
-{
- mb();
- barrier();
- spin_lock_irq(&suspend_pagedir_lock);
-}
-int do_magic_suspend_2(void)
+int swsusp_suspend(void)
{
- int is_problem;
+ int error;
read_swapfiles();
- is_problem = suspend_prepare_image();
- spin_unlock_irq(&suspend_pagedir_lock);
- if (!is_problem)
- return suspend_save_image();
- printk(KERN_EMERG "%sSuspend failed, trying to recover...\n", name_suspend);
- barrier();
- mb();
- mdelay(1000);
- return -EFAULT;
+ error = suspend_prepare_image();
+ if (!error)
+ error = suspend_save_image();
+ if (error) {
+ printk(KERN_EMERG "%sSuspend failed, trying to recover...\n",
+ name_suspend);
+ barrier();
+ mb();
+ mdelay(1000);
+ }
+ return error;
}
/* More restore stuff */
@@ -701,61 +673,146 @@ static int __init sanity_check(struct suspend_header *sh)
return 0;
}
-static int __init bdev_read_page(struct block_device *bdev,
- long pos, void *buf)
+static struct block_device * resume_bdev;
+
+
+/**
+ * Using bio to read from swap.
+ * This code requires a bit more work than just using buffer heads
+ * but, it is the recommended way for 2.5/2.6.
+ * The following are to signal the beginning and end of I/O. Bios
+ * finish asynchronously, while we want them to happen synchronously.
+ * A simple atomic_t, and a wait loop take care of this problem.
+ */
+
+static atomic_t io_done = ATOMIC_INIT(0);
+
+static void start_io(void)
{
- struct buffer_head *bh;
- BUG_ON (pos%PAGE_SIZE);
- bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
- if (!bh || (!bh->b_data)) {
- return -1;
- }
- memcpy(buf, bh->b_data, PAGE_SIZE); /* FIXME: may need kmap() */
- BUG_ON(!buffer_uptodate(bh));
- brelse(bh);
+ atomic_set(&io_done,1);
+}
+
+static int end_io(struct bio * bio, unsigned int num, int err)
+{
+ atomic_set(&io_done,0);
return 0;
-}
+}
+
+static void wait_io(void)
+{
+ blk_run_queues();
+ while(atomic_read(&io_done))
+ io_schedule();
+}
+
+
+/**
+ * submit - submit BIO request.
+ * @rw: READ or WRITE.
+ * @off physical offset of page.
+ * @page: page we're reading or writing.
+ *
+ * Straight from the textbook - allocate and initialize the bio.
+ * If we're writing, make sure the page is marked as dirty.
+ * Then submit it and wait.
+ */
+
+static int submit(int rw, pgoff_t page_off, void * page)
+{
+ int error = 0;
+ struct bio * bio;
+
+ bio = bio_alloc(GFP_ATOMIC,1);
+ if (!bio)
+ return -ENOMEM;
+ bio->bi_sector = page_off * (PAGE_SIZE >> 9);
+ bio_get(bio);
+ bio->bi_bdev = resume_bdev;
+ bio->bi_end_io = end_io;
+
+ if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) {
+ printk("ERROR: adding page to bio at %ld\n",page_off);
+ error = -EFAULT;
+ goto Done;
+ }
+
+ if (rw == WRITE)
+ bio_set_pages_dirty(bio);
+ start_io();
+ submit_bio(rw,bio);
+ wait_io();
+ Done:
+ bio_put(bio);
+ return error;
+}
+
+static int
+read_page(pgoff_t page_off, void * page)
+{
+ return submit(READ,page_off,page);
+}
+
+static int
+write_page(pgoff_t page_off, void * page)
+{
+ return submit(WRITE,page_off,page);
+}
+
extern dev_t __init name_to_dev_t(const char *line);
-static int __init read_suspend_image(struct block_device *bdev,
- union diskpage *cur)
+
+#define next_entry(diskpage) diskpage->link.next
+
+static int __init read_suspend_image(void)
{
swp_entry_t next;
int i, nr_pgdir_pages;
+ union diskpage *cur;
+ int error = 0;
-#define PREPARENEXT \
- { next = cur->link.next; \
- next.val = swp_offset(next) * PAGE_SIZE; \
- }
-
- if (bdev_read_page(bdev, 0, cur)) return -EIO;
+ cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
+ if (!cur)
+ return -ENOMEM;
- if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) ||
- (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) {
- printk(KERN_ERR "%sThis is normal swap space\n", name_resume );
- return -EINVAL;
- }
+ if ((error = read_page(0, cur)))
+ goto Done;
- PREPARENEXT; /* We have to read next position before we overwrite it */
+ /*
+ * We have to read next position before we overwrite it
+ */
+ next = next_entry(cur);
if (!memcmp("S1",cur->swh.magic.magic,2))
memcpy(cur->swh.magic.magic,"SWAP-SPACE",10);
else if (!memcmp("S2",cur->swh.magic.magic,2))
memcpy(cur->swh.magic.magic,"SWAPSPACE2",10);
- else {
- printk("swsusp: %s: Unable to find suspended-data signature (%.10s - misspelled?\n",
- name_resume, cur->swh.magic.magic);
- return -EFAULT;
+ else if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) ||
+ (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) {
+ printk(KERN_ERR "swsusp: Partition is normal swap space\n");
+ error = -EINVAL;
+ goto Done;
+ } else {
+ printk(KERN_ERR "swsusp: Invalid partition type.\n");
+ error = -EINVAL;
+ goto Done;
}
+ /*
+ * Reset swap signature now.
+ */
+ if ((error = write_page(0,cur)))
+ goto Done;
+
printk( "%sSignature found, resuming\n", name_resume );
MDELAY(1000);
- if (bdev_read_page(bdev, next.val, cur)) return -EIO;
- if (sanity_check(&cur->sh)) /* Is this same machine? */
- return -EPERM;
- PREPARENEXT;
+ if ((error = read_page(swp_offset(next), cur)))
+ goto Done;
+ /* Is this same machine? */
+ if ((error = sanity_check(&cur->sh)))
+ goto Done;
+ next = next_entry(cur);
pagedir_save = cur->sh.suspend_pagedir;
nr_copy_pages = cur->sh.num_pbes;
@@ -763,8 +820,10 @@ static int __init read_suspend_image(struct block_device *bdev,
pagedir_order = get_bitmask_order(nr_pgdir_pages);
pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order);
- if (!pagedir_nosave)
- return -ENOMEM;
+ if (!pagedir_nosave) {
+ error = -ENOMEM;
+ goto Done;
+ }
PRINTK( "%sReading pagedir, ", name_resume );
@@ -772,15 +831,17 @@ static int __init read_suspend_image(struct block_device *bdev,
for (i=nr_pgdir_pages-1; i>=0; i--) {
BUG_ON (!next.val);
cur = (union diskpage *)((char *) pagedir_nosave)+i;
- if (bdev_read_page(bdev, next.val, cur)) return -EIO;
- PREPARENEXT;
+ error = read_page(swp_offset(next), cur);
+ if (error)
+ goto FreePagedir;
+ next = next_entry(cur);
}
BUG_ON (next.val);
- if (relocate_pagedir())
- return -ENOMEM;
- if (check_pagedir())
- return -ENOMEM;
+ if ((error = relocate_pagedir()))
+ goto FreePagedir;
+ if ((error = check_pagedir()))
+ goto FreePagedir;
printk( "Reading image data (%d pages): ", nr_copy_pages );
for(i=0; i < nr_copy_pages; i++) {
@@ -789,11 +850,18 @@ static int __init read_suspend_image(struct block_device *bdev,
printk( "." );
/* You do not need to check for overlaps...
... check_pagedir already did this work */
- if (bdev_read_page(bdev, swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address)))
- return -EIO;
+ error = read_page(swp_offset(swap_address),
+ (char *)((pagedir_nosave+i)->address));
+ if (error)
+ goto FreePagedir;
}
printk( "|\n" );
- return 0;
+ Done:
+ free_page((unsigned long)cur);
+ return error;
+ FreePagedir:
+ free_pages((unsigned long)pagedir_nosave,pagedir_order);
+ goto Done;
}
/**
@@ -806,24 +874,23 @@ int swsusp_save(void)
printk("swsusp is not supported with high- or discontig-mem.\n");
return -EPERM;
#endif
- return 0;
+ return arch_prepare_suspend();
}
/**
* swsusp_write - Write saved memory image to swap.
*
- * do_magic(0) returns after system is resumed.
+ * swsusp_arch_suspend(0) returns after system is resumed.
*
- * do_magic() copies all "used" memory to "free" memory, then
- * unsuspends all device drivers, and writes memory to disk
+ * swsusp_arch_suspend() copies all "used" memory to "free" memory,
+ * then unsuspends all device drivers, and writes memory to disk
* using normal kernel mechanism.
*/
int swsusp_write(void)
{
- arch_prepare_suspend();
- return do_magic(0);
+ return swsusp_arch_suspend(0);
}
@@ -833,7 +900,6 @@ int swsusp_write(void)
int __init swsusp_read(void)
{
- union diskpage *cur;
int error;
char b[BDEVNAME_SIZE];
@@ -844,19 +910,13 @@ int __init swsusp_read(void)
printk("swsusp: Resume From Partition: %s, Device: %s\n",
resume_file, __bdevname(resume_device, b));
- cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
- if (cur) {
- struct block_device *bdev;
- bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW);
- if (!IS_ERR(bdev)) {
- set_blocksize(bdev, PAGE_SIZE);
- error = read_suspend_image(bdev, cur);
- blkdev_put(bdev, BDEV_RAW);
- } else
- error = PTR_ERR(bdev);
- free_page((unsigned long)cur);
+ resume_bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW);
+ if (!IS_ERR(resume_bdev)) {
+ set_blocksize(resume_bdev, PAGE_SIZE);
+ error = read_suspend_image();
+ blkdev_put(resume_bdev, BDEV_RAW);
} else
- error = -ENOMEM;
+ error = PTR_ERR(resume_bdev);
if (!error)
PRINTK("Reading resume file was successful\n");
@@ -873,7 +933,7 @@ int __init swsusp_read(void)
int __init swsusp_restore(void)
{
- return do_magic(1);
+ return swsusp_arch_suspend(1);
}
@@ -885,13 +945,20 @@ int swsusp_free(void)
{
PRINTK( "Freeing prev allocated pagedir\n" );
free_suspend_pagedir((unsigned long) pagedir_save);
-
- PRINTK( "Fixing swap signatures... " );
- mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
- PRINTK( "ok\n" );
return 0;
}
+
+int software_suspend(void)
+{
+ struct pm_ops swsusp_ops = {
+ .pm_disk_mode = PM_DISK_SHUTDOWN,
+ };
+
+ pm_set_ops(&swsusp_ops);
+ return pm_suspend(PM_SUSPEND_DISK);
+}
+
static int __init resume_setup(char *str)
{
if (strlen(str))
diff --git a/kernel/sys.c b/kernel/sys.c
index 02b5a12dfd59..d77453173d29 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -456,7 +456,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user
#ifdef CONFIG_SOFTWARE_SUSPEND
case LINUX_REBOOT_CMD_SW_SUSPEND:
- if (!pm_suspend(PM_SUSPEND_DISK))
+ if (!software_suspend())
break;
do_exit(0);
break;