[PATCH] swsusp: do not use pm_ops (was: Re: suspend2 merge (was: Re: CFS and suspend2: hang in atomic copy))

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[Trimmed the CC list to a reasonable minimum]

On Thursday, 26 April 2007 18:31, Johannes Berg wrote:
> On Thu, 2007-04-26 at 13:30 +0200, Pavel Machek wrote:
> 
> > > From looking at pm_ops which I was recently working with a lot, it seems
> > > that it was designed by somebody who was reading the ACPI documentation
> > > and was otherwise pretty clueless, even at that level std tries to look
> > > like suspend. IMHO that is one of the first things that should be ripped
> > > out, no pm_ops for STD, it's a pain to work with.
> > 
> > That code goes back to Patrick, AFAICT. (And yes, ACPI S3 and ACPI S4
> > low-level enter is pretty similar).
> > 
> > Patches would be welcome
> 
> That was easier than I thought. This applies on top of a patch that
> makes kernel/power/user.c optional since I had no idea how to fix it,
> problems I see:
>  * it surfaces kernel implementation details about pm_ops and thus makes
>    the whole thing very fragile
>  * it has yet another interface (yuck) to determine whether to reboot,
>    shut down etc, doesn't use /sys/power/disk
>  * I generally had no idea wtf it is doing in some places
> 
> Anyway, this patch is only compile tested, it
>  * introduces include/linux/hibernate.h with hibernate_ops and
>    a new hibernate() function to hibernate the system
>  * rips apart a lot of the suspend code and puts it back together using
>    the hibernate_ops
>  * switches ACPI to hibernate_ops (the only user of pm_ops.pm_disk_mode)
>  * might apply/compile against -mm, I have all my and some of Rafael's
>    suspend/hibernate work in my tree.
>  * breaks user suspend as I noted above
>  * is incomplete, somewhere pm_suspend_disk() is still defined iirc

OK, I reworked it a bit.

Main changes:

- IMHO 'hibernation_ops' sounds better than 'hibernate_ops', for example, so
now the new names start with 'hibernation_' (or 'HIBERNATION_')

- Moved the hibernation-related definitions to include/linux/suspend.h, since
some hibernation-specific definitions are already there.  We can introduce
hibernation.h in a separate patch (it'll have to #include suspend.h IMO).

- Changed the names starting from 'pm_disk_' (or 'PM_DISK_').

- Cleaned up the new ACPI code (it didn't compile and included some things
unrelated to hibernation).  I'm still not sure about acpi_hibernation_finish()
(is the code after acpi_disable_wakeup_device() really needed?)

- Made kernel/power/user.c compile (and hopefully work too)

It looks like we'll have to change CONFIG_SOFTWARE_SUSPEND into
CONFIG_HIBERNATION, since some pieces of code now look silly.

The appended patch is agaist 2.6.21-rc7-mm2 with two freezer patches applied
(should not affect this one).  Compilation tested on x86_64.

Greetings,
Rafael

---
 Documentation/power/userland-swsusp.txt |   26 ++--
 drivers/acpi/sleep/main.c               |   79 +++++++++++--
 drivers/acpi/sleep/proc.c               |    2 
 drivers/i2c/chips/tps65010.c            |    2 
 include/linux/pm.h                      |   31 -----
 kernel/power/disk.c                     |  184 +++++++++++++++++---------------
 kernel/power/main.c                     |   42 ++-----
 kernel/power/power.h                    |    7 -
 kernel/power/user.c                     |   13 +-
 kernel/sys.c                            |    2 
 10 files changed, 204 insertions(+), 184 deletions(-)

Index: linux-2.6.21-rc7-mm2/include/linux/pm.h
===================================================================
--- linux-2.6.21-rc7-mm2.orig/include/linux/pm.h	2007-04-29 13:39:02.000000000 +0200
+++ linux-2.6.21-rc7-mm2/include/linux/pm.h	2007-04-29 13:39:17.000000000 +0200
@@ -107,26 +107,11 @@ typedef int __bitwise suspend_state_t;
 #define PM_SUSPEND_ON		((__force suspend_state_t) 0)
 #define PM_SUSPEND_STANDBY	((__force suspend_state_t) 1)
 #define PM_SUSPEND_MEM		((__force suspend_state_t) 3)
-#define PM_SUSPEND_DISK		((__force suspend_state_t) 4)
-#define PM_SUSPEND_MAX		((__force suspend_state_t) 5)
-
-typedef int __bitwise suspend_disk_method_t;
-
-/* invalid must be 0 so struct pm_ops initialisers can leave it out */
-#define PM_DISK_INVALID		((__force suspend_disk_method_t) 0)
-#define	PM_DISK_PLATFORM	((__force suspend_disk_method_t) 1)
-#define	PM_DISK_SHUTDOWN	((__force suspend_disk_method_t) 2)
-#define	PM_DISK_REBOOT		((__force suspend_disk_method_t) 3)
-#define	PM_DISK_TEST		((__force suspend_disk_method_t) 4)
-#define	PM_DISK_TESTPROC	((__force suspend_disk_method_t) 5)
-#define	PM_DISK_MAX		((__force suspend_disk_method_t) 6)
+#define PM_SUSPEND_MAX		((__force suspend_state_t) 4)
 
 /**
  * struct pm_ops - Callbacks for managing platform dependent suspend states.
  * @valid: Callback to determine whether the given state can be entered.
- * 	If %CONFIG_SOFTWARE_SUSPEND is set then %PM_SUSPEND_DISK is
- *	always valid and never passed to this call. If not assigned,
- *	no suspend states are valid.
  *	Valid states are advertised in /sys/power/state but can still
  *	be rejected by prepare or enter if the conditions aren't right.
  *	There is a %pm_valid_only_mem function available that can be assigned
@@ -140,24 +125,12 @@ typedef int __bitwise suspend_disk_metho
  *
  * @finish: Called when the system has left the given state and all devices
  *	are resumed. The return value is ignored.
- *
- * @pm_disk_mode: The generic code always allows one of the shutdown methods
- *	%PM_DISK_SHUTDOWN, %PM_DISK_REBOOT, %PM_DISK_TEST and
- *	%PM_DISK_TESTPROC. If this variable is set, the mode it is set
- *	to is allowed in addition to those modes and is also made default.
- *	When this mode is sent selected, the @prepare call will be called
- *	before suspending to disk (if present), the @enter call should be
- *	present and will be called after all state has been saved and the
- *	machine is ready to be powered off; the @finish callback is called
- *	after state has been restored. All these calls are called with
- *	%PM_SUSPEND_DISK as the state.
  */
 struct pm_ops {
 	int (*valid)(suspend_state_t state);
 	int (*prepare)(suspend_state_t state);
 	int (*enter)(suspend_state_t state);
 	int (*finish)(suspend_state_t state);
-	suspend_disk_method_t pm_disk_mode;
 };
 
 /**
@@ -258,8 +231,6 @@ extern void device_power_up(void);
 extern void device_resume(void);
 
 #ifdef CONFIG_PM
-extern suspend_disk_method_t pm_disk_mode;
-
 extern int device_suspend(pm_message_t state);
 extern int device_prepare_suspend(pm_message_t state);
 
Index: linux-2.6.21-rc7-mm2/kernel/power/main.c
===================================================================
--- linux-2.6.21-rc7-mm2.orig/kernel/power/main.c	2007-04-29 13:39:02.000000000 +0200
+++ linux-2.6.21-rc7-mm2/kernel/power/main.c	2007-04-29 13:43:34.000000000 +0200
@@ -30,7 +30,6 @@
 DEFINE_MUTEX(pm_mutex);
 
 struct pm_ops *pm_ops;
-suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN;
 
 /**
  *	pm_set_ops - Set the global power method table. 
@@ -41,10 +40,6 @@ void pm_set_ops(struct pm_ops * ops)
 {
 	mutex_lock(&pm_mutex);
 	pm_ops = ops;
-	if (ops && ops->pm_disk_mode != PM_DISK_INVALID) {
-		pm_disk_mode = ops->pm_disk_mode;
-	} else
-		pm_disk_mode = PM_DISK_SHUTDOWN;
 	mutex_unlock(&pm_mutex);
 }
 
@@ -196,24 +191,12 @@ static void suspend_finish(suspend_state
 static const char * const pm_states[PM_SUSPEND_MAX] = {
 	[PM_SUSPEND_STANDBY]	= "standby",
 	[PM_SUSPEND_MEM]	= "mem",
-	[PM_SUSPEND_DISK]	= "disk",
 };
 
 static inline int valid_state(suspend_state_t state)
 {
-	/* Suspend-to-disk does not really need low-level support.
-	 * It can work with shutdown/reboot if needed. If it isn't
-	 * configured, then it cannot be supported.
-	 */
-	if (state == PM_SUSPEND_DISK)
-#ifdef CONFIG_SOFTWARE_SUSPEND
-		return 1;
-#else
-		return 0;
-#endif
-
-	/* all other states need lowlevel support and need to be
-	 * valid to the lowlevel implementation, no valid callback
+	/* All states need lowlevel support and need to be valid
+	 * to the lowlevel implementation, no valid callback
 	 * implies that none are valid. */
 	if (!pm_ops || !pm_ops->valid || !pm_ops->valid(state))
 		return 0;
@@ -241,11 +224,6 @@ static int enter_state(suspend_state_t s
 	if (!mutex_trylock(&pm_mutex))
 		return -EBUSY;
 
-	if (state == PM_SUSPEND_DISK) {
-		error = pm_suspend_disk();
-		goto Unlock;
-	}
-
 	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
 	if ((error = suspend_prepare(state)))
 		goto Unlock;
@@ -263,7 +241,7 @@ static int enter_state(suspend_state_t s
 
 /**
  *	pm_suspend - Externally visible function for suspending system.
- *	@state:		Enumarted value of state to enter.
+ *	@state:		Enumerated value of state to enter.
  *
  *	Determine whether or not value is within range, get state 
  *	structure, and enter (above).
@@ -301,7 +279,13 @@ static ssize_t state_show(struct subsyst
 		if (pm_states[i] && valid_state(i))
 			s += sprintf(s,"%s ", pm_states[i]);
 	}
-	s += sprintf(s,"\n");
+#ifdef CONFIG_SOFTWARE_SUSPEND
+	s += sprintf(s, "%s\n", "disk");
+#else
+	if (s != buf)
+		/* convert the last space to a newline */
+		*(s-1) = "\n";
+#endif
 	return (s - buf);
 }
 
@@ -316,6 +300,12 @@ static ssize_t state_store(struct subsys
 	p = memchr(buf, '\n', n);
 	len = p ? p - buf : n;
 
+	/* First, check if we are requested to hibernate */
+	if (strncmp(buf, "disk", len)) {
+		error = hibernate();
+		return error ? error : n;
+	}
+
 	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
 		if (*s && !strncmp(buf, *s, len))
 			break;
Index: linux-2.6.21-rc7-mm2/kernel/power/disk.c
===================================================================
--- linux-2.6.21-rc7-mm2.orig/kernel/power/disk.c	2007-04-29 13:39:02.000000000 +0200
+++ linux-2.6.21-rc7-mm2/kernel/power/disk.c	2007-04-29 13:54:50.000000000 +0200
@@ -30,30 +30,60 @@ char resume_file[256] = CONFIG_PM_STD_PA
 dev_t swsusp_resume_device;
 sector_t swsusp_resume_block;
 
+static int hibernation_mode;
+
+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)
+
+struct hibernation_ops *hibernation_ops;
+
+void hibernation_set_ops(struct hibernation_ops *ops)
+{
+	mutex_lock(&pm_mutex);
+	hibernation_ops = ops;
+	mutex_unlock(&pm_mutex);
+	if (hibernation_ops) {
+		BUG_ON(!hibernation_ops->prepare);
+		BUG_ON(!hibernation_ops->enter);
+		BUG_ON(!hibernation_ops->finish);
+	}
+}
+
+
 /**
  *	platform_prepare - prepare the machine for hibernation using the
  *	platform driver if so configured and return an error code if it fails
  */
 
-static inline int platform_prepare(void)
+static int platform_prepare(void)
 {
-	int error = 0;
+	return (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) ?
+		hibernation_ops->prepare() : 0;
+}
 
-	switch (pm_disk_mode) {
-	case PM_DISK_TEST:
-	case PM_DISK_TESTPROC:
-	case PM_DISK_SHUTDOWN:
-	case PM_DISK_REBOOT:
-		break;
-	default:
-		if (pm_ops && pm_ops->prepare)
-			error = pm_ops->prepare(PM_SUSPEND_DISK);
-	}
-	return error;
+/**
+ *	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(void)
+{
+	if (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops)
+		hibernation_ops->finish();
 }
 
 /**
- *	power_down - Shut machine down for hibernate.
+ *	power_down - Shut the machine down for hibernation.
  *
  *	Use the platform driver, if configured so; otherwise try
  *	to power off or reboot.
@@ -61,20 +91,20 @@ static inline int platform_prepare(void)
 
 static void power_down(void)
 {
-	switch (pm_disk_mode) {
-	case PM_DISK_TEST:
-	case PM_DISK_TESTPROC:
+	switch (hibernation_mode) {
+	case HIBERNATION_TEST:
+	case HIBERNATION_TESTPROC:
 		break;
-	case PM_DISK_SHUTDOWN:
+	case HIBERNATION_SHUTDOWN:
 		kernel_power_off();
 		break;
-	case PM_DISK_REBOOT:
+	case HIBERNATION_REBOOT:
 		kernel_restart(NULL);
 		break;
-	default:
-		if (pm_ops && pm_ops->enter) {
+	case HIBERNATION_PLATFORM:
+		if (hibernation_ops) {
 			kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK);
-			pm_ops->enter(PM_SUSPEND_DISK);
+			hibernation_ops->enter();
 			break;
 		}
 	}
@@ -87,20 +117,6 @@ static void power_down(void)
 	while(1);
 }
 
-static inline void platform_finish(void)
-{
-	switch (pm_disk_mode) {
-	case PM_DISK_TEST:
-	case PM_DISK_TESTPROC:
-	case PM_DISK_SHUTDOWN:
-	case PM_DISK_REBOOT:
-		break;
-	default:
-		if (pm_ops && pm_ops->finish)
-			pm_ops->finish(PM_SUSPEND_DISK);
-	}
-}
-
 static void unprepare_processes(void)
 {
 	thaw_processes();
@@ -120,13 +136,10 @@ static int prepare_processes(void)
 }
 
 /**
- *	pm_suspend_disk - The granpappy of hibernation power management.
- *
- *	If not, then call swsusp to do its thing, then figure out how
- *	to power down the system.
+ *	hibernate - The granpappy of the built-in hibernation management
  */
 
-int pm_suspend_disk(void)
+int hibernate(void)
 {
 	int error;
 
@@ -151,7 +164,7 @@ int pm_suspend_disk(void)
 	if (error)
 		goto Thaw;
 
-	if (pm_disk_mode == PM_DISK_TESTPROC) {
+	if (hibernation_mode == HIBERNATION_TESTPROC) {
 		printk("swsusp debug: Waiting for 5 seconds.\n");
 		mdelay(5000);
 		goto Thaw;
@@ -176,7 +189,7 @@ int pm_suspend_disk(void)
 	if (error)
 		goto Enable_cpus;
 
-	if (pm_disk_mode == PM_DISK_TEST) {
+	if (hibernation_mode == HIBERNATION_TEST) {
 		printk("swsusp debug: Waiting for 5 seconds.\n");
 		mdelay(5000);
 		goto Enable_cpus;
@@ -230,7 +243,7 @@ int pm_suspend_disk(void)
  *	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.
+ *	return above (in hibernate() ) if everything goes well.
  *	Otherwise, we fail gracefully and return to the normally
  *	scheduled program.
  *
@@ -336,25 +349,26 @@ static int software_resume(void)
 late_initcall(software_resume);
 
 
-static const char * const pm_disk_modes[] = {
-	[PM_DISK_PLATFORM]	= "platform",
-	[PM_DISK_SHUTDOWN]	= "shutdown",
-	[PM_DISK_REBOOT]	= "reboot",
-	[PM_DISK_TEST]		= "test",
-	[PM_DISK_TESTPROC]	= "testproc",
+static const char * const hibernation_modes[] = {
+	[HIBERNATION_PLATFORM]	= "platform",
+	[HIBERNATION_SHUTDOWN]	= "shutdown",
+	[HIBERNATION_REBOOT]	= "reboot",
+	[HIBERNATION_TEST]	= "test",
+	[HIBERNATION_TESTPROC]	= "testproc",
 };
 
 /**
- *	disk - Control suspend-to-disk mode
+ *	disk - Control hibernation mode
  *
  *	Suspend-to-disk can be handled in several ways. We have a few options
  *	for putting the system to sleep - using the platform driver (e.g. ACPI
- *	or other pm_ops), powering off the system or rebooting the system
- *	(for testing) as well as the two test modes.
+ *	or other hibernation_ops), powering off the system or rebooting the
+ *	system (for testing) as well as the two test modes.
  *
  *	The system can support 'platform', and that is known a priori (and
- *	encoded in pm_ops). However, the user may choose 'shutdown' or 'reboot'
- *	as alternatives, as well as the test modes 'test' and 'testproc'.
+ *	encoded by the presence of hibernation_ops). However, the user may
+ *	choose 'shutdown' or 'reboot' as alternatives, as well as one fo the
+ *	test modes, 'test' or 'testproc'.
  *
  *	show() will display what the mode is currently set to.
  *	store() will accept one of
@@ -366,7 +380,7 @@ static const char * const pm_disk_modes[
  *	'testproc'
  *
  *	It will only change to 'platform' if the system
- *	supports it (as determined from pm_ops->pm_disk_mode).
+ *	supports it (as determined by having hibernation_ops).
  */
 
 static ssize_t disk_show(struct subsystem * subsys, char * buf)
@@ -374,27 +388,26 @@ static ssize_t disk_show(struct subsyste
 	int i;
 	char *start = buf;
 
-	for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) {
-		if (!pm_disk_modes[i])
+	for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) {
+		if (!hibernation_modes[i])
 			continue;
 		switch (i) {
-		case PM_DISK_SHUTDOWN:
-		case PM_DISK_REBOOT:
-		case PM_DISK_TEST:
-		case PM_DISK_TESTPROC:
+		case HIBERNATION_SHUTDOWN:
+		case HIBERNATION_REBOOT:
+		case HIBERNATION_TEST:
+		case HIBERNATION_TESTPROC:
 			break;
-		default:
-			if (pm_ops && pm_ops->enter &&
-			    (i == pm_ops->pm_disk_mode))
+		case HIBERNATION_PLATFORM:
+			if (hibernation_ops)
 				break;
 			/* not a valid mode, continue with loop */
 			continue;
 		}
-		if (i == pm_disk_mode)
-			buf += sprintf(buf, "[%s]", pm_disk_modes[i]);
+		if (i == hibernation_mode)
+			buf += sprintf(buf, "[%s]", hibernation_modes[i]);
 		else
-			buf += sprintf(buf, "%s", pm_disk_modes[i]);
-		if (i+1 != PM_DISK_MAX)
+			buf += sprintf(buf, "%s", hibernation_modes[i]);
+		if (i+1 != HIBERNATION_MAX)
 			buf += sprintf(buf, " ");
 	}
 	buf += sprintf(buf, "\n");
@@ -408,39 +421,38 @@ static ssize_t disk_store(struct subsyst
 	int i;
 	int len;
 	char *p;
-	suspend_disk_method_t mode = 0;
+	int mode = HIBERNATION_INVALID;
 
 	p = memchr(buf, '\n', n);
 	len = p ? p - buf : n;
 
 	mutex_lock(&pm_mutex);
-	for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) {
-		if (!strncmp(buf, pm_disk_modes[i], len)) {
+	for (i = HIBERNATION_FIRST; i < HIBERNATION_MAX; i++) {
+		if (!strncmp(buf, hibernation_modes[i], len)) {
 			mode = i;
 			break;
 		}
 	}
-	if (mode) {
+	if (mode != HIBERNATION_INVALID) {
 		switch (mode) {
-		case PM_DISK_SHUTDOWN:
-		case PM_DISK_REBOOT:
-		case PM_DISK_TEST:
-		case PM_DISK_TESTPROC:
-			pm_disk_mode = mode;
+		case HIBERNATION_SHUTDOWN:
+		case HIBERNATION_REBOOT:
+		case HIBERNATION_TEST:
+		case HIBERNATION_TESTPROC:
+			hibernation_mode = mode;
 			break;
-		default:
-			if (pm_ops && pm_ops->enter &&
-			    (mode == pm_ops->pm_disk_mode))
-				pm_disk_mode = mode;
+		case HIBERNATION_PLATFORM:
+			if (hibernation_ops)
+				hibernation_mode = mode;
 			else
 				error = -EINVAL;
 		}
-	} else {
+	} else
 		error = -EINVAL;
-	}
 
-	pr_debug("PM: suspend-to-disk mode set to '%s'\n",
-		 pm_disk_modes[mode]);
+	if (!error)
+		pr_debug("PM: suspend-to-disk mode set to '%s'\n",
+			 hibernation_modes[mode]);
 	mutex_unlock(&pm_mutex);
 	return error ? error : n;
 }
Index: linux-2.6.21-rc7-mm2/Documentation/power/userland-swsusp.txt
===================================================================
--- linux-2.6.21-rc7-mm2.orig/Documentation/power/userland-swsusp.txt	2007-04-29 13:39:02.000000000 +0200
+++ linux-2.6.21-rc7-mm2/Documentation/power/userland-swsusp.txt	2007-04-29 13:39:18.000000000 +0200
@@ -93,21 +93,23 @@ SNAPSHOT_S2RAM - suspend to RAM; using t
 	to resume the system from RAM if there's enough battery power or restore
 	its state on the basis of the saved suspend image otherwise)
 
-SNAPSHOT_PMOPS - enable the usage of the pmops->prepare, pmops->enter and
-	pmops->finish methods (the in-kernel swsusp knows these as the "platform
-	method") which are needed on many machines to (among others) speed up
-	the resume by letting the BIOS skip some steps or to let the system
-	recognise the correct state of the hardware after the resume (in
-	particular on many machines this ensures that unplugged AC
-	adapters get correctly detected and that kacpid does not run wild after
-	the resume).  The last ioctl() argument can take one of the three
-	values, defined in kernel/power/power.h:
+SNAPSHOT_PMOPS - enable the usage of the hibernation_ops->prepare,
+	hibernate_ops->enter and hibernation_ops->finish methods (the in-kernel
+	swsusp knows these as the "platform method") which are needed on many
+	machines to (among others) speed up the resume by letting the BIOS skip
+	some steps or to let the system recognise the correct state of the
+	hardware after the resume (in particular on many machines this ensures
+	that unplugged AC adapters get correctly detected and that kacpid does
+	not run wild after the resume).  The last ioctl() argument can take one
+	of the three values, defined in kernel/power/power.h:
 	PMOPS_PREPARE - make the kernel carry out the
-		pm_ops->prepare(PM_SUSPEND_DISK) operation
+		hibernation_ops->prepare() operation
 	PMOPS_ENTER - make the kernel power off the system by calling
-		pm_ops->enter(PM_SUSPEND_DISK)
+		hibernation_ops->enter()
 	PMOPS_FINISH - make the kernel carry out the
-		pm_ops->finish(PM_SUSPEND_DISK) operation
+		hibernation_ops->finish() operation
+	Note that the actual constants are misnamed because they surface
+	internal kernel implementation details that have changed.
 
 The device's read() operation can be used to transfer the snapshot image from
 the kernel.  It has the following limitations:
Index: linux-2.6.21-rc7-mm2/drivers/i2c/chips/tps65010.c
===================================================================
--- linux-2.6.21-rc7-mm2.orig/drivers/i2c/chips/tps65010.c	2007-04-29 13:39:02.000000000 +0200
+++ linux-2.6.21-rc7-mm2/drivers/i2c/chips/tps65010.c	2007-04-29 13:39:18.000000000 +0200
@@ -354,7 +354,7 @@ static void tps65010_interrupt(struct tp
 			 * also needs to get error handling and probably
 			 * an #ifdef CONFIG_SOFTWARE_SUSPEND
 			 */
-			pm_suspend(PM_SUSPEND_DISK);
+			hibernate();
 #endif
 			poll = 1;
 		}
Index: linux-2.6.21-rc7-mm2/kernel/sys.c
===================================================================
--- linux-2.6.21-rc7-mm2.orig/kernel/sys.c	2007-04-29 13:39:02.000000000 +0200
+++ linux-2.6.21-rc7-mm2/kernel/sys.c	2007-04-29 13:39:18.000000000 +0200
@@ -942,7 +942,7 @@ asmlinkage long sys_reboot(int magic1, i
 #ifdef CONFIG_SOFTWARE_SUSPEND
 	case LINUX_REBOOT_CMD_SW_SUSPEND:
 		{
-			int ret = pm_suspend(PM_SUSPEND_DISK);
+			int ret = hibernate();
 			unlock_kernel();
 			return ret;
 		}
Index: linux-2.6.21-rc7-mm2/drivers/acpi/sleep/main.c
===================================================================
--- linux-2.6.21-rc7-mm2.orig/drivers/acpi/sleep/main.c	2007-04-29 13:39:02.000000000 +0200
+++ linux-2.6.21-rc7-mm2/drivers/acpi/sleep/main.c	2007-04-29 14:16:30.000000000 +0200
@@ -29,7 +29,6 @@ static u32 acpi_suspend_states[] = {
 	[PM_SUSPEND_ON] = ACPI_STATE_S0,
 	[PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
 	[PM_SUSPEND_MEM] = ACPI_STATE_S3,
-	[PM_SUSPEND_DISK] = ACPI_STATE_S4,
 	[PM_SUSPEND_MAX] = ACPI_STATE_S5
 };
 
@@ -94,14 +93,6 @@ static int acpi_pm_enter(suspend_state_t
 		do_suspend_lowlevel();
 		break;
 
-	case PM_SUSPEND_DISK:
-		if (acpi_pm_ops.pm_disk_mode == PM_DISK_PLATFORM)
-			status = acpi_enter_sleep_state(acpi_state);
-		break;
-	case PM_SUSPEND_MAX:
-		acpi_power_off();
-		break;
-
 	default:
 		return -EINVAL;
 	}
@@ -157,12 +148,13 @@ int acpi_suspend(u32 acpi_state)
 	suspend_state_t states[] = {
 		[1] = PM_SUSPEND_STANDBY,
 		[3] = PM_SUSPEND_MEM,
-		[4] = PM_SUSPEND_DISK,
 		[5] = PM_SUSPEND_MAX
 	};
 
 	if (acpi_state < 6 && states[acpi_state])
 		return pm_suspend(states[acpi_state]);
+	if (acpi_state == 4)
+		return hibernate();
 	return -EINVAL;
 }
 
@@ -189,6 +181,61 @@ static struct pm_ops acpi_pm_ops = {
 	.finish = acpi_pm_finish,
 };
 
+#ifdef CONFIG_SOFTWARE_SUSPEND
+static int acpi_hibernation_prepare(void)
+{
+	return acpi_sleep_prepare(ACPI_STATE_S4);
+}
+
+static int acpi_hibernation_enter(void)
+{
+	acpi_status status = AE_OK;
+	unsigned long flags = 0;
+	int error;
+
+	ACPI_FLUSH_CPU_CACHE();
+
+	/* Do arch specific saving of state. */
+	error = acpi_save_state_mem();
+	if (error)
+		return error;
+
+	local_irq_save(flags);
+	acpi_enable_wakeup_device(ACPI_STATE_S4);
+	status = acpi_enter_sleep_state(ACPI_STATE_S4);
+	local_irq_restore(flags);
+
+	/*
+	 * Restore processor state
+	 * We should only be here if we're coming back from hibernation and
+	 * the memory image should have already been loaded from disk.
+	 */
+	acpi_restore_state_mem();
+
+	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
+}
+
+static void acpi_hibernation_finish(void)
+{
+	acpi_leave_sleep_state(ACPI_STATE_S4);
+	acpi_disable_wakeup_device(ACPI_STATE_S4);
+
+	/* reset firmware waking vector */
+	acpi_set_firmware_waking_vector((acpi_physical_address) 0);
+
+	if (init_8259A_after_S1) {
+		printk("Broken toshiba laptop -> kicking interrupts\n");
+		init_8259A(0);
+	}
+}
+
+static struct hibernation_ops acpi_hibernation_ops = {
+	.prepare = acpi_hibernation_prepare,
+	.enter = acpi_hibernation_enter,
+	.finish = acpi_hibernation_finish,
+};
+#endif /* CONFIG_SOFTWARE_SUSPEND */
+
 /*
  * Toshiba fails to preserve interrupts over S1, reinitialization
  * of 8259 is needed after S1 resume.
@@ -227,14 +274,18 @@ int __init acpi_sleep_init(void)
 			sleep_states[i] = 1;
 			printk(" S%d", i);
 		}
-		if (i == ACPI_STATE_S4) {
-			if (sleep_states[i])
-				acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM;
-		}
 	}
 	printk(")\n");
 
 	pm_set_ops(&acpi_pm_ops);
+
+#ifdef CONFIG_SOFTWARE_SUSPEND
+	if (sleep_states[ACPI_STATE_S4])
+		hibernation_set_ops(&acpi_hibernation_ops);
+#else
+	sleep_states[ACPI_STATE_S4] = 0;
+#endif
+
 	return 0;
 }
 
Index: linux-2.6.21-rc7-mm2/kernel/power/power.h
===================================================================
--- linux-2.6.21-rc7-mm2.orig/kernel/power/power.h	2007-04-29 13:39:02.000000000 +0200
+++ linux-2.6.21-rc7-mm2/kernel/power/power.h	2007-04-29 13:55:55.000000000 +0200
@@ -25,12 +25,7 @@ struct swsusp_info {
  */
 #define SPARE_PAGES	((1024 * 1024) >> PAGE_SHIFT)
 
-extern int pm_suspend_disk(void);
-#else
-static inline int pm_suspend_disk(void)
-{
-	return -EPERM;
-}
+extern struct hibernation_ops *hibernation_ops;
 #endif
 
 extern int pfn_is_nosave(unsigned long);
Index: linux-2.6.21-rc7-mm2/drivers/acpi/sleep/proc.c
===================================================================
--- linux-2.6.21-rc7-mm2.orig/drivers/acpi/sleep/proc.c	2007-04-29 13:39:02.000000000 +0200
+++ linux-2.6.21-rc7-mm2/drivers/acpi/sleep/proc.c	2007-04-29 13:49:42.000000000 +0200
@@ -60,7 +60,7 @@ acpi_system_write_sleep(struct file *fil
 	state = simple_strtoul(str, NULL, 0);
 #ifdef CONFIG_SOFTWARE_SUSPEND
 	if (state == 4) {
-		error = pm_suspend(PM_SUSPEND_DISK);
+		error = hibernate();
 		goto Done;
 	}
 #endif
Index: linux-2.6.21-rc7-mm2/kernel/power/user.c
===================================================================
--- linux-2.6.21-rc7-mm2.orig/kernel/power/user.c	2007-04-29 13:43:34.000000000 +0200
+++ linux-2.6.21-rc7-mm2/kernel/power/user.c	2007-04-29 14:00:42.000000000 +0200
@@ -138,16 +138,16 @@ static inline int platform_prepare(void)
 {
 	int error = 0;
 
-	if (pm_ops && pm_ops->prepare)
-		error = pm_ops->prepare(PM_SUSPEND_DISK);
+	if (hibernation_ops)
+		error = hibernation_ops->prepare();
 
 	return error;
 }
 
 static inline void platform_finish(void)
 {
-	if (pm_ops && pm_ops->finish)
-		pm_ops->finish(PM_SUSPEND_DISK);
+	if (hibernation_ops)
+		hibernation_ops->finish();
 }
 
 static inline int snapshot_suspend(int platform_suspend)
@@ -407,7 +407,7 @@ static int snapshot_ioctl(struct inode *
 		switch (arg) {
 
 		case PMOPS_PREPARE:
-			if (pm_ops && pm_ops->enter) {
+			if (hibernation_ops) {
 				data->platform_suspend = 1;
 				error = 0;
 			} else {
@@ -418,8 +418,7 @@ static int snapshot_ioctl(struct inode *
 		case PMOPS_ENTER:
 			if (data->platform_suspend) {
 				kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK);
-				error = pm_ops->enter(PM_SUSPEND_DISK);
-				error = 0;
+				error = hibernation_ops->enter();
 			}
 			break;
 




> 
> johannes
> ---
>  Documentation/power/userland-swsusp.txt |   26 +++----
>  drivers/acpi/sleep/main.c               |   89 ++++++++++++++++++++----
>  drivers/acpi/sleep/proc.c               |    3 
>  drivers/i2c/chips/tps65010.c            |    2 
>  include/linux/hibernate.h               |   36 +++++++++
>  include/linux/pm.h                      |   31 --------
>  kernel/power/disk.c                     |  117 +++++++++++++++++++-------------
>  kernel/power/main.c                     |   47 +++++-------
>  kernel/power/power.h                    |   13 ---
>  kernel/power/user.c                     |   28 +------
>  kernel/sys.c                            |    3 
>  11 files changed, 231 insertions(+), 164 deletions(-)
> 
> --- wireless-dev.orig/include/linux/pm.h	2007-04-26 18:15:00.440691185 +0200
> +++ wireless-dev/include/linux/pm.h	2007-04-26 18:15:09.410691185 +0200
> @@ -107,26 +107,11 @@ typedef int __bitwise suspend_state_t;
>  #define PM_SUSPEND_ON		((__force suspend_state_t) 0)
>  #define PM_SUSPEND_STANDBY	((__force suspend_state_t) 1)
>  #define PM_SUSPEND_MEM		((__force suspend_state_t) 3)
> -#define PM_SUSPEND_DISK		((__force suspend_state_t) 4)
> -#define PM_SUSPEND_MAX		((__force suspend_state_t) 5)
> -
> -typedef int __bitwise suspend_disk_method_t;
> -
> -/* invalid must be 0 so struct pm_ops initialisers can leave it out */
> -#define PM_DISK_INVALID		((__force suspend_disk_method_t) 0)
> -#define	PM_DISK_PLATFORM	((__force suspend_disk_method_t) 1)
> -#define	PM_DISK_SHUTDOWN	((__force suspend_disk_method_t) 2)
> -#define	PM_DISK_REBOOT		((__force suspend_disk_method_t) 3)
> -#define	PM_DISK_TEST		((__force suspend_disk_method_t) 4)
> -#define	PM_DISK_TESTPROC	((__force suspend_disk_method_t) 5)
> -#define	PM_DISK_MAX		((__force suspend_disk_method_t) 6)
> +#define PM_SUSPEND_MAX		((__force suspend_state_t) 4)
>  
>  /**
>   * struct pm_ops - Callbacks for managing platform dependent suspend states.
>   * @valid: Callback to determine whether the given state can be entered.
> - * 	If %CONFIG_SOFTWARE_SUSPEND is set then %PM_SUSPEND_DISK is
> - *	always valid and never passed to this call. If not assigned,
> - *	no suspend states are valid.
>   *	Valid states are advertised in /sys/power/state but can still
>   *	be rejected by prepare or enter if the conditions aren't right.
>   *	There is a %pm_valid_only_mem function available that can be assigned
> @@ -140,24 +125,12 @@ typedef int __bitwise suspend_disk_metho
>   *
>   * @finish: Called when the system has left the given state and all devices
>   *	are resumed. The return value is ignored.
> - *
> - * @pm_disk_mode: The generic code always allows one of the shutdown methods
> - *	%PM_DISK_SHUTDOWN, %PM_DISK_REBOOT, %PM_DISK_TEST and
> - *	%PM_DISK_TESTPROC. If this variable is set, the mode it is set
> - *	to is allowed in addition to those modes and is also made default.
> - *	When this mode is sent selected, the @prepare call will be called
> - *	before suspending to disk (if present), the @enter call should be
> - *	present and will be called after all state has been saved and the
> - *	machine is ready to be powered off; the @finish callback is called
> - *	after state has been restored. All these calls are called with
> - *	%PM_SUSPEND_DISK as the state.
>   */
>  struct pm_ops {
>  	int (*valid)(suspend_state_t state);
>  	int (*prepare)(suspend_state_t state);
>  	int (*enter)(suspend_state_t state);
>  	int (*finish)(suspend_state_t state);
> -	suspend_disk_method_t pm_disk_mode;
>  };
>  
>  /**
> @@ -276,8 +249,6 @@ extern void device_power_up(void);
>  extern void device_resume(void);
>  
>  #ifdef CONFIG_PM
> -extern suspend_disk_method_t pm_disk_mode;
> -
>  extern int device_suspend(pm_message_t state);
>  extern int device_prepare_suspend(pm_message_t state);
>  
> --- wireless-dev.orig/kernel/power/main.c	2007-04-26 18:15:00.790691185 +0200
> +++ wireless-dev/kernel/power/main.c	2007-04-26 18:15:09.410691185 +0200
> @@ -21,6 +21,7 @@
>  #include <linux/resume-trace.h>
>  #include <linux/freezer.h>
>  #include <linux/vmstat.h>
> +#include <linux/hibernate.h>
>  
>  #include "power.h"
>  
> @@ -30,7 +31,6 @@
>  DEFINE_MUTEX(pm_mutex);
>  
>  struct pm_ops *pm_ops;
> -suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN;
>  
>  /**
>   *	pm_set_ops - Set the global power method table. 
> @@ -41,10 +41,6 @@ void pm_set_ops(struct pm_ops * ops)
>  {
>  	mutex_lock(&pm_mutex);
>  	pm_ops = ops;
> -	if (ops && ops->pm_disk_mode != PM_DISK_INVALID) {
> -		pm_disk_mode = ops->pm_disk_mode;
> -	} else
> -		pm_disk_mode = PM_DISK_SHUTDOWN;
>  	mutex_unlock(&pm_mutex);
>  }
>  
> @@ -184,24 +180,12 @@ static void suspend_finish(suspend_state
>  static const char * const pm_states[PM_SUSPEND_MAX] = {
>  	[PM_SUSPEND_STANDBY]	= "standby",
>  	[PM_SUSPEND_MEM]	= "mem",
> -	[PM_SUSPEND_DISK]	= "disk",
>  };
>  
>  static inline int valid_state(suspend_state_t state)
>  {
> -	/* Suspend-to-disk does not really need low-level support.
> -	 * It can work with shutdown/reboot if needed. If it isn't
> -	 * configured, then it cannot be supported.
> -	 */
> -	if (state == PM_SUSPEND_DISK)
> -#ifdef CONFIG_SOFTWARE_SUSPEND
> -		return 1;
> -#else
> -		return 0;
> -#endif
> -
> -	/* all other states need lowlevel support and need to be
> -	 * valid to the lowlevel implementation, no valid callback
> +	/* All states need lowlevel support and need to be valid
> +	 * to the lowlevel implementation, no valid callback
>  	 * implies that none are valid. */
>  	if (!pm_ops || !pm_ops->valid || !pm_ops->valid(state))
>  		return 0;
> @@ -229,11 +213,6 @@ static int enter_state(suspend_state_t s
>  	if (!mutex_trylock(&pm_mutex))
>  		return -EBUSY;
>  
> -	if (state == PM_SUSPEND_DISK) {
> -		error = pm_suspend_disk();
> -		goto Unlock;
> -	}
> -
>  	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
>  	if ((error = suspend_prepare(state)))
>  		goto Unlock;
> @@ -251,7 +230,7 @@ static int enter_state(suspend_state_t s
>  
>  /**
>   *	pm_suspend - Externally visible function for suspending system.
> - *	@state:		Enumarted value of state to enter.
> + *	@state:		Enumerated value of state to enter.
>   *
>   *	Determine whether or not value is within range, get state 
>   *	structure, and enter (above).
> @@ -283,13 +262,19 @@ decl_subsys(power,NULL,NULL);
>  static ssize_t state_show(struct subsystem * subsys, char * buf)
>  {
>  	int i;
> -	char * s = buf;
> +	char *s = buf;
>  
>  	for (i = 0; i < PM_SUSPEND_MAX; i++) {
>  		if (pm_states[i] && valid_state(i))
> -			s += sprintf(s,"%s ", pm_states[i]);
> +			s += sprintf(s, "%s ", pm_states[i]);
>  	}
> -	s += sprintf(s,"\n");
> +#ifdef CONFIG_SOFTWARE_SUSPEND
> +	s += sprintf(s, "%s\n", "disk");
> +#else
> +	if (s != buf)
> +		/* convert the last space to a newline */
> +		*(s-1) = "\n";
> +#endif
>  	return (s - buf);
>  }
>  
> @@ -304,6 +289,12 @@ static ssize_t state_store(struct subsys
>  	p = memchr(buf, '\n', n);
>  	len = p ? p - buf : n;
>  
> +	/* first check hibernate */
> +	if (strncmp(buf, "disk", len)) {
> +		error = hibernate();
> +		return error ? error : n;
> +	}
> +
>  	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
>  		if (*s && !strncmp(buf, *s, len))
>  			break;
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ wireless-dev/include/linux/hibernate.h	2007-04-26 18:21:38.130691185 +0200
> @@ -0,0 +1,36 @@
> +#ifndef __LINUX_HIBERNATE
> +#define __LINUX_HIBERNATE
> +/*
> + * hibernate ('suspend to disk') functionality
> + */
> +
> +/**
> + * struct hibernate_ops - hibernate platform support
> + *
> + * The methods in this structure allow a platform to override what
> + * happens for shutting down the machine when going into hibernation.
> + *
> + * All three methods must be assigned.
> + *
> + * @prepare: prepare system for hibernation
> + * @enter: shut down system after state has been saved to disk
> + * @finish: finish/clean up after state has been reloaded
> + */
> +struct hibernate_ops {
> +	int (*prepare)(void);
> +	int (*enter)(void);
> +	void (*finish)(void);
> +};
> +
> +/**
> + * hibernate_set_ops - set the global hibernate operations
> + * @ops: the hibernate operations to use from now on.
> + */
> +void hibernate_set_ops(struct hibernate_ops *ops);
> +
> +/**
> + * hibernate - hibernate the system
> + */
> +int hibernate(void);
> +
> +#endif /* __LINUX_HIBERNATE */
> --- wireless-dev.orig/kernel/power/disk.c	2007-04-26 18:15:00.800691185 +0200
> +++ wireless-dev/kernel/power/disk.c	2007-04-26 18:15:09.420691185 +0200
> @@ -21,45 +21,72 @@
>  #include <linux/console.h>
>  #include <linux/cpu.h>
>  #include <linux/freezer.h>
> +#include <linux/hibernate.h>
>  
>  #include "power.h"
>  
>  
> -static int noresume = 0;
> +static int noresume;
>  char resume_file[256] = CONFIG_PM_STD_PARTITION;
>  dev_t swsusp_resume_device;
>  sector_t swsusp_resume_block;
>  
> +static struct hibernate_ops *hibernate_ops;
> +static int pm_disk_mode;
> +
> +enum {
> +	PM_DISK_INVALID,
> +	PM_DISK_PLATFORM,
> +	PM_DISK_TEST,
> +	PM_DISK_TESTPROC,
> +	PM_DISK_SHUTDOWN,
> +	PM_DISK_REBOOT,
> +	/* keep last */
> +	__PM_DISK_AFTER_LAST
> +};
> +#define PM_DISK_MAX (__PM_DISK_AFTER_LAST-1)
> +#define PM_DISK_FIRST (PM_DISK_INVALID + 1)
> +
> +void hibernate_set_ops(struct hibernate_ops *ops)
> +{
> +	BUG_ON(!hibernate_ops->prepare);
> +	BUG_ON(!hibernate_ops->enter);
> +	BUG_ON(!hibernate_ops->finish);
> +	mutex_lock(&pm_mutex);
> +	hibernate_ops = ops;
> +	mutex_unlock(&pm_mutex);
> +}
> +
> +
>  /**
> - *	platform_prepare - prepare the machine for hibernation using the
> - *	platform driver if so configured and return an error code if it fails
> + *	hibernate_platform_prepare - prepare the machine for hibernation using
> + *	the platform driver if so configured and return an error code if it
> + *	fails.
>   */
>  
> -static inline int platform_prepare(void)
> +int hibernate_platform_prepare(void)
>  {
> -	int error = 0;
> -
>  	switch (pm_disk_mode) {
>  	case PM_DISK_TEST:
>  	case PM_DISK_TESTPROC:
>  	case PM_DISK_SHUTDOWN:
>  	case PM_DISK_REBOOT:
>  		break;
> -	default:
> -		if (pm_ops && pm_ops->prepare)
> -			error = pm_ops->prepare(PM_SUSPEND_DISK);
> +	case PM_DISK_PLATFORM:
> +		if (hibernate_ops)
> +			return hibernate_ops->prepare();
>  	}
> -	return error;
> +	return 0;
>  }
>  
>  /**
> - *	power_down - Shut machine down for hibernate.
> + *	hibernate_power_down - Shut machine down for hibernate.
>   *
>   *	Use the platform driver, if configured so; otherwise try
>   *	to power off or reboot.
>   */
>  
> -static void power_down(void)
> +static void hibernate_power_down(void)
>  {
>  	switch (pm_disk_mode) {
>  	case PM_DISK_TEST:
> @@ -70,11 +97,10 @@ static void power_down(void)
>  	case PM_DISK_REBOOT:
>  		kernel_restart(NULL);
>  		break;
> -	default:
> -		if (pm_ops && pm_ops->enter) {
> +	case PM_DISK_PLATFORM:
> +		if (hibernate_ops) {
>  			kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK);
> -			pm_ops->enter(PM_SUSPEND_DISK);
> -			break;
> +			hibernate_ops->enter();
>  		}
>  	}
>  
> @@ -85,7 +111,7 @@ static void power_down(void)
>  	while(1);
>  }
>  
> -static inline void platform_finish(void)
> +void hibernate_platform_finish(void)
>  {
>  	switch (pm_disk_mode) {
>  	case PM_DISK_TEST:
> @@ -93,9 +119,9 @@ static inline void platform_finish(void)
>  	case PM_DISK_SHUTDOWN:
>  	case PM_DISK_REBOOT:
>  		break;
> -	default:
> -		if (pm_ops && pm_ops->finish)
> -			pm_ops->finish(PM_SUSPEND_DISK);
> +	case PM_DISK_PLATFORM:
> +		if (hibernate_ops)
> +			hibernate_ops->finish();
>  	}
>  }
>  
> @@ -118,13 +144,13 @@ static int prepare_processes(void)
>  }
>  
>  /**
> - *	pm_suspend_disk - The granpappy of hibernation power management.
> + *	hibernate - The granpappy of hibernation power management.
>   *
>   *	If not, then call swsusp to do its thing, then figure out how
>   *	to power down the system.
>   */
>  
> -int pm_suspend_disk(void)
> +int hibernate(void)
>  {
>  	int error;
>  
> @@ -147,7 +173,7 @@ int pm_suspend_disk(void)
>  	if (error)
>  		goto Finish;
>  
> -	error = platform_prepare();
> +	error = hibernate_platform_prepare();
>  	if (error)
>  		goto Finish;
>  
> @@ -175,13 +201,13 @@ int pm_suspend_disk(void)
>  
>  	if (in_suspend) {
>  		enable_nonboot_cpus();
> -		platform_finish();
> +		hibernate_platform_finish();
>  		device_resume();
>  		resume_console();
>  		pr_debug("PM: writing image.\n");
>  		error = swsusp_write();
>  		if (!error)
> -			power_down();
> +			hibernate_power_down();
>  		else {
>  			swsusp_free();
>  			goto Finish;
> @@ -194,7 +220,7 @@ int pm_suspend_disk(void)
>   Enable_cpus:
>  	enable_nonboot_cpus();
>   Resume_devices:
> -	platform_finish();
> +	hibernate_platform_finish();
>  	device_resume();
>  	resume_console();
>   Finish:
> @@ -211,7 +237,7 @@ int pm_suspend_disk(void)
>   *	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.
> + *	return above (in hibernate() ) if everything goes well.
>   *	Otherwise, we fail gracefully and return to the normally
>   *	scheduled program.
>   *
> @@ -311,12 +337,13 @@ static const char * const pm_disk_modes[
>   *
>   *	Suspend-to-disk can be handled in several ways. We have a few options
>   *	for putting the system to sleep - using the platform driver (e.g. ACPI
> - *	or other pm_ops), powering off the system or rebooting the system
> - *	(for testing) as well as the two test modes.
> + *	or other hibernate_ops), powering off the system or rebooting the
> + *	system (for testing) as well as the two test modes.
>   *
>   *	The system can support 'platform', and that is known a priori (and
> - *	encoded in pm_ops). However, the user may choose 'shutdown' or 'reboot'
> - *	as alternatives, as well as the test modes 'test' and 'testproc'.
> + *	encoded by the presence of hibernate_ops). However, the user may choose
> + *	'shutdown' or 'reboot' as alternatives, as well as the test modes 'test'
> + *	and 'testproc'.
>   *
>   *	show() will display what the mode is currently set to.
>   *	store() will accept one of
> @@ -328,7 +355,7 @@ static const char * const pm_disk_modes[
>   *	'testproc'
>   *
>   *	It will only change to 'platform' if the system
> - *	supports it (as determined from pm_ops->pm_disk_mode).
> + *	supports it (as determined by having hibernate_ops).
>   */
>  
>  static ssize_t disk_show(struct subsystem * subsys, char * buf)
> @@ -336,7 +363,7 @@ static ssize_t disk_show(struct subsyste
>  	int i;
>  	char *start = buf;
>  
> -	for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) {
> +	for (i = PM_DISK_FIRST; i <= PM_DISK_MAX; i++) {
>  		if (!pm_disk_modes[i])
>  			continue;
>  		switch (i) {
> @@ -345,9 +372,8 @@ static ssize_t disk_show(struct subsyste
>  		case PM_DISK_TEST:
>  		case PM_DISK_TESTPROC:
>  			break;
> -		default:
> -			if (pm_ops && pm_ops->enter &&
> -			    (i == pm_ops->pm_disk_mode))
> +		case PM_DISK_PLATFORM:
> +			if (hibernate_ops)
>  				break;
>  			/* not a valid mode, continue with loop */
>  			continue;
> @@ -370,19 +396,19 @@ static ssize_t disk_store(struct subsyst
>  	int i;
>  	int len;
>  	char *p;
> -	suspend_disk_method_t mode = 0;
> +	int mode = PM_DISK_INVALID;
>  
>  	p = memchr(buf, '\n', n);
>  	len = p ? p - buf : n;
>  
>  	mutex_lock(&pm_mutex);
> -	for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) {
> +	for (i = PM_DISK_FIRST; i < PM_DISK_MAX; i++) {
>  		if (!strncmp(buf, pm_disk_modes[i], len)) {
>  			mode = i;
>  			break;
>  		}
>  	}
> -	if (mode) {
> +	if (mode != PM_DISK_INVALID) {
>  		switch (mode) {
>  		case PM_DISK_SHUTDOWN:
>  		case PM_DISK_REBOOT:
> @@ -390,19 +416,18 @@ static ssize_t disk_store(struct subsyst
>  		case PM_DISK_TESTPROC:
>  			pm_disk_mode = mode;
>  			break;
> -		default:
> -			if (pm_ops && pm_ops->enter &&
> -			    (mode == pm_ops->pm_disk_mode))
> +		case PM_DISK_PLATFORM:
> +			if (hibernate_ops)
>  				pm_disk_mode = mode;
>  			else
>  				error = -EINVAL;
>  		}
> -	} else {
> +	} else
>  		error = -EINVAL;
> -	}
>  
> -	pr_debug("PM: suspend-to-disk mode set to '%s'\n",
> -		 pm_disk_modes[mode]);
> +	if (!error)
> +		pr_debug("PM: suspend-to-disk mode set to '%s'\n",
> +			 pm_disk_modes[mode]);
>  	mutex_unlock(&pm_mutex);
>  	return error ? error : n;
>  }
> --- wireless-dev.orig/kernel/power/user.c	2007-04-26 18:15:01.130691185 +0200
> +++ wireless-dev/kernel/power/user.c	2007-04-26 18:15:09.420691185 +0200
> @@ -128,22 +128,6 @@ static ssize_t snapshot_write(struct fil
>  	return res;
>  }
>  
> -static inline int platform_prepare(void)
> -{
> -	int error = 0;
> -
> -	if (pm_ops && pm_ops->prepare)
> -		error = pm_ops->prepare(PM_SUSPEND_DISK);
> -
> -	return error;
> -}
> -
> -static inline void platform_finish(void)
> -{
> -	if (pm_ops && pm_ops->finish)
> -		pm_ops->finish(PM_SUSPEND_DISK);
> -}
> -
>  static inline int snapshot_suspend(int platform_suspend)
>  {
>  	int error;
> @@ -155,7 +139,7 @@ static inline int snapshot_suspend(int p
>  		goto Finish;
>  
>  	if (platform_suspend) {
> -		error = platform_prepare();
> +		error = hibernate_platform_prepare();
>  		if (error)
>  			goto Finish;
>  	}
> @@ -172,7 +156,7 @@ static inline int snapshot_suspend(int p
>  	enable_nonboot_cpus();
>   Resume_devices:
>  	if (platform_suspend)
> -		platform_finish();
> +		hibernate_platform_finish();
>  
>  	device_resume();
>  	resume_console();
> @@ -188,7 +172,7 @@ static inline int snapshot_restore(int p
>  	mutex_lock(&pm_mutex);
>  	pm_prepare_console();
>  	if (platform_suspend) {
> -		error = platform_prepare();
> +		error = hibernate_platform_prepare();
>  		if (error)
>  			goto Finish;
>  	}
> @@ -204,7 +188,7 @@ static inline int snapshot_restore(int p
>  	enable_nonboot_cpus();
>   Resume_devices:
>  	if (platform_suspend)
> -		platform_finish();
> +		hibernate_platform_finish();
>  
>  	device_resume();
>  	resume_console();
> @@ -406,13 +390,15 @@ static int snapshot_ioctl(struct inode *
>  		case PMOPS_ENTER:
>  			if (data->platform_suspend) {
>  				kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK);
> -				error = pm_ops->enter(PM_SUSPEND_DISK);
> +				error = hibernate_ops->enter();
> +				/* how can this possibly do the right thing? */
>  				error = 0;
>  			}
>  			break;
>  
>  		case PMOPS_FINISH:
>  			if (data->platform_suspend)
> +				/* and why doesn't this invoke anything??? */
>  				error = 0;
>  
>  			break;
> --- wireless-dev.orig/Documentation/power/userland-swsusp.txt	2007-04-26 18:15:02.120691185 +0200
> +++ wireless-dev/Documentation/power/userland-swsusp.txt	2007-04-26 18:15:09.440691185 +0200
> @@ -93,21 +93,23 @@ SNAPSHOT_S2RAM - suspend to RAM; using t
>  	to resume the system from RAM if there's enough battery power or restore
>  	its state on the basis of the saved suspend image otherwise)
>  
> -SNAPSHOT_PMOPS - enable the usage of the pmops->prepare, pmops->enter and
> -	pmops->finish methods (the in-kernel swsusp knows these as the "platform
> -	method") which are needed on many machines to (among others) speed up
> -	the resume by letting the BIOS skip some steps or to let the system
> -	recognise the correct state of the hardware after the resume (in
> -	particular on many machines this ensures that unplugged AC
> -	adapters get correctly detected and that kacpid does not run wild after
> -	the resume).  The last ioctl() argument can take one of the three
> -	values, defined in kernel/power/power.h:
> +SNAPSHOT_PMOPS - enable the usage of the hibernate_ops->prepare,
> +	hibernate_ops->enter and hibernate_ops->finish methods (the in-kernel
> +	swsusp knows these as the "platform method") which are needed on many
> +	machines to (among others) speed up the resume by letting the BIOS skip
> +	some steps or to let the system recognise the correct state of the
> +	hardware after the resume (in particular on many machines this ensures
> +	that unplugged AC adapters get correctly detected and that kacpid does
> +	not run wild after the resume).  The last ioctl() argument can take one
> +	of the three values, defined in kernel/power/power.h:
>  	PMOPS_PREPARE - make the kernel carry out the
> -		pm_ops->prepare(PM_SUSPEND_DISK) operation
> +		hibernate_ops->prepare() operation
>  	PMOPS_ENTER - make the kernel power off the system by calling
> -		pm_ops->enter(PM_SUSPEND_DISK)
> +		hibernate_ops->enter()
>  	PMOPS_FINISH - make the kernel carry out the
> -		pm_ops->finish(PM_SUSPEND_DISK) operation
> +		hibernate_ops->finish() operation
> +	Note that the actual constants are misnamed because they surface
> +	internal kernel implementation details that have changed.
>  
>  The device's read() operation can be used to transfer the snapshot image from
>  the kernel.  It has the following limitations:
> --- wireless-dev.orig/drivers/i2c/chips/tps65010.c	2007-04-26 18:15:02.150691185 +0200
> +++ wireless-dev/drivers/i2c/chips/tps65010.c	2007-04-26 18:15:09.440691185 +0200
> @@ -354,7 +354,7 @@ static void tps65010_interrupt(struct tp
>  			 * also needs to get error handling and probably
>  			 * an #ifdef CONFIG_SOFTWARE_SUSPEND
>  			 */
> -			pm_suspend(PM_SUSPEND_DISK);
> +			hibernate();
>  #endif
>  			poll = 1;
>  		}
> --- wireless-dev.orig/kernel/sys.c	2007-04-26 18:15:01.310691185 +0200
> +++ wireless-dev/kernel/sys.c	2007-04-26 18:15:09.450691185 +0200
> @@ -25,6 +25,7 @@
>  #include <linux/security.h>
>  #include <linux/dcookies.h>
>  #include <linux/suspend.h>
> +#include <linux/hibernate.h>
>  #include <linux/tty.h>
>  #include <linux/signal.h>
>  #include <linux/cn_proc.h>
> @@ -881,7 +882,7 @@ asmlinkage long sys_reboot(int magic1, i
>  #ifdef CONFIG_SOFTWARE_SUSPEND
>  	case LINUX_REBOOT_CMD_SW_SUSPEND:
>  		{
> -			int ret = pm_suspend(PM_SUSPEND_DISK);
> +			int ret = hibernate();
>  			unlock_kernel();
>  			return ret;
>  		}
> --- wireless-dev.orig/drivers/acpi/sleep/main.c	2007-04-26 18:15:02.290691185 +0200
> +++ wireless-dev/drivers/acpi/sleep/main.c	2007-04-26 18:15:09.630691185 +0200
> @@ -15,6 +15,7 @@
>  #include <linux/dmi.h>
>  #include <linux/device.h>
>  #include <linux/suspend.h>
> +#include <linux/hibernate.h>
>  #include <acpi/acpi_bus.h>
>  #include <acpi/acpi_drivers.h>
>  #include "sleep.h"
> @@ -29,7 +30,6 @@ static u32 acpi_suspend_states[] = {
>  	[PM_SUSPEND_ON] = ACPI_STATE_S0,
>  	[PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
>  	[PM_SUSPEND_MEM] = ACPI_STATE_S3,
> -	[PM_SUSPEND_DISK] = ACPI_STATE_S4,
>  	[PM_SUSPEND_MAX] = ACPI_STATE_S5
>  };
>  
> @@ -94,14 +94,6 @@ static int acpi_pm_enter(suspend_state_t
>  		do_suspend_lowlevel();
>  		break;
>  
> -	case PM_SUSPEND_DISK:
> -		if (acpi_pm_ops.pm_disk_mode == PM_DISK_PLATFORM)
> -			status = acpi_enter_sleep_state(acpi_state);
> -		break;
> -	case PM_SUSPEND_MAX:
> -		acpi_power_off();
> -		break;
> -
>  	default:
>  		return -EINVAL;
>  	}
> @@ -157,12 +149,13 @@ int acpi_suspend(u32 acpi_state)
>  	suspend_state_t states[] = {
>  		[1] = PM_SUSPEND_STANDBY,
>  		[3] = PM_SUSPEND_MEM,
> -		[4] = PM_SUSPEND_DISK,
>  		[5] = PM_SUSPEND_MAX
>  	};
>  
>  	if (acpi_state < 6 && states[acpi_state])
>  		return pm_suspend(states[acpi_state]);
> +	if (acpi_state == 4)
> +		return hibernate();
>  	return -EINVAL;
>  }
>  
> @@ -189,6 +182,71 @@ static struct pm_ops acpi_pm_ops = {
>  	.finish = acpi_pm_finish,
>  };
>  
> +#ifdef CONFIG_SOFTWARE_SUSPEND
> +static int acpi_hib_prepare(void)
> +{
> +	return acpi_sleep_prepare(ACPI_STATE_S4);
> +}
> +
> +static int acpi_hib_enter(void)
> +{
> +	acpi_status status = AE_OK;
> +	unsigned long flags = 0;
> +	u32 acpi_state = acpi_suspend_states[pm_state];
> +
> +	ACPI_FLUSH_CPU_CACHE();
> +
> +	/* Do arch specific saving of state. */
> +	int error = acpi_save_state_mem();
> +	if (error)
> +		return error;
> +
> +	local_irq_save(flags);
> +	acpi_enable_wakeup_device(acpi_state);
> +	status = acpi_enter_sleep_state(acpi_state);
> +
> +	/* ACPI 3.0 specs (P62) says that it's the responsabilty
> +	 * of the OSPM to clear the status bit [ implying that the
> +	 * POWER_BUTTON event should not reach userspace ]
> +	 */
> +	if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3))
> +		acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
> +
> +	local_irq_restore(flags);
> +	printk(KERN_DEBUG "Back to C!\n");
> +
> +	/* restore processor state
> +	 * We should only be here if we're coming back from STR or STD.
> +	 * And, in the case of the latter, the memory image should have already
> +	 * been loaded from disk.
> +	 */
> +	acpi_restore_state_mem();
> +
> +	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
> +}
> +
> +static void acpi_hib_finish(void)
> +{
> +	acpi_leave_sleep_state(ACPI_STATE_S4);
> +	acpi_disable_wakeup_device(ACPI_STATE_S4);
> +
> +	/* reset firmware waking vector */
> +	acpi_set_firmware_waking_vector((acpi_physical_address) 0);
> +
> +	if (init_8259A_after_S1) {
> +		printk("Broken toshiba laptop -> kicking interrupts\n");
> +		init_8259A(0);
> +	}
> +	return 0;
> +}
> +
> +static struct hibernate_ops acpi_hib_ops = {
> +	.prepare = acpi_hib_prepare,
> +	.enter = acpi_hib_enter,
> +	.finish = acpi_hib_finish,
> +};
> +#endif /* CONFIG_SOFTWARE_SUSPEND */
> +
>  /*
>   * Toshiba fails to preserve interrupts over S1, reinitialization
>   * of 8259 is needed after S1 resume.
> @@ -227,13 +285,16 @@ int __init acpi_sleep_init(void)
>  			sleep_states[i] = 1;
>  			printk(" S%d", i);
>  		}
> -		if (i == ACPI_STATE_S4) {
> -			if (sleep_states[i])
> -				acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM;
> -		}
>  	}
>  	printk(")\n");
>  
> +#ifdef CONFIG_SOFTWARE_SUSPEND
> +	if (sleep_states[ACPI_STATE_S4])
> +		hibernate_set_ops(&acpi_hib_ops);
> +#else
> +	sleep_states[ACPI_STATE_S4] = 0;
> +#endif
> +
>  	pm_set_ops(&acpi_pm_ops);
>  	return 0;
>  }
> --- wireless-dev.orig/kernel/power/power.h	2007-04-26 18:15:01.240691185 +0200
> +++ wireless-dev/kernel/power/power.h	2007-04-26 18:15:09.630691185 +0200
> @@ -13,16 +13,6 @@ struct swsusp_info {
>  
>  
>  
> -#ifdef CONFIG_SOFTWARE_SUSPEND
> -extern int pm_suspend_disk(void);
> -
> -#else
> -static inline int pm_suspend_disk(void)
> -{
> -	return -EPERM;
> -}
> -#endif
> -
>  extern struct mutex pm_mutex;
>  
>  #define power_attr(_name) \
> @@ -179,3 +169,6 @@ extern int suspend_enter(suspend_state_t
>  struct timeval;
>  extern void swsusp_show_speed(struct timeval *, struct timeval *,
>  				unsigned int, char *);
> +
> +extern int hibernate_platform_prepare(void);
> +extern void hibernate_platform_finish(void);
> --- wireless-dev.orig/drivers/acpi/sleep/proc.c	2007-04-26 18:15:02.720691185 +0200
> +++ wireless-dev/drivers/acpi/sleep/proc.c	2007-04-26 18:15:09.630691185 +0200
> @@ -1,6 +1,7 @@
>  #include <linux/proc_fs.h>
>  #include <linux/seq_file.h>
>  #include <linux/suspend.h>
> +#include <linux/hibernate.h>
>  #include <linux/bcd.h>
>  #include <asm/uaccess.h>
>  
> @@ -60,7 +61,7 @@ acpi_system_write_sleep(struct file *fil
>  	state = simple_strtoul(str, NULL, 0);
>  #ifdef CONFIG_SOFTWARE_SUSPEND
>  	if (state == 4) {
> -		error = pm_suspend(PM_SUSPEND_DISK);
> +		error = hibernate();
>  		goto Done;
>  	}
>  #endif
> 
> 
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
> 
> 

-- 
Rafael J. Wysocki, Ph.D.
Institute of Theoretical Physics
Faculty of Physics of Warsaw University
ul. Hoza 69, 00-681 Warsaw
[tel: +48 22 55 32 263]
[mob: +48 60 50 53 693]
----------------------------
One should not increase, beyond what is necessary,
the number of entities required to explain anything.
			-- William of Ockham
_______________________________________________
linux-pm mailing list
linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

[Index of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux