diff mbox

[1/2] hibernate: create one-way disable mode

Message ID 1402602419-27934-2-git-send-email-keescook@chromium.org (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Kees Cook June 12, 2014, 7:46 p.m. UTC
To support using kernel features that are not compatible with hibernation,
this creates a "disabled" hibernation state so that hibernation selection
can be a boot-time choice instead of only a compile-time choice.

Signed-off-by: Kees Cook <keescook@chromium.org>
---
 include/linux/suspend.h  |    2 ++
 kernel/power/hibernate.c |   55 +++++++++++++++++++++++++++++++---------------
 kernel/power/main.c      |    6 ++---
 kernel/power/user.c      |    3 +++
 4 files changed, 44 insertions(+), 22 deletions(-)

Comments

Rafael J. Wysocki June 12, 2014, 8:12 p.m. UTC | #1
On Thursday, June 12, 2014 12:46:58 PM Kees Cook wrote:
> To support using kernel features that are not compatible with hibernation,
> this creates a "disabled" hibernation state so that hibernation selection
> can be a boot-time choice instead of only a compile-time choice.

Well, hibernation_mode is only to determine how to turn the system off
after saving the image.  It doesn't say how the whole hibernation is
supposed to work (or not).

Please just extend the hibernate= command line argument to cover this.

Rafael

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index f76994b9396c..519064e0c943 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -327,6 +327,7 @@  extern unsigned long get_safe_page(gfp_t gfp_mask);
 extern void hibernation_set_ops(const struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern bool hibernation_available(void);
 asmlinkage int swsusp_save(void);
 extern struct pbe *restore_pblist;
 #else /* CONFIG_HIBERNATION */
@@ -339,6 +340,7 @@  static inline void swsusp_unset_page_free(struct page *p) {}
 static inline void hibernation_set_ops(const struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline bool hibernation_available(void) { return false; }
 #endif /* CONFIG_HIBERNATION */
 
 /* Hibernation and suspend events */
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index df88d55dc436..32672da81114 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -43,6 +43,7 @@  __visible int in_suspend __nosavedata;
 
 enum {
 	HIBERNATION_INVALID,
+	HIBERNATION_DISABLED,
 	HIBERNATION_PLATFORM,
 	HIBERNATION_SHUTDOWN,
 	HIBERNATION_REBOOT,
@@ -61,6 +62,11 @@  bool freezer_test_done;
 
 static const struct platform_hibernation_ops *hibernation_ops;
 
+bool hibernation_available(void)
+{
+	return (hibernation_mode > HIBERNATION_DISABLED);
+}
+
 /**
  * hibernation_set_ops - Set the global hibernate operations.
  * @ops: Hibernation operations to use in subsequent hibernation transitions.
@@ -75,10 +81,12 @@  void hibernation_set_ops(const struct platform_hibernation_ops *ops)
 	}
 	lock_system_sleep();
 	hibernation_ops = ops;
-	if (ops)
-		hibernation_mode = HIBERNATION_PLATFORM;
-	else if (hibernation_mode == HIBERNATION_PLATFORM)
-		hibernation_mode = HIBERNATION_SHUTDOWN;
+	if (hibernation_available()) {
+		if (ops)
+			hibernation_mode = HIBERNATION_PLATFORM;
+		else if (hibernation_mode == HIBERNATION_PLATFORM)
+			hibernation_mode = HIBERNATION_SHUTDOWN;
+	}
 
 	unlock_system_sleep();
 }
@@ -639,6 +647,11 @@  int hibernate(void)
 {
 	int error;
 
+	if (!hibernation_available()) {
+		pr_debug("PM: Hibernation not available.\n");
+		return -EINVAL;
+	}
+
 	lock_system_sleep();
 	/* The snapshot device should not be opened while we're running */
 	if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
@@ -731,7 +744,7 @@  static int software_resume(void)
 	/*
 	 * If the user said "noresume".. bail out early.
 	 */
-	if (noresume)
+	if (noresume || !hibernation_available())
 		return 0;
 
 	/*
@@ -857,6 +870,7 @@  late_initcall_sync(software_resume);
 
 
 static const char * const hibernation_modes[] = {
+	[HIBERNATION_DISABLED]	= "disabled",
 	[HIBERNATION_PLATFORM]	= "platform",
 	[HIBERNATION_SHUTDOWN]	= "shutdown",
 	[HIBERNATION_REBOOT]	= "reboot",
@@ -931,6 +945,9 @@  static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
 	char *p;
 	int mode = HIBERNATION_INVALID;
 
+	if (!hibernation_available())
+		return -EINVAL;
+
 	p = memchr(buf, '\n', n);
 	len = p ? p - buf : n;
 
@@ -942,23 +959,25 @@  static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
 			break;
 		}
 	}
-	if (mode != HIBERNATION_INVALID) {
-		switch (mode) {
-		case HIBERNATION_SHUTDOWN:
-		case HIBERNATION_REBOOT:
+	switch (mode) {
+	case HIBERNATION_DISABLED:
+	case HIBERNATION_SHUTDOWN:
+	case HIBERNATION_REBOOT:
 #ifdef CONFIG_SUSPEND
-		case HIBERNATION_SUSPEND:
+	case HIBERNATION_SUSPEND:
 #endif
+		hibernation_mode = mode;
+		break;
+	case HIBERNATION_PLATFORM:
+		if (hibernation_ops)
 			hibernation_mode = mode;
-			break;
-		case HIBERNATION_PLATFORM:
-			if (hibernation_ops)
-				hibernation_mode = mode;
-			else
-				error = -EINVAL;
-		}
-	} else
+		else
+			error = -EINVAL;
+		break;
+	default:
 		error = -EINVAL;
+		break;
+	}
 
 	if (!error)
 		pr_debug("PM: Hibernation mode set to '%s'\n",
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 573410d6647e..8e90f330f139 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -300,13 +300,11 @@  static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 			s += sprintf(s,"%s ", pm_states[i].label);
 
 #endif
-#ifdef CONFIG_HIBERNATION
-	s += sprintf(s, "%s\n", "disk");
-#else
+	if (hibernation_available())
+		s += sprintf(s, "disk ");
 	if (s != buf)
 		/* convert the last space to a newline */
 		*(s-1) = '\n';
-#endif
 	return (s - buf);
 }
 
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 98d357584cd6..000b94419182 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -49,6 +49,9 @@  static int snapshot_open(struct inode *inode, struct file *filp)
 	struct snapshot_data *data;
 	int error;
 
+	if (!hibernation_available())
+		return -EINVAL;
+
 	lock_system_sleep();
 
 	if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {