All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Huang, Ying" <ying.huang@intel.com>
To: "Eric W. Biederman" <ebiederm@xmission.com>,
	Pavel Machek <pavel@ucw.cz>,
	nigel@nigel.suspend2.net, "Rafael J. Wysocki" <rjw@sisk.pl>,
	Andrew Morton <akpm@linux-foundation.org>,
	Jeremy Maitin-Shepard <jbms@cmu.edu>
Cc: linux-pm@lists.linux-foundation.org,
	Kexec Mailing List <kexec@lists.infradead.org>,
	linux-kernel@vger.kernel.org
Subject: [PATCH 3/3 -mm] kexec based hibernation -v6: kexec hibernate/resume
Date: Mon, 19 Nov 2007 15:02:49 +0800	[thread overview]
Message-ID: <1195455769.10291.26.camel@caritas-dev.intel.com> (raw)

This patch implements kexec based hibernate/resume. This is based on
the facility provided by kexec_jump. The ACPI methods are called at
specified environment to conform the ACPI specification. Two new
reboot commands are added to trigger hibernate/resume.

Signed-off-by: Huang Ying <ying.huang@intel.com>

---
 include/linux/kexec.h   |    5 +
 include/linux/reboot.h  |    2 
 include/linux/suspend.h |    9 ++
 kernel/power/disk.c     |  155 ++++++++++++++++++++++++++++++++++++++++++++++++
 kernel/sys.c            |   42 +++++++++++++
 5 files changed, 212 insertions(+), 1 deletion(-)

--- a/kernel/power/disk.c
+++ b/kernel/power/disk.c
@@ -21,6 +21,7 @@
 #include <linux/console.h>
 #include <linux/cpu.h>
 #include <linux/freezer.h>
+#include <linux/kexec.h>
 
 #include "power.h"
 
@@ -438,6 +439,160 @@ int hibernate(void)
 	return error;
 }
 
+#ifdef CONFIG_KEXEC
+static void kexec_hibernate_power_down(void)
+{
+	switch (hibernation_mode) {
+	case HIBERNATION_TEST:
+	case HIBERNATION_TESTPROC:
+		break;
+	case HIBERNATION_REBOOT:
+		machine_restart(NULL);
+		break;
+	case HIBERNATION_PLATFORM:
+		if (!hibernation_ops)
+			break;
+		hibernation_ops->enter();
+		/* We should never get here */
+		while (1);
+		break;
+	case HIBERNATION_SHUTDOWN:
+		machine_power_off();
+		break;
+	}
+	machine_halt();
+	/*
+	 * Valid image is on the disk, if we continue we risk serious data
+	 * corruption after resume.
+	 */
+	printk(KERN_CRIT "Please power me down manually\n");
+	while (1);
+}
+
+int kexec_hibernate(struct kimage *image)
+{
+	int error;
+	int platform_mode = (hibernation_mode == HIBERNATION_PLATFORM);
+	unsigned long cmd_ret;
+
+	mutex_lock(&pm_mutex);
+
+	pm_prepare_console();
+	suspend_console();
+
+	error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
+	if (error)
+		goto Resume_console;
+
+	error = platform_start(platform_mode);
+	if (error)
+		goto Resume_console;
+
+	error = device_suspend(PMSG_FREEZE);
+	if (error)
+		goto Resume_console;
+
+	error = platform_pre_snapshot(platform_mode);
+	if (error)
+		goto Resume_devices;
+
+	error = disable_nonboot_cpus();
+	if (error)
+		goto Resume_devices;
+	local_irq_disable();
+	/* At this point, device_suspend() has been called, but *not*
+	 * device_power_down(). We *must* device_power_down() now.
+	 * Otherwise, drivers for some devices (e.g. interrupt
+	 * controllers) become desynchronized with the actual state of
+	 * the hardware at resume time, and evil weirdness ensues.
+	 */
+	error = device_power_down(PMSG_FREEZE);
+	if (error)
+		goto Enable_irqs;
+
+	save_processor_state();
+	error = machine_kexec_jump(image, &cmd_ret,
+				   KJUMP_CMD_HIBERNATE_WRITE_IMAGE);
+	restore_processor_state();
+
+	if (cmd_ret == KJUMP_CMD_HIBERNATE_POWER_DOWN)
+		kexec_hibernate_power_down();
+
+	platform_leave(platform_mode);
+
+	/* NOTE:  device_power_up() is just a resume() for devices
+	 * that suspended with irqs off ... no overall powerup.
+	 */
+	device_power_up();
+ Enable_irqs:
+	local_irq_enable();
+	enable_nonboot_cpus();
+ Resume_devices:
+	platform_finish(platform_mode);
+	device_resume();
+ Resume_console:
+	pm_notifier_call_chain(PM_POST_HIBERNATION);
+	resume_console();
+	pm_restore_console();
+	mutex_unlock(&pm_mutex);
+	return error;
+}
+
+int kexec_resume(struct kimage *image)
+{
+	int error;
+	int platform_mode = (hibernation_mode == HIBERNATION_PLATFORM);
+
+	mutex_lock(&pm_mutex);
+
+	pm_prepare_console();
+	suspend_console();
+
+	error = device_suspend(PMSG_PRETHAW);
+	if (error)
+		goto Resume_console;
+
+	error = platform_pre_restore(platform_mode);
+	if (error)
+		goto Resume_devices;
+
+	error = disable_nonboot_cpus();
+	if (error)
+		goto Resume_devices;
+	local_irq_disable();
+	/* At this point, device_suspend() has been called, but *not*
+	 * device_power_down(). We *must* device_power_down() now.
+	 * Otherwise, drivers for some devices (e.g. interrupt controllers)
+	 * become desynchronized with the actual state of the hardware
+	 * at resume time, and evil weirdness ensues.
+	 */
+	error = device_power_down(PMSG_PRETHAW);
+	if (error)
+		goto Enable_irqs;
+
+	save_processor_state();
+	error = machine_kexec_jump(image, NULL, KJUMP_CMD_HIBERNATE_RESUME);
+	restore_processor_state();
+
+	platform_leave(platform_mode);
+
+	/* NOTE:  device_power_up() is just a resume() for devices
+	 * that suspended with irqs off ... no overall powerup.
+	 */
+	device_power_up();
+ Enable_irqs:
+	local_irq_enable();
+	enable_nonboot_cpus();
+ Resume_devices:
+	platform_restore_cleanup(platform_mode);
+	device_resume();
+ Resume_console:
+	resume_console();
+	pm_restore_console();
+	mutex_unlock(&pm_mutex);
+	return error;
+}
+#endif
 
 /**
  *	software_resume - Resume from a saved image.
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -207,6 +207,15 @@ static inline void hibernation_set_ops(s
 static inline int hibernate(void) { return -ENOSYS; }
 #endif /* CONFIG_HIBERNATION */
 
+struct kimage;
+#if defined(CONFIG_HIBERNATION) && defined(CONFIG_KEXEC)
+extern int kexec_hibernate(struct kimage *image);
+extern int kexec_resume(struct kimage *image);
+#else
+static inline int kexec_hibernate(struct kimage *image) { return -ENOSYS; }
+static inline int kexec_resume(struct kimage *image) { return -ENOSYS; }
+#endif
+
 #ifdef CONFIG_PM_SLEEP
 void save_processor_state(void);
 void restore_processor_state(void);
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -321,6 +321,34 @@ static int kernel_kexec(unsigned long cm
 	return ret;
 }
 
+static int kernel_kexec_hibernate(void)
+{
+	int ret = -ENOSYS;
+#if defined(CONFIG_KEXEC) && defined(CONFIG_HIBERNATION)
+	struct kimage *image;
+	image = xchg(&kexec_image, NULL);
+	if (!image)
+		return -EINVAL;
+	ret = kexec_hibernate(image);
+	kimage_free(image);
+#endif
+	return ret;
+}
+
+static int kernel_kexec_resume(void)
+{
+	int ret = -ENOSYS;
+#if defined(CONFIG_KEXEC) && defined(CONFIG_HIBERNATION)
+	struct kimage *image;
+	image = xchg(&kexec_image, NULL);
+	if (!image)
+		return -EINVAL;
+	ret = kexec_resume(image);
+	kimage_free(image);
+#endif
+	return ret;
+}
+
 static void kernel_shutdown_prepare(enum system_states state)
 {
 	blocking_notifier_call_chain(&reboot_notifier_list,
@@ -442,6 +470,20 @@ asmlinkage long sys_reboot(int magic1, i
 		}
 #endif
 
+	case LINUX_REBOOT_CMD_KEXEC_HIBERNATE:
+		{
+			int ret = kernel_kexec_hibernate();
+			unlock_kernel();
+			return ret;
+		}
+
+	case LINUX_REBOOT_CMD_KEXEC_RESUME:
+		{
+			int ret = kernel_kexec_resume();
+			unlock_kernel();
+			return ret;
+		}
+
 	default:
 		unlock_kernel();
 		return -EINVAL;
--- a/include/linux/reboot.h
+++ b/include/linux/reboot.h
@@ -33,6 +33,8 @@
 #define	LINUX_REBOOT_CMD_RESTART2	0xA1B2C3D4
 #define	LINUX_REBOOT_CMD_SW_SUSPEND	0xD000FCE2
 #define	LINUX_REBOOT_CMD_KEXEC		0x45584543
+#define LINUX_REBOOT_CMD_KEXEC_HIBERNATE 0xE4390DF3
+#define LINUX_REBOOT_CMD_KEXEC_RESUME	0xF5A41C2E
 
 
 #ifdef __KERNEL__
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -129,7 +129,10 @@ extern int machine_kexec_jump(struct kim
 			      unsigned long cmd);
 extern int kexec_jump(struct kimage *image, unsigned long *cmd_ret,
 		      unsigned long cmd);
-#define KJUMP_CMD_NONE 0
+#define KJUMP_CMD_NONE			0x0
+#define KJUMP_CMD_HIBERNATE_WRITE_IMAGE	0x6b630001
+#define KJUMP_CMD_HIBERNATE_POWER_DOWN	0x6b630002
+#define KJUMP_CMD_HIBERNATE_RESUME	0x6b630003
 #ifdef CONFIG_COMPAT
 extern asmlinkage long compat_sys_kexec_load(unsigned long entry,
 				unsigned long nr_segments,

_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

WARNING: multiple messages have this Message-ID (diff)
From: "Huang, Ying" <ying.huang@intel.com>
To: "Eric W. Biederman" <ebiederm@xmission.com>,
	Pavel Machek <pavel@ucw.cz>,
	nigel@nigel.suspend2.net, "Rafael J. Wysocki" <rjw@sisk.pl>,
	Andrew Morton <akpm@linux-foundation.org>,
	Jeremy Maitin-Shepard <jbms@cmu.edu>
Cc: linux-kernel@vger.kernel.org,
	linux-pm@lists.linux-foundation.org,
	Kexec Mailing List <kexec@lists.infradead.org>
Subject: [PATCH 3/3 -mm] kexec based hibernation -v6: kexec hibernate/resume
Date: Mon, 19 Nov 2007 15:02:49 +0800	[thread overview]
Message-ID: <1195455769.10291.26.camel@caritas-dev.intel.com> (raw)

This patch implements kexec based hibernate/resume. This is based on
the facility provided by kexec_jump. The ACPI methods are called at
specified environment to conform the ACPI specification. Two new
reboot commands are added to trigger hibernate/resume.

Signed-off-by: Huang Ying <ying.huang@intel.com>

---
 include/linux/kexec.h   |    5 +
 include/linux/reboot.h  |    2 
 include/linux/suspend.h |    9 ++
 kernel/power/disk.c     |  155 ++++++++++++++++++++++++++++++++++++++++++++++++
 kernel/sys.c            |   42 +++++++++++++
 5 files changed, 212 insertions(+), 1 deletion(-)

--- a/kernel/power/disk.c
+++ b/kernel/power/disk.c
@@ -21,6 +21,7 @@
 #include <linux/console.h>
 #include <linux/cpu.h>
 #include <linux/freezer.h>
+#include <linux/kexec.h>
 
 #include "power.h"
 
@@ -438,6 +439,160 @@ int hibernate(void)
 	return error;
 }
 
+#ifdef CONFIG_KEXEC
+static void kexec_hibernate_power_down(void)
+{
+	switch (hibernation_mode) {
+	case HIBERNATION_TEST:
+	case HIBERNATION_TESTPROC:
+		break;
+	case HIBERNATION_REBOOT:
+		machine_restart(NULL);
+		break;
+	case HIBERNATION_PLATFORM:
+		if (!hibernation_ops)
+			break;
+		hibernation_ops->enter();
+		/* We should never get here */
+		while (1);
+		break;
+	case HIBERNATION_SHUTDOWN:
+		machine_power_off();
+		break;
+	}
+	machine_halt();
+	/*
+	 * Valid image is on the disk, if we continue we risk serious data
+	 * corruption after resume.
+	 */
+	printk(KERN_CRIT "Please power me down manually\n");
+	while (1);
+}
+
+int kexec_hibernate(struct kimage *image)
+{
+	int error;
+	int platform_mode = (hibernation_mode == HIBERNATION_PLATFORM);
+	unsigned long cmd_ret;
+
+	mutex_lock(&pm_mutex);
+
+	pm_prepare_console();
+	suspend_console();
+
+	error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
+	if (error)
+		goto Resume_console;
+
+	error = platform_start(platform_mode);
+	if (error)
+		goto Resume_console;
+
+	error = device_suspend(PMSG_FREEZE);
+	if (error)
+		goto Resume_console;
+
+	error = platform_pre_snapshot(platform_mode);
+	if (error)
+		goto Resume_devices;
+
+	error = disable_nonboot_cpus();
+	if (error)
+		goto Resume_devices;
+	local_irq_disable();
+	/* At this point, device_suspend() has been called, but *not*
+	 * device_power_down(). We *must* device_power_down() now.
+	 * Otherwise, drivers for some devices (e.g. interrupt
+	 * controllers) become desynchronized with the actual state of
+	 * the hardware at resume time, and evil weirdness ensues.
+	 */
+	error = device_power_down(PMSG_FREEZE);
+	if (error)
+		goto Enable_irqs;
+
+	save_processor_state();
+	error = machine_kexec_jump(image, &cmd_ret,
+				   KJUMP_CMD_HIBERNATE_WRITE_IMAGE);
+	restore_processor_state();
+
+	if (cmd_ret == KJUMP_CMD_HIBERNATE_POWER_DOWN)
+		kexec_hibernate_power_down();
+
+	platform_leave(platform_mode);
+
+	/* NOTE:  device_power_up() is just a resume() for devices
+	 * that suspended with irqs off ... no overall powerup.
+	 */
+	device_power_up();
+ Enable_irqs:
+	local_irq_enable();
+	enable_nonboot_cpus();
+ Resume_devices:
+	platform_finish(platform_mode);
+	device_resume();
+ Resume_console:
+	pm_notifier_call_chain(PM_POST_HIBERNATION);
+	resume_console();
+	pm_restore_console();
+	mutex_unlock(&pm_mutex);
+	return error;
+}
+
+int kexec_resume(struct kimage *image)
+{
+	int error;
+	int platform_mode = (hibernation_mode == HIBERNATION_PLATFORM);
+
+	mutex_lock(&pm_mutex);
+
+	pm_prepare_console();
+	suspend_console();
+
+	error = device_suspend(PMSG_PRETHAW);
+	if (error)
+		goto Resume_console;
+
+	error = platform_pre_restore(platform_mode);
+	if (error)
+		goto Resume_devices;
+
+	error = disable_nonboot_cpus();
+	if (error)
+		goto Resume_devices;
+	local_irq_disable();
+	/* At this point, device_suspend() has been called, but *not*
+	 * device_power_down(). We *must* device_power_down() now.
+	 * Otherwise, drivers for some devices (e.g. interrupt controllers)
+	 * become desynchronized with the actual state of the hardware
+	 * at resume time, and evil weirdness ensues.
+	 */
+	error = device_power_down(PMSG_PRETHAW);
+	if (error)
+		goto Enable_irqs;
+
+	save_processor_state();
+	error = machine_kexec_jump(image, NULL, KJUMP_CMD_HIBERNATE_RESUME);
+	restore_processor_state();
+
+	platform_leave(platform_mode);
+
+	/* NOTE:  device_power_up() is just a resume() for devices
+	 * that suspended with irqs off ... no overall powerup.
+	 */
+	device_power_up();
+ Enable_irqs:
+	local_irq_enable();
+	enable_nonboot_cpus();
+ Resume_devices:
+	platform_restore_cleanup(platform_mode);
+	device_resume();
+ Resume_console:
+	resume_console();
+	pm_restore_console();
+	mutex_unlock(&pm_mutex);
+	return error;
+}
+#endif
 
 /**
  *	software_resume - Resume from a saved image.
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -207,6 +207,15 @@ static inline void hibernation_set_ops(s
 static inline int hibernate(void) { return -ENOSYS; }
 #endif /* CONFIG_HIBERNATION */
 
+struct kimage;
+#if defined(CONFIG_HIBERNATION) && defined(CONFIG_KEXEC)
+extern int kexec_hibernate(struct kimage *image);
+extern int kexec_resume(struct kimage *image);
+#else
+static inline int kexec_hibernate(struct kimage *image) { return -ENOSYS; }
+static inline int kexec_resume(struct kimage *image) { return -ENOSYS; }
+#endif
+
 #ifdef CONFIG_PM_SLEEP
 void save_processor_state(void);
 void restore_processor_state(void);
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -321,6 +321,34 @@ static int kernel_kexec(unsigned long cm
 	return ret;
 }
 
+static int kernel_kexec_hibernate(void)
+{
+	int ret = -ENOSYS;
+#if defined(CONFIG_KEXEC) && defined(CONFIG_HIBERNATION)
+	struct kimage *image;
+	image = xchg(&kexec_image, NULL);
+	if (!image)
+		return -EINVAL;
+	ret = kexec_hibernate(image);
+	kimage_free(image);
+#endif
+	return ret;
+}
+
+static int kernel_kexec_resume(void)
+{
+	int ret = -ENOSYS;
+#if defined(CONFIG_KEXEC) && defined(CONFIG_HIBERNATION)
+	struct kimage *image;
+	image = xchg(&kexec_image, NULL);
+	if (!image)
+		return -EINVAL;
+	ret = kexec_resume(image);
+	kimage_free(image);
+#endif
+	return ret;
+}
+
 static void kernel_shutdown_prepare(enum system_states state)
 {
 	blocking_notifier_call_chain(&reboot_notifier_list,
@@ -442,6 +470,20 @@ asmlinkage long sys_reboot(int magic1, i
 		}
 #endif
 
+	case LINUX_REBOOT_CMD_KEXEC_HIBERNATE:
+		{
+			int ret = kernel_kexec_hibernate();
+			unlock_kernel();
+			return ret;
+		}
+
+	case LINUX_REBOOT_CMD_KEXEC_RESUME:
+		{
+			int ret = kernel_kexec_resume();
+			unlock_kernel();
+			return ret;
+		}
+
 	default:
 		unlock_kernel();
 		return -EINVAL;
--- a/include/linux/reboot.h
+++ b/include/linux/reboot.h
@@ -33,6 +33,8 @@
 #define	LINUX_REBOOT_CMD_RESTART2	0xA1B2C3D4
 #define	LINUX_REBOOT_CMD_SW_SUSPEND	0xD000FCE2
 #define	LINUX_REBOOT_CMD_KEXEC		0x45584543
+#define LINUX_REBOOT_CMD_KEXEC_HIBERNATE 0xE4390DF3
+#define LINUX_REBOOT_CMD_KEXEC_RESUME	0xF5A41C2E
 
 
 #ifdef __KERNEL__
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -129,7 +129,10 @@ extern int machine_kexec_jump(struct kim
 			      unsigned long cmd);
 extern int kexec_jump(struct kimage *image, unsigned long *cmd_ret,
 		      unsigned long cmd);
-#define KJUMP_CMD_NONE 0
+#define KJUMP_CMD_NONE			0x0
+#define KJUMP_CMD_HIBERNATE_WRITE_IMAGE	0x6b630001
+#define KJUMP_CMD_HIBERNATE_POWER_DOWN	0x6b630002
+#define KJUMP_CMD_HIBERNATE_RESUME	0x6b630003
 #ifdef CONFIG_COMPAT
 extern asmlinkage long compat_sys_kexec_load(unsigned long entry,
 				unsigned long nr_segments,

             reply	other threads:[~2007-11-19  6:59 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-11-19  7:02 Huang, Ying [this message]
2007-11-19  7:02 ` [PATCH 3/3 -mm] kexec based hibernation -v6: kexec hibernate/resume Huang, Ying
2007-11-19 18:22 ` Rafael J. Wysocki
2007-11-19 18:22 ` Rafael J. Wysocki
2007-11-19 18:22   ` Rafael J. Wysocki
2007-11-20  1:56   ` Huang, Ying
2007-11-20  1:56     ` Huang, Ying
2007-11-20  2:24     ` Rafael J. Wysocki
2007-11-20  2:24       ` Rafael J. Wysocki
2007-11-20  2:50       ` Huang, Ying
2007-11-20  2:50         ` Huang, Ying
2007-11-20 14:58         ` Alan Stern
2007-11-20 14:58         ` [linux-pm] " Alan Stern
2007-11-20 14:58           ` Alan Stern
2007-11-21  0:00           ` Rafael J. Wysocki
2007-11-21  0:00             ` Rafael J. Wysocki
2007-11-21  0:00           ` Rafael J. Wysocki
2007-11-21  0:00         ` Rafael J. Wysocki
2007-11-21  0:00         ` Rafael J. Wysocki
2007-11-21  0:00           ` Rafael J. Wysocki
2007-11-21  8:05           ` Huang, Ying
2007-11-21  8:05             ` Huang, Ying
2007-11-21 20:09             ` Rafael J. Wysocki
2007-11-21 20:09             ` Rafael J. Wysocki
2007-11-21 20:09               ` Rafael J. Wysocki
2007-11-21  8:05           ` Huang, Ying
2007-11-20  2:50       ` Huang, Ying
2007-11-20  2:24     ` Rafael J. Wysocki
2007-11-20  1:56   ` Huang, Ying
  -- strict thread matches above, loose matches on Subject: below --
2007-11-19  7:02 Huang, Ying

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1195455769.10291.26.camel@caritas-dev.intel.com \
    --to=ying.huang@intel.com \
    --cc=akpm@linux-foundation.org \
    --cc=ebiederm@xmission.com \
    --cc=jbms@cmu.edu \
    --cc=kexec@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@lists.linux-foundation.org \
    --cc=nigel@nigel.suspend2.net \
    --cc=pavel@ucw.cz \
    --cc=rjw@sisk.pl \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.