All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH 0/8] Suspend block api
@ 2009-04-15  1:41 Arve Hjønnevåg
  2009-04-15  1:41 ` [PATCH 1/8] PM: Add suspend " Arve Hjønnevåg
  2009-04-15 23:04 ` [RFC][PATCH 0/8] Suspend " Rafael J. Wysocki
  0 siblings, 2 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-15  1:41 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, r-woodruff2, 

This patch series adds a suspend-block api that provides the same
functionality as the android wakelock api from my last patch series,
Android PM extensions (version 3). As requested, wake_lock has been
renamed to suspend_block(er), and timeout support has been separated
into its own patch.

--
Arve Hjønnevåg

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* [PATCH 1/8] PM: Add suspend block api.
  2009-04-15  1:41 [RFC][PATCH 0/8] Suspend block api Arve Hjønnevåg
@ 2009-04-15  1:41 ` Arve Hjønnevåg
  2009-04-15  1:41   ` [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space Arve Hjønnevåg
                     ` (4 more replies)
  2009-04-15 23:04 ` [RFC][PATCH 0/8] Suspend " Rafael J. Wysocki
  1 sibling, 5 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-15  1:41 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, r-woodruff2, 

Adds /sys/power/request_state, a non-blocking interface that specifies
which suspend state to enter when no suspend blockers are active. A
special state, "on", stops the process by activating the "main" suspend
blocker.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/suspend-blockers.txt |   76 +++++++++
 include/linux/suspend_block.h            |   61 +++++++
 kernel/power/Kconfig                     |   10 ++
 kernel/power/Makefile                    |    1 +
 kernel/power/main.c                      |   62 +++++++
 kernel/power/power.h                     |    6 +
 kernel/power/suspend_block.c             |  257 ++++++++++++++++++++++++++++++
 7 files changed, 473 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/power/suspend-blockers.txt
 create mode 100755 include/linux/suspend_block.h
 create mode 100644 kernel/power/suspend_block.c

diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
new file mode 100644
index 0000000..743b870
--- /dev/null
+++ b/Documentation/power/suspend-blockers.txt
@@ -0,0 +1,76 @@
+Suspend blockers
+================
+
+A suspend_blocker prevents the system from entering suspend.
+
+If the suspend operation has already started when calling suspend_block on a
+suspend_blocker, it will abort the suspend operation as long it has not already
+reached the sysdev suspend stage. This means that calling suspend_block from an
+interrupt handler or a freezeable thread always works, but if you call
+block_suspend from a sysdev suspend handler you must also return an error from
+that handler to abort suspend.
+
+Suspend blockers can be used to allow user-space to decide which keys should
+wake the full system up and turn the screen on. Use set_irq_wake or a platform
+specific api to make sure the keypad interrupt wakes up the cpu. Once the keypad
+driver has resumed, the sequence of events can look like this:
+- The Keypad driver gets an interrupt. It then calls suspend_block on the
+  keypad-scan suspend_blocker and starts scanning the keypad matrix.
+- The keypad-scan code detects a key change and reports it to the input-event
+  driver.
+- The input-event driver sees the key change, enqueues an event, and calls
+  suspend_block on the input-event-queue suspend_blocker.
+- The keypad-scan code detects that no keys are held and calls suspend_unblock
+  on the keypad-scan suspend_blocker.
+- The user-space input-event thread returns from select/poll, calls
+  suspend_block on the process-input-events suspend_blocker and then calls read
+  on the input-event device.
+- The input-event driver dequeues the key-event and, since the queue is now
+  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
+- The user-space input-event thread returns from read. It determines that the
+  key should not wake up the full system, calls suspend_unblock on the
+  process-input-events suspend_blocker and calls select or poll.
+
+                 Key pressed   Key released
+                     |             |
+keypad-scan          ++++++++++++++++++
+input-event-queue        +++          +++
+process-input-events       +++          +++
+
+
+Driver API
+==========
+
+A driver can use the suspend block api by adding a suspend_blocker variable to
+its state and calling suspend_blocker_init. For instance:
+struct state {
+	struct suspend_blocker suspend_blocker;
+}
+
+init() {
+	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
+}
+
+Before freeing the memory, suspend_blocker_destroy must be called:
+
+uninit() {
+	suspend_blocker_destroy(&state->suspend_blocker);
+}
+
+When the driver determines that it needs to run (usually in an interrupt
+handler) it calls suspend_block:
+	suspend_block(&state->suspend_blocker);
+
+When it no longer needs to run it calls suspend_unblock:
+	suspend_unblock(&state->suspend_blocker);
+
+Calling suspend_block when the suspend blocker is active or suspend_unblock when
+it is not active has no effect. This allows drivers to update their state and
+call suspend suspend_block or suspend_unblock based on the result.
+For instance:
+
+if (list_empty(&state->pending_work))
+	suspend_unblock(&state->suspend_blocker);
+else
+	suspend_block(&state->suspend_blocker);
+
diff --git a/include/linux/suspend_block.h b/include/linux/suspend_block.h
new file mode 100755
index 0000000..7820c60
--- /dev/null
+++ b/include/linux/suspend_block.h
@@ -0,0 +1,61 @@
+/* include/linux/suspend_block.h
+ *
+ * Copyright (C) 2007-2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SUSPEND_BLOCK_H
+#define _LINUX_SUSPEND_BLOCK_H
+
+/* A suspend_blocker prevents the system from entering suspend when active.
+ */
+
+struct suspend_blocker {
+#ifdef CONFIG_SUSPEND_BLOCK
+	atomic_t            flags;
+	const char         *name;
+#endif
+};
+
+#ifdef CONFIG_SUSPEND_BLOCK
+
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
+void suspend_blocker_destroy(struct suspend_blocker *blocker);
+void suspend_block(struct suspend_blocker *blocker);
+void suspend_unblock(struct suspend_blocker *blocker);
+
+/* is_blocking_suspend returns true if the suspend_blocker is currently active.
+ */
+bool is_blocking_suspend(struct suspend_blocker *blocker);
+
+/* suspend_is_blocked can be used by generic power management code to abort
+ * suspend.
+ *
+ * To preserve backward compatibility suspend_is_blocked returns 0 unless it
+ * is called during suspend initiated from the suspend_block code.
+ */
+bool suspend_is_blocked(void);
+
+#else
+
+static inline void suspend_blocker_init(struct suspend_blocker *blocker,
+					const char *name) {}
+static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
+static inline void suspend_block(struct suspend_blocker *blocker) {}
+static inline void suspend_unblock(struct suspend_blocker *blocker) {}
+static inline bool is_blocking_suspend(struct suspend_blocker *bl) { return 0; }
+static inline bool suspend_is_blocked(void) { return 0; }
+
+#endif
+
+#endif
+
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 23bd4da..9d1df13 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -116,6 +116,16 @@ config SUSPEND_FREEZER
 
 	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
 
+config SUSPEND_BLOCK
+	bool "Suspend block"
+	depends on PM
+	select RTC_LIB
+	default n
+	---help---
+	  Enable suspend_block. When user space requests a sleep state through
+	  /sys/power/request_state, the requested sleep state will be entered
+	  when no suspend_blockers are active.
+
 config HIBERNATION
 	bool "Hibernation (aka 'suspend to disk')"
 	depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 720ea4f..29cdc9e 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -6,6 +6,7 @@ endif
 obj-$(CONFIG_PM)		+= main.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
+obj-$(CONFIG_SUSPEND_BLOCK)	+= suspend_block.o
 obj-$(CONFIG_HIBERNATION)	+= swsusp.o disk.o snapshot.o swap.o user.o
 
 obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
diff --git a/kernel/power/main.c b/kernel/power/main.c
index c9632f8..e4c6b20 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -22,6 +22,7 @@
 #include <linux/freezer.h>
 #include <linux/vmstat.h>
 #include <linux/syscalls.h>
+#include <linux/suspend_block.h>
 
 #include "power.h"
 
@@ -392,6 +393,9 @@ static void suspend_finish(void)
 
 
 static const char * const pm_states[PM_SUSPEND_MAX] = {
+#ifdef CONFIG_SUSPEND_BLOCK
+	[PM_SUSPEND_ON]		= "on",
+#endif
 	[PM_SUSPEND_STANDBY]	= "standby",
 	[PM_SUSPEND_MEM]	= "mem",
 };
@@ -540,6 +544,61 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 
 power_attr(state);
 
+/**
+ *	request_state - control system power state.
+ *
+ *	This is similar to state, but it does not block until the system
+ *	resumes, and it will try to re-enter the state until another state is
+ *	requested. Suspend blockers are respected and the requested state will
+ *	only be entered when no suspend blockers are active.
+ *	Write "on" to cancel.
+ */
+
+#ifdef CONFIG_SUSPEND_BLOCK
+static ssize_t request_state_show(struct kobject *kobj,
+				  struct kobj_attribute *attr, char *buf)
+{
+	char *s = buf;
+	int i;
+
+	for (i = 0; i < PM_SUSPEND_MAX; i++) {
+		if (pm_states[i] && (i == PM_SUSPEND_ON || valid_state(i)))
+			s += sprintf(s, "%s ", pm_states[i]);
+	}
+	if (s != buf)
+		/* convert the last space to a newline */
+		*(s-1) = '\n';
+	return (s - buf);
+}
+
+static ssize_t request_state_store(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   const char *buf, size_t n)
+{
+	suspend_state_t state = PM_SUSPEND_ON;
+	const char * const *s;
+	char *p;
+	int len;
+	int error = -EINVAL;
+
+	p = memchr(buf, '\n', n);
+	len = p ? p - buf : n;
+
+	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
+		if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
+			break;
+	}
+	if (state < PM_SUSPEND_MAX && *s)
+		if (state == PM_SUSPEND_ON || valid_state(state)) {
+			error = 0;
+			request_suspend_state(state);
+		}
+	return error ? error : n;
+}
+
+power_attr(request_state);
+#endif /* CONFIG_SUSPEND_BLOCK */
+
 #ifdef CONFIG_PM_TRACE
 int pm_trace_enabled;
 
@@ -567,6 +626,9 @@ power_attr(pm_trace);
 
 static struct attribute * g[] = {
 	&state_attr.attr,
+#ifdef CONFIG_SUSPEND_BLOCK
+	&request_state_attr.attr,
+#endif
 #ifdef CONFIG_PM_TRACE
 	&pm_trace_attr.attr,
 #endif
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 46b5ec7..2414a74 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -223,3 +223,9 @@ static inline void suspend_thaw_processes(void)
 {
 }
 #endif
+
+#ifdef CONFIG_SUSPEND_BLOCK
+/* kernel/power/suspend_block.c */
+void request_suspend_state(suspend_state_t state);
+#endif
+
diff --git a/kernel/power/suspend_block.c b/kernel/power/suspend_block.c
new file mode 100644
index 0000000..b4f2191
--- /dev/null
+++ b/kernel/power/suspend_block.c
@@ -0,0 +1,257 @@
+/* kernel/power/suspend_block.c
+ *
+ * Copyright (C) 2005-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/suspend.h>
+#include <linux/suspend_block.h>
+#include <linux/sysdev.h>
+#include "power.h"
+
+enum {
+	DEBUG_EXIT_SUSPEND = 1U << 0,
+	DEBUG_WAKEUP = 1U << 1,
+	DEBUG_USER_STATE = 1U << 2,
+	DEBUG_SUSPEND = 1U << 3,
+	DEBUG_SUSPEND_BLOCKER = 1U << 4,
+};
+static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SB_INITIALIZED            (1U << 8)
+#define SB_ACTIVE                 (1U << 9)
+
+static DEFINE_SPINLOCK(state_lock);
+static atomic_t suspend_block_count;
+static atomic_t current_event_num;
+struct workqueue_struct *suspend_work_queue;
+struct suspend_blocker main_suspend_blocker;
+static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
+static bool enable_suspend_blockers;
+
+bool suspend_is_blocked(void)
+{
+	if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
+		return 0;
+	return !!atomic_read(&suspend_block_count);
+}
+
+static void suspend_worker(struct work_struct *work)
+{
+	int ret;
+	int entry_event_num;
+
+	enable_suspend_blockers = 1;
+
+retry:
+	if (suspend_is_blocked()) {
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("suspend: abort suspend\n");
+		goto abort;
+	}
+
+	entry_event_num = atomic_read(&current_event_num);
+	if (debug_mask & DEBUG_SUSPEND)
+		pr_info("suspend: enter suspend\n");
+	ret = pm_suspend(requested_suspend_state);
+	if (debug_mask & DEBUG_EXIT_SUSPEND) {
+		struct timespec ts;
+		struct rtc_time tm;
+		getnstimeofday(&ts);
+		rtc_time_to_tm(ts.tv_sec, &tm);
+		pr_info("suspend: exit suspend, ret = %d "
+			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+	}
+	if (atomic_read(&current_event_num) == entry_event_num) {
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("suspend: pm_suspend returned with no event\n");
+		goto retry;
+	}
+abort:
+	enable_suspend_blockers = 0;
+}
+static DECLARE_WORK(suspend_work, suspend_worker);
+
+static int suspend_block_suspend(struct sys_device *dev, pm_message_t state)
+{
+	int ret = suspend_is_blocked() ? -EAGAIN : 0;
+	if (debug_mask & DEBUG_SUSPEND)
+		pr_info("suspend_block_suspend return %d\n", ret);
+	return ret;
+}
+
+static struct sysdev_class suspend_block_sysclass = {
+	.name = "suspend_block",
+	.suspend = suspend_block_suspend,
+};
+static struct sys_device suspend_block_sysdev = {
+	.cls = &suspend_block_sysclass,
+};
+
+/**
+ * suspend_blocker_init() - Initialize a suspend blocker
+ * @blocker:	The suspend blocker to initialize.
+ * @name:	The name of the suspend blocker to show in
+ *		/proc/suspend_blockers
+ */
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
+{
+	if (name)
+		blocker->name = name;
+	BUG_ON(!blocker->name);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_blocker_init name=%s\n", blocker->name);
+
+	atomic_set(&blocker->flags, SB_INITIALIZED);
+}
+EXPORT_SYMBOL(suspend_blocker_init);
+
+/**
+ * suspend_blocker_destroy() - Destroy a suspend blocker
+ * @blocker:	The suspend blocker to destroy.
+ */
+void suspend_blocker_destroy(struct suspend_blocker *blocker)
+{
+	int flags;
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
+	flags = atomic_xchg(&blocker->flags, 0);
+	WARN(!(flags & SB_INITIALIZED), "suspend_blocker_destroy called on "
+					"uninitialized suspend_blocker\n");
+	if (flags == (SB_INITIALIZED | SB_ACTIVE))
+		if (atomic_dec_and_test(&suspend_block_count))
+			queue_work(suspend_work_queue, &suspend_work);
+}
+EXPORT_SYMBOL(suspend_blocker_destroy);
+
+/**
+ * suspend_block() - Block suspend
+ * @blocker:	The suspend blocker to use
+ */
+void suspend_block(struct suspend_blocker *blocker)
+{
+	BUG_ON(!(atomic_read(&blocker->flags) & SB_INITIALIZED));
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_block: %s\n", blocker->name);
+	if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED,
+	    SB_INITIALIZED | SB_ACTIVE) == SB_INITIALIZED)
+		atomic_inc(&suspend_block_count);
+
+	atomic_inc(&current_event_num);
+}
+EXPORT_SYMBOL(suspend_block);
+
+/**
+ * suspend_unblock() - Unblock suspend
+ * @blocker:	The suspend blocker to unblock.
+ *
+ * If no other suspend blockers block suspend, the system will suspend.
+ */
+void suspend_unblock(struct suspend_blocker *blocker)
+{
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_unblock: %s\n", blocker->name);
+
+	if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED | SB_ACTIVE,
+	    SB_INITIALIZED) == (SB_INITIALIZED | SB_ACTIVE))
+		if (atomic_dec_and_test(&suspend_block_count))
+			queue_work(suspend_work_queue, &suspend_work);
+}
+EXPORT_SYMBOL(suspend_unblock);
+
+/**
+ * is_blocking_suspend() - Test if a suspend blocker is blocking suspend
+ * @blocker:	The suspend blocker to check.
+ */
+bool is_blocking_suspend(struct suspend_blocker *blocker)
+{
+	return !!(atomic_read(&blocker->flags) & SB_ACTIVE);
+}
+EXPORT_SYMBOL(is_blocking_suspend);
+
+void request_suspend_state(suspend_state_t state)
+{
+	unsigned long irqflags;
+	spin_lock_irqsave(&state_lock, irqflags);
+	if (debug_mask & DEBUG_USER_STATE) {
+		struct timespec ts;
+		struct rtc_time tm;
+		getnstimeofday(&ts);
+		rtc_time_to_tm(ts.tv_sec, &tm);
+		pr_info("request_suspend_state: %s (%d->%d) at %lld "
+			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
+			state != PM_SUSPEND_ON ? "sleep" : "wakeup",
+			requested_suspend_state, state,
+			ktime_to_ns(ktime_get()),
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+	}
+	requested_suspend_state = state;
+	if (state == PM_SUSPEND_ON)
+		suspend_block(&main_suspend_blocker);
+	else
+		suspend_unblock(&main_suspend_blocker);
+	spin_unlock_irqrestore(&state_lock, irqflags);
+}
+
+static int __init suspend_block_init(void)
+{
+	int ret;
+
+	suspend_blocker_init(&main_suspend_blocker, "main");
+	suspend_block(&main_suspend_blocker);
+
+	ret = sysdev_class_register(&suspend_block_sysclass);
+	if (ret) {
+		pr_err("suspend_block_init: sysdev_class_register failed\n");
+		goto err_sysdev_class_register;
+	}
+	ret = sysdev_register(&suspend_block_sysdev);
+	if (ret) {
+		pr_err("suspend_block_init: suspend_block_sysdev failed\n");
+		goto err_sysdev_register;
+	}
+
+	suspend_work_queue = create_singlethread_workqueue("suspend");
+	if (suspend_work_queue == NULL) {
+		ret = -ENOMEM;
+		goto err_suspend_work_queue;
+	}
+
+	return 0;
+
+err_suspend_work_queue:
+	sysdev_unregister(&suspend_block_sysdev);
+err_sysdev_register:
+	sysdev_class_unregister(&suspend_block_sysclass);
+err_sysdev_class_register:
+	suspend_blocker_destroy(&main_suspend_blocker);
+	return ret;
+}
+
+static void  __exit suspend_block_exit(void)
+{
+	destroy_workqueue(suspend_work_queue);
+	sysdev_unregister(&suspend_block_sysdev);
+	sysdev_class_unregister(&suspend_block_sysclass);
+	suspend_blocker_destroy(&main_suspend_blocker);
+}
+
+core_initcall(suspend_block_init);
+module_exit(suspend_block_exit);
-- 
1.6.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space
  2009-04-15  1:41 ` [PATCH 1/8] PM: Add suspend " Arve Hjønnevåg
@ 2009-04-15  1:41   ` Arve Hjønnevåg
  2009-04-15  1:41     ` [PATCH 3/8] PM: suspend_block: Abort task freezing if a suspend_blocker is active Arve Hjønnevåg
  2009-04-29 22:52     ` [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space Rafael J. Wysocki
  2009-04-15 15:29   ` [PATCH 1/8] PM: Add suspend block api Alan Stern
                     ` (3 subsequent siblings)
  4 siblings, 2 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-15  1:41 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, r-woodruff2, 

Adds ioctls to create a suspend_blocker, and to block and unblock suspend.
To delete the suspend_blocker, close the device.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/suspend-blockers.txt |   17 ++++
 include/linux/suspend_block_dev.h        |   25 ++++++
 kernel/power/Kconfig                     |    9 ++
 kernel/power/Makefile                    |    1 +
 kernel/power/user_suspend_block.c        |  125 ++++++++++++++++++++++++++++++
 5 files changed, 177 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/suspend_block_dev.h
 create mode 100644 kernel/power/user_suspend_block.c

diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
index 743b870..35bc713 100644
--- a/Documentation/power/suspend-blockers.txt
+++ b/Documentation/power/suspend-blockers.txt
@@ -74,3 +74,20 @@ if (list_empty(&state->pending_work))
 else
 	suspend_block(&state->suspend_blocker);
 
+User-space API
+==============
+
+To create a suspend_blocker from user-space, open the suspend_blocker device:
+    fd = open("/dev/suspend_blocker", O_RDWR | O_CLOEXEC);
+then call:
+    ioctl(fd, SUSPEND_BLOCKER_IOCTL_INIT(strlen(name)), name);
+
+To activate a suspend_blocker call:
+    ioctl(fd, SUSPEND_BLOCKER_IOCTL_BLOCK);
+
+To unblock call:
+    ioctl(fd, SUSPEND_BLOCKER_IOCTL_UNBLOCK);
+
+To destroy the suspend_blocker, close the device:
+    close(fd);
+
diff --git a/include/linux/suspend_block_dev.h b/include/linux/suspend_block_dev.h
new file mode 100644
index 0000000..a7c0bf9
--- /dev/null
+++ b/include/linux/suspend_block_dev.h
@@ -0,0 +1,25 @@
+/* include/linux/suspend_block_dev.h
+ *
+ * Copyright (C) 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SUSPEND_BLOCK_DEV_H
+#define _LINUX_SUSPEND_BLOCK_DEV_H
+
+#include <linux/ioctl.h>
+
+#define SUSPEND_BLOCKER_IOCTL_INIT(len)		_IOC(_IOC_WRITE, 's', 0, len)
+#define SUSPEND_BLOCKER_IOCTL_BLOCK		_IO('s', 1)
+#define SUSPEND_BLOCKER_IOCTL_UNBLOCK		_IO('s', 3)
+
+#endif
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 9d1df13..2cf4c4d 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -126,6 +126,15 @@ config SUSPEND_BLOCK
 	  /sys/power/request_state, the requested sleep state will be entered
 	  when no suspend_blockers are active.
 
+config USER_SUSPEND_BLOCK
+	bool "Userspace suspend blockers"
+	depends on SUSPEND_BLOCK
+	default y
+	---help---
+	  User-space suspend block api. Creates a misc device with ioctls
+	  to create, block and unblock a suspend_blocker. The suspend_blocker
+	  will be deleted when the device is closed.
+
 config HIBERNATION
 	bool "Hibernation (aka 'suspend to disk')"
 	depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 29cdc9e..5c524f1 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_SUSPEND_BLOCK)	+= suspend_block.o
+obj-$(CONFIG_USER_SUSPEND_BLOCK)	+= user_suspend_block.o
 obj-$(CONFIG_HIBERNATION)	+= swsusp.o disk.o snapshot.o swap.o user.o
 
 obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
diff --git a/kernel/power/user_suspend_block.c b/kernel/power/user_suspend_block.c
new file mode 100644
index 0000000..782407c
--- /dev/null
+++ b/kernel/power/user_suspend_block.c
@@ -0,0 +1,125 @@
+/* kernel/power/user_suspend_block.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/suspend_block.h>
+#include <linux/suspend_block_dev.h>
+
+enum {
+	DEBUG_FAILURE	= BIT(0),
+};
+static int debug_mask = DEBUG_FAILURE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static DEFINE_MUTEX(ioctl_lock);
+
+struct user_suspend_blocker {
+	struct suspend_blocker	blocker;
+	char			name[0];
+};
+
+static int create_user_suspend_blocker(struct file *file, void __user *name,
+				 size_t name_len)
+{
+	struct user_suspend_blocker *bl;
+	if (file->private_data)
+		return -EBUSY;
+	bl = kzalloc(sizeof(*bl) + name_len + 1, GFP_KERNEL);
+	if (!bl)
+		return -ENOMEM;
+	if (copy_from_user(bl->name, name, name_len))
+		goto err_fault;
+	suspend_blocker_init(&bl->blocker, bl->name);
+	file->private_data = bl;
+	return 0;
+
+err_fault:
+	kfree(bl);
+	return -EFAULT;
+}
+
+static long user_suspend_blocker_ioctl(struct file *file, unsigned int cmd,
+				unsigned long _arg)
+{
+	void __user *arg = (void __user *)_arg;
+	struct user_suspend_blocker *bl;
+	long ret;
+
+	mutex_lock(&ioctl_lock);
+	if ((cmd & ~IOCSIZE_MASK) == SUSPEND_BLOCKER_IOCTL_INIT(0)) {
+		ret = create_user_suspend_blocker(file, arg, _IOC_SIZE(cmd));
+		goto done;
+	}
+	bl = file->private_data;
+	if (!bl) {
+		ret = -ENOENT;
+		goto done;
+	}
+	switch (cmd) {
+	case SUSPEND_BLOCKER_IOCTL_BLOCK:
+		suspend_block(&bl->blocker);
+		ret = 0;
+		break;
+	case SUSPEND_BLOCKER_IOCTL_UNBLOCK:
+		suspend_unblock(&bl->blocker);
+		ret = 0;
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+done:
+	if (ret && debug_mask & DEBUG_FAILURE)
+		pr_err("user_suspend_blocker_ioctl: cmd %x failed, %ld\n",
+			cmd, ret);
+	mutex_unlock(&ioctl_lock);
+	return ret;
+}
+
+static int user_suspend_blocker_release(struct inode *inode, struct file *file)
+{
+	struct user_suspend_blocker *bl = file->private_data;
+	if (!bl)
+		return 0;
+	suspend_blocker_destroy(&bl->blocker);
+	kfree(bl);
+	return 0;
+}
+
+const struct file_operations user_suspend_blocker_fops = {
+	.release = user_suspend_blocker_release,
+	.unlocked_ioctl = user_suspend_blocker_ioctl,
+};
+
+struct miscdevice user_suspend_blocker_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "suspend_blocker",
+	.fops = &user_suspend_blocker_fops,
+};
+
+static int __init user_suspend_blocker_init(void)
+{
+	return misc_register(&user_suspend_blocker_device);
+}
+
+static void __exit user_suspend_blocker_exit(void)
+{
+	misc_deregister(&user_suspend_blocker_device);
+}
+
+module_init(user_suspend_blocker_init);
+module_exit(user_suspend_blocker_exit);
-- 
1.6.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* [PATCH 3/8] PM: suspend_block: Abort task freezing if a suspend_blocker is active.
  2009-04-15  1:41   ` [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space Arve Hjønnevåg
@ 2009-04-15  1:41     ` Arve Hjønnevåg
  2009-04-15  1:41       ` [PATCH 4/8] Input: Block suspend while event queue is not empty Arve Hjønnevåg
  2009-04-29 22:56       ` [PATCH 3/8] PM: suspend_block: Abort task freezing if a suspend_blocker is active Rafael J. Wysocki
  2009-04-29 22:52     ` [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space Rafael J. Wysocki
  1 sibling, 2 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-15  1:41 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, r-woodruff2, 

If a suspend_blocker is active, suspend will fail anyway. Since
try_to_freeze_tasks can take up to 20 seconds to complete or fail, aborting
as soon as someone blocks suspend (e.g. from an interrupt handler) improves
the worst case wakeup latency.

On an older kernel where task freezing could fail for processes attached
to a debugger, this fixed a problem where the device sometimes hung for
20 seconds before the screen turned on.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 kernel/power/process.c |   23 ++++++++++++++++++-----
 1 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/kernel/power/process.c b/kernel/power/process.c
index ca63401..76d7cdb 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/syscalls.h>
 #include <linux/freezer.h>
+#include <linux/suspend_block.h>
 
 /* 
  * Timeout for stopping processes
@@ -36,6 +37,7 @@ static int try_to_freeze_tasks(bool sig_only)
 	struct timeval start, end;
 	u64 elapsed_csecs64;
 	unsigned int elapsed_csecs;
+	unsigned int wakeup = 0;
 
 	do_gettimeofday(&start);
 
@@ -62,6 +64,10 @@ static int try_to_freeze_tasks(bool sig_only)
 		} while_each_thread(g, p);
 		read_unlock(&tasklist_lock);
 		yield();			/* Yield is okay here */
+		if (todo && suspend_is_blocked()) {
+			wakeup = 1;
+			break;
+		}
 		if (time_after(jiffies, end_time))
 			break;
 	} while (todo);
@@ -77,11 +83,18 @@ static int try_to_freeze_tasks(bool sig_only)
 		 * and caller must call thaw_processes() if something fails),
 		 * but it cleans up leftover PF_FREEZE requests.
 		 */
-		printk("\n");
-		printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds "
-				"(%d tasks refusing to freeze):\n",
-				elapsed_csecs / 100, elapsed_csecs % 100, todo);
-		show_state();
+		if (wakeup) {
+			printk("\n");
+			printk(KERN_ERR "Freezing of %s aborted\n",
+					sig_only ? "user space " : "tasks ");
+		} else {
+			printk("\n");
+			printk(KERN_ERR
+			       "Freezing of tasks failed after %d.%02d seconds "
+			       "(%d tasks refusing to freeze):\n",
+			       elapsed_csecs / 100, elapsed_csecs % 100, todo);
+			show_state();
+		}
 		read_lock(&tasklist_lock);
 		do_each_thread(g, p) {
 			task_lock(p);
-- 
1.6.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* [PATCH 4/8] Input: Block suspend while event queue is not empty.
  2009-04-15  1:41     ` [PATCH 3/8] PM: suspend_block: Abort task freezing if a suspend_blocker is active Arve Hjønnevåg
@ 2009-04-15  1:41       ` Arve Hjønnevåg
  2009-04-15  1:41         ` [PATCH 5/8] PM: suspend_block: Switch to list of active and inactive suspend blockers Arve Hjønnevåg
  2009-04-29 22:56       ` [PATCH 3/8] PM: suspend_block: Abort task freezing if a suspend_blocker is active Rafael J. Wysocki
  1 sibling, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-15  1:41 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, r-woodruff2, 

Add an ioctl, EVIOCSSUSPENDBLOCK, to enables a suspend_blocker that will block
suspend while the event queue is not empty. This allows userspace code to
process input events while the device appears to be asleep.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 drivers/input/evdev.c |   21 +++++++++++++++++++++
 include/linux/input.h |    3 +++
 2 files changed, 24 insertions(+), 0 deletions(-)

diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index ed8baa0..3a61425 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -19,6 +19,7 @@
 #include <linux/input.h>
 #include <linux/major.h>
 #include <linux/device.h>
+#include <linux/suspend_block.h>
 #include "input-compat.h"
 
 struct evdev {
@@ -43,6 +44,8 @@ struct evdev_client {
 	struct fasync_struct *fasync;
 	struct evdev *evdev;
 	struct list_head node;
+	int use_suspend_blocker;
+	struct suspend_blocker suspend_blocker;
 };
 
 static struct evdev *evdev_table[EVDEV_MINORS];
@@ -55,6 +58,8 @@ static void evdev_pass_event(struct evdev_client *client,
 	 * Interrupts are disabled, just acquire the lock
 	 */
 	spin_lock(&client->buffer_lock);
+	if (client->use_suspend_blocker)
+		suspend_block(&client->suspend_blocker);
 	client->buffer[client->head++] = *event;
 	client->head &= EVDEV_BUFFER_SIZE - 1;
 	spin_unlock(&client->buffer_lock);
@@ -236,6 +241,8 @@ static int evdev_release(struct inode *inode, struct file *file)
 	mutex_unlock(&evdev->mutex);
 
 	evdev_detach_client(evdev, client);
+	if (client->use_suspend_blocker)
+		suspend_blocker_destroy(&client->suspend_blocker);
 	kfree(client);
 
 	evdev_close_device(evdev);
@@ -335,6 +342,8 @@ static int evdev_fetch_next_event(struct evdev_client *client,
 	if (have_event) {
 		*event = client->buffer[client->tail++];
 		client->tail &= EVDEV_BUFFER_SIZE - 1;
+		if (client->use_suspend_blocker && client->head == client->tail)
+			suspend_unblock(&client->suspend_blocker);
 	}
 
 	spin_unlock_irq(&client->buffer_lock);
@@ -585,6 +594,18 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
 		else
 			return evdev_ungrab(evdev, client);
 
+	case EVIOCGSUSPENDBLOCK:
+		return put_user(EV_VERSION, ip);
+
+	case EVIOCSSUSPENDBLOCK:
+		spin_lock_irq(&client->buffer_lock);
+		if ((client->use_suspend_blocker = !!p))
+			suspend_blocker_init(&client->suspend_blocker, "evdev");
+		else
+			suspend_blocker_destroy(&client->suspend_blocker);
+		spin_unlock_irq(&client->buffer_lock);
+		return 0;
+
 	default:
 
 		if (_IOC_TYPE(cmd) != 'E')
diff --git a/include/linux/input.h b/include/linux/input.h
index 1249a0c..67e2332 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -81,6 +81,9 @@ struct input_absinfo {
 
 #define EVIOCGRAB		_IOW('E', 0x90, int)			/* Grab/Release device */
 
+#define EVIOCGSUSPENDBLOCK	_IOR('E', 0x91, int)			/* get suspend block enable */
+#define EVIOCSSUSPENDBLOCK	_IOW('E', 0x91, int)			/* set suspend block enable */
+
 /*
  * Event types
  */
-- 
1.6.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* [PATCH 5/8] PM: suspend_block: Switch to list of active and inactive suspend blockers
  2009-04-15  1:41       ` [PATCH 4/8] Input: Block suspend while event queue is not empty Arve Hjønnevåg
@ 2009-04-15  1:41         ` Arve Hjønnevåg
  2009-04-15  1:41           ` [PATCH 6/8] PM: suspend_block: Add suspend_blocker stats Arve Hjønnevåg
  0 siblings, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-15  1:41 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, r-woodruff2, 

This allows active suspend blockers to be listed

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 include/linux/suspend_block.h |    5 ++-
 kernel/power/suspend_block.c  |   77 ++++++++++++++++++++++++++++------------
 2 files changed, 58 insertions(+), 24 deletions(-)

diff --git a/include/linux/suspend_block.h b/include/linux/suspend_block.h
index 7820c60..73ac5f7 100755
--- a/include/linux/suspend_block.h
+++ b/include/linux/suspend_block.h
@@ -16,12 +16,15 @@
 #ifndef _LINUX_SUSPEND_BLOCK_H
 #define _LINUX_SUSPEND_BLOCK_H
 
+#include <linux/list.h>
+
 /* A suspend_blocker prevents the system from entering suspend when active.
  */
 
 struct suspend_blocker {
 #ifdef CONFIG_SUSPEND_BLOCK
-	atomic_t            flags;
+	struct list_head    link;
+	int                 flags;
 	const char         *name;
 #endif
 };
diff --git a/kernel/power/suspend_block.c b/kernel/power/suspend_block.c
index b4f2191..697f52a 100644
--- a/kernel/power/suspend_block.c
+++ b/kernel/power/suspend_block.c
@@ -33,19 +33,29 @@ module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
 #define SB_INITIALIZED            (1U << 8)
 #define SB_ACTIVE                 (1U << 9)
 
+static DEFINE_SPINLOCK(list_lock);
 static DEFINE_SPINLOCK(state_lock);
-static atomic_t suspend_block_count;
-static atomic_t current_event_num;
+static LIST_HEAD(inactive_blockers);
+static LIST_HEAD(active_blockers);
+static int current_event_num;
 struct workqueue_struct *suspend_work_queue;
 struct suspend_blocker main_suspend_blocker;
 static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
 static bool enable_suspend_blockers;
 
+static void print_active_blockers_locked(void)
+{
+	struct suspend_blocker *blocker;
+
+	list_for_each_entry(blocker, &active_blockers, link)
+		pr_info("active suspend blocker %s\n", blocker->name);
+}
+
 bool suspend_is_blocked(void)
 {
 	if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
 		return 0;
-	return !!atomic_read(&suspend_block_count);
+	return !list_empty(&active_blockers);
 }
 
 static void suspend_worker(struct work_struct *work)
@@ -62,7 +72,7 @@ retry:
 		goto abort;
 	}
 
-	entry_event_num = atomic_read(&current_event_num);
+	entry_event_num = current_event_num;
 	if (debug_mask & DEBUG_SUSPEND)
 		pr_info("suspend: enter suspend\n");
 	ret = pm_suspend(requested_suspend_state);
@@ -76,7 +86,7 @@ retry:
 			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
 			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
 	}
-	if (atomic_read(&current_event_num) == entry_event_num) {
+	if (current_event_num == entry_event_num) {
 		if (debug_mask & DEBUG_SUSPEND)
 			pr_info("suspend: pm_suspend returned with no event\n");
 		goto retry;
@@ -110,6 +120,8 @@ static struct sys_device suspend_block_sysdev = {
  */
 void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
 {
+	unsigned long irqflags = 0;
+
 	if (name)
 		blocker->name = name;
 	BUG_ON(!blocker->name);
@@ -117,7 +129,12 @@ void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
 	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
 		pr_info("suspend_blocker_init name=%s\n", blocker->name);
 
-	atomic_set(&blocker->flags, SB_INITIALIZED);
+	blocker->flags = SB_INITIALIZED;
+	INIT_LIST_HEAD(&blocker->link);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	list_add(&blocker->link, &inactive_blockers);
+	spin_unlock_irqrestore(&list_lock, irqflags);
 }
 EXPORT_SYMBOL(suspend_blocker_init);
 
@@ -127,15 +144,15 @@ EXPORT_SYMBOL(suspend_blocker_init);
  */
 void suspend_blocker_destroy(struct suspend_blocker *blocker)
 {
-	int flags;
+	unsigned long irqflags;
 	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
 		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
-	flags = atomic_xchg(&blocker->flags, 0);
-	WARN(!(flags & SB_INITIALIZED), "suspend_blocker_destroy called on "
-					"uninitialized suspend_blocker\n");
-	if (flags == (SB_INITIALIZED | SB_ACTIVE))
-		if (atomic_dec_and_test(&suspend_block_count))
-			queue_work(suspend_work_queue, &suspend_work);
+	spin_lock_irqsave(&list_lock, irqflags);
+	blocker->flags &= ~SB_INITIALIZED;
+	list_del(&blocker->link);
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(suspend_work_queue, &suspend_work);
+	spin_unlock_irqrestore(&list_lock, irqflags);
 }
 EXPORT_SYMBOL(suspend_blocker_destroy);
 
@@ -145,15 +162,19 @@ EXPORT_SYMBOL(suspend_blocker_destroy);
  */
 void suspend_block(struct suspend_blocker *blocker)
 {
-	BUG_ON(!(atomic_read(&blocker->flags) & SB_INITIALIZED));
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	BUG_ON(!(blocker->flags & SB_INITIALIZED));
 
+	blocker->flags |= SB_ACTIVE;
+	list_del(&blocker->link);
 	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
 		pr_info("suspend_block: %s\n", blocker->name);
-	if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED,
-	    SB_INITIALIZED | SB_ACTIVE) == SB_INITIALIZED)
-		atomic_inc(&suspend_block_count);
+	list_add(&blocker->link, &active_blockers);
 
-	atomic_inc(&current_event_num);
+	current_event_num++;
+	spin_unlock_irqrestore(&list_lock, irqflags);
 }
 EXPORT_SYMBOL(suspend_block);
 
@@ -165,13 +186,23 @@ EXPORT_SYMBOL(suspend_block);
  */
 void suspend_unblock(struct suspend_blocker *blocker)
 {
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
 	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
 		pr_info("suspend_unblock: %s\n", blocker->name);
+	list_del(&blocker->link);
+	list_add(&blocker->link, &inactive_blockers);
 
-	if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED | SB_ACTIVE,
-	    SB_INITIALIZED) == (SB_INITIALIZED | SB_ACTIVE))
-		if (atomic_dec_and_test(&suspend_block_count))
-			queue_work(suspend_work_queue, &suspend_work);
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(suspend_work_queue, &suspend_work);
+	blocker->flags &= ~(SB_ACTIVE);
+	if (blocker == &main_suspend_blocker) {
+		if (debug_mask & DEBUG_SUSPEND)
+			print_active_blockers_locked();
+	}
+	spin_unlock_irqrestore(&list_lock, irqflags);
 }
 EXPORT_SYMBOL(suspend_unblock);
 
@@ -181,7 +212,7 @@ EXPORT_SYMBOL(suspend_unblock);
  */
 bool is_blocking_suspend(struct suspend_blocker *blocker)
 {
-	return !!(atomic_read(&blocker->flags) & SB_ACTIVE);
+	return !!(blocker->flags & SB_ACTIVE);
 }
 EXPORT_SYMBOL(is_blocking_suspend);
 
-- 
1.6.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* [PATCH 6/8] PM: suspend_block: Add suspend_blocker stats
  2009-04-15  1:41         ` [PATCH 5/8] PM: suspend_block: Switch to list of active and inactive suspend blockers Arve Hjønnevåg
@ 2009-04-15  1:41           ` Arve Hjønnevåg
  2009-04-15  1:41             ` [PATCH 7/8] PM: suspend_block: Add timeout support Arve Hjønnevåg
  0 siblings, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-15  1:41 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, r-woodruff2, 

Report suspend block stats in /proc/suspend_blockers.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 include/linux/suspend_block.h |   11 +++
 kernel/power/Kconfig          |    7 ++
 kernel/power/suspend_block.c  |  201 ++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 217 insertions(+), 2 deletions(-)

diff --git a/include/linux/suspend_block.h b/include/linux/suspend_block.h
index 73ac5f7..31225c5 100755
--- a/include/linux/suspend_block.h
+++ b/include/linux/suspend_block.h
@@ -17,6 +17,7 @@
 #define _LINUX_SUSPEND_BLOCK_H
 
 #include <linux/list.h>
+#include <linux/ktime.h>
 
 /* A suspend_blocker prevents the system from entering suspend when active.
  */
@@ -26,6 +27,16 @@ struct suspend_blocker {
 	struct list_head    link;
 	int                 flags;
 	const char         *name;
+#ifdef CONFIG_SUSPEND_BLOCK_STAT
+	struct {
+		int             count;
+		int             wakeup_count;
+		ktime_t         total_time;
+		ktime_t         prevent_suspend_time;
+		ktime_t         max_time;
+		ktime_t         last_time;
+	} stat;
+#endif
 #endif
 };
 
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 2cf4c4d..3de0c5f 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -126,6 +126,13 @@ config SUSPEND_BLOCK
 	  /sys/power/request_state, the requested sleep state will be entered
 	  when no suspend_blockers are active.
 
+config SUSPEND_BLOCK_STAT
+	bool "Suspend block stats"
+	depends on SUSPEND_BLOCK
+	default y
+	---help---
+	  Report suspend block stats in /proc/suspend_blockers
+
 config USER_SUSPEND_BLOCK
 	bool "Userspace suspend blockers"
 	depends on SUSPEND_BLOCK
diff --git a/kernel/power/suspend_block.c b/kernel/power/suspend_block.c
index 697f52a..78d5880 100644
--- a/kernel/power/suspend_block.c
+++ b/kernel/power/suspend_block.c
@@ -18,6 +18,9 @@
 #include <linux/suspend.h>
 #include <linux/suspend_block.h>
 #include <linux/sysdev.h>
+#ifdef CONFIG_SUSPEND_BLOCK_STAT
+#include <linux/proc_fs.h>
+#endif
 #include "power.h"
 
 enum {
@@ -32,6 +35,7 @@ module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
 
 #define SB_INITIALIZED            (1U << 8)
 #define SB_ACTIVE                 (1U << 9)
+#define SB_PREVENTING_SUSPEND     (1U << 10)
 
 static DEFINE_SPINLOCK(list_lock);
 static DEFINE_SPINLOCK(state_lock);
@@ -42,6 +46,165 @@ struct workqueue_struct *suspend_work_queue;
 struct suspend_blocker main_suspend_blocker;
 static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
 static bool enable_suspend_blockers;
+static struct suspend_blocker unknown_wakeup;
+
+#ifdef CONFIG_SUSPEND_BLOCK_STAT
+static struct suspend_blocker deleted_suspend_blockers;
+static ktime_t last_sleep_time_update;
+static bool wait_for_wakeup;
+
+static int print_blocker_stat(char *buf, struct suspend_blocker *blocker)
+{
+	int lock_count = blocker->stat.count;
+	ktime_t active_time = ktime_set(0, 0);
+	ktime_t total_time = blocker->stat.total_time;
+	ktime_t max_time = blocker->stat.max_time;
+	ktime_t prevent_suspend_time = blocker->stat.prevent_suspend_time;
+	if (blocker->flags & SB_ACTIVE) {
+		ktime_t now, add_time;
+		now = ktime_get();
+		add_time = ktime_sub(now, blocker->stat.last_time);
+		lock_count++;
+		active_time = add_time;
+		total_time = ktime_add(total_time, add_time);
+		if (blocker->flags & SB_PREVENTING_SUSPEND)
+			prevent_suspend_time = ktime_add(prevent_suspend_time,
+					ktime_sub(now, last_sleep_time_update));
+		if (add_time.tv64 > max_time.tv64)
+			max_time = add_time;
+	}
+
+	return sprintf(buf, "\"%s\"\t%d\t%d\t%lld\t%lld\t%lld\t%lld\t%lld\n",
+		       blocker->name, lock_count, blocker->stat.wakeup_count,
+		       ktime_to_ns(active_time), ktime_to_ns(total_time),
+		       ktime_to_ns(prevent_suspend_time), ktime_to_ns(max_time),
+		       ktime_to_ns(blocker->stat.last_time));
+}
+
+
+static int suspend_blocker_stats_read_proc(char *page, char **start, off_t off,
+			       int count, int *eof, void *data)
+{
+	unsigned long irqflags;
+	struct suspend_blocker *blocker;
+	int len = 0;
+	char *p = page;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	p += sprintf(p, "name\tcount\twake_count\tactive_since"
+		     "\ttotal_time\tsleep_time\tmax_time\tlast_change\n");
+	list_for_each_entry(blocker, &inactive_blockers, link) {
+		p += print_blocker_stat(p, blocker);
+	}
+	list_for_each_entry(blocker, &active_blockers, link)
+		p += print_blocker_stat(p, blocker);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+
+	*start = page + off;
+
+	len = p - page;
+	if (len > off)
+		len -= off;
+	else
+		len = 0;
+
+	return len < count ? len  : count;
+}
+
+static void suspend_blocker_stat_init_locked(struct suspend_blocker *blocker)
+{
+	blocker->stat.count = 0;
+	blocker->stat.wakeup_count = 0;
+	blocker->stat.total_time = ktime_set(0, 0);
+	blocker->stat.prevent_suspend_time = ktime_set(0, 0);
+	blocker->stat.max_time = ktime_set(0, 0);
+	blocker->stat.last_time = ktime_set(0, 0);
+}
+
+static void suspend_blocker_stat_destroy_locked(struct suspend_blocker *bl)
+{
+	if (!bl->stat.count)
+		return;
+	deleted_suspend_blockers.stat.count += bl->stat.count;
+	deleted_suspend_blockers.stat.total_time = ktime_add(
+		deleted_suspend_blockers.stat.total_time, bl->stat.total_time);
+	deleted_suspend_blockers.stat.prevent_suspend_time = ktime_add(
+		deleted_suspend_blockers.stat.prevent_suspend_time,
+		bl->stat.prevent_suspend_time);
+	deleted_suspend_blockers.stat.max_time = ktime_add(
+		deleted_suspend_blockers.stat.max_time, bl->stat.max_time);
+}
+
+static void suspend_unblock_stat_locked(struct suspend_blocker *blocker)
+{
+	ktime_t duration;
+	ktime_t now;
+	if (!(blocker->flags & SB_ACTIVE))
+		return;
+	now = ktime_get();
+	blocker->stat.count++;
+	duration = ktime_sub(now, blocker->stat.last_time);
+	blocker->stat.total_time =
+		ktime_add(blocker->stat.total_time, duration);
+	if (ktime_to_ns(duration) > ktime_to_ns(blocker->stat.max_time))
+		blocker->stat.max_time = duration;
+	blocker->stat.last_time = ktime_get();
+	if (blocker->flags & SB_PREVENTING_SUSPEND) {
+		duration = ktime_sub(now, last_sleep_time_update);
+		blocker->stat.prevent_suspend_time = ktime_add(
+			blocker->stat.prevent_suspend_time, duration);
+		blocker->flags &= ~SB_PREVENTING_SUSPEND;
+	}
+}
+
+static void suspend_block_stat_locked(struct suspend_blocker *blocker)
+{
+	if (wait_for_wakeup) {
+		if (debug_mask & DEBUG_WAKEUP)
+			pr_info("wakeup suspend blocker: %s\n", blocker->name);
+		wait_for_wakeup = false;
+		blocker->stat.wakeup_count++;
+	}
+	if (!(blocker->flags & SB_ACTIVE))
+		blocker->stat.last_time = ktime_get();
+}
+
+static void update_sleep_wait_stats_locked(bool done)
+{
+	struct suspend_blocker *blocker;
+	ktime_t now, elapsed, add;
+
+	now = ktime_get();
+	elapsed = ktime_sub(now, last_sleep_time_update);
+	list_for_each_entry(blocker, &active_blockers, link) {
+		if (blocker->flags & SB_PREVENTING_SUSPEND) {
+			add = elapsed;
+			blocker->stat.prevent_suspend_time = ktime_add(
+				blocker->stat.prevent_suspend_time, add);
+		}
+		if (done)
+			blocker->flags &= ~SB_PREVENTING_SUSPEND;
+		else
+			blocker->flags |= SB_PREVENTING_SUSPEND;
+	}
+	last_sleep_time_update = now;
+}
+
+#else
+
+static inline void suspend_blocker_stat_init_locked(
+					struct suspend_blocker *blocker) {}
+static inline void suspend_blocker_stat_destroy_locked(
+					struct suspend_blocker *blocker) {}
+static inline void suspend_block_stat_locked(
+					struct suspend_blocker *blocker) {}
+static inline void suspend_unblock_stat_locked(
+					struct suspend_blocker *blocker) {}
+static inline void update_sleep_wait_stats_locked(bool done) {}
+
+#endif
+
 
 static void print_active_blockers_locked(void)
 {
@@ -65,7 +228,6 @@ static void suspend_worker(struct work_struct *work)
 
 	enable_suspend_blockers = 1;
 
-retry:
 	if (suspend_is_blocked()) {
 		if (debug_mask & DEBUG_SUSPEND)
 			pr_info("suspend: abort suspend\n");
@@ -89,7 +251,8 @@ retry:
 	if (current_event_num == entry_event_num) {
 		if (debug_mask & DEBUG_SUSPEND)
 			pr_info("suspend: pm_suspend returned with no event\n");
-		goto retry;
+		suspend_block(&unknown_wakeup);
+		suspend_unblock(&unknown_wakeup);
 	}
 abort:
 	enable_suspend_blockers = 0;
@@ -99,6 +262,9 @@ static DECLARE_WORK(suspend_work, suspend_worker);
 static int suspend_block_suspend(struct sys_device *dev, pm_message_t state)
 {
 	int ret = suspend_is_blocked() ? -EAGAIN : 0;
+#ifdef CONFIG_SUSPEND_BLOCK_STAT
+	wait_for_wakeup = true;
+#endif
 	if (debug_mask & DEBUG_SUSPEND)
 		pr_info("suspend_block_suspend return %d\n", ret);
 	return ret;
@@ -133,6 +299,7 @@ void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
 	INIT_LIST_HEAD(&blocker->link);
 
 	spin_lock_irqsave(&list_lock, irqflags);
+	suspend_blocker_stat_init_locked(blocker);
 	list_add(&blocker->link, &inactive_blockers);
 	spin_unlock_irqrestore(&list_lock, irqflags);
 }
@@ -148,6 +315,7 @@ void suspend_blocker_destroy(struct suspend_blocker *blocker)
 	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
 		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
 	spin_lock_irqsave(&list_lock, irqflags);
+	suspend_blocker_stat_destroy_locked(blocker);
 	blocker->flags &= ~SB_INITIALIZED;
 	list_del(&blocker->link);
 	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
@@ -167,6 +335,7 @@ void suspend_block(struct suspend_blocker *blocker)
 	spin_lock_irqsave(&list_lock, irqflags);
 	BUG_ON(!(blocker->flags & SB_INITIALIZED));
 
+	suspend_block_stat_locked(blocker);
 	blocker->flags |= SB_ACTIVE;
 	list_del(&blocker->link);
 	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
@@ -174,6 +343,10 @@ void suspend_block(struct suspend_blocker *blocker)
 	list_add(&blocker->link, &active_blockers);
 
 	current_event_num++;
+	if (blocker == &main_suspend_blocker)
+		update_sleep_wait_stats_locked(true);
+	else if (!is_blocking_suspend(&main_suspend_blocker))
+		update_sleep_wait_stats_locked(false);
 	spin_unlock_irqrestore(&list_lock, irqflags);
 }
 EXPORT_SYMBOL(suspend_block);
@@ -190,6 +363,8 @@ void suspend_unblock(struct suspend_blocker *blocker)
 
 	spin_lock_irqsave(&list_lock, irqflags);
 
+	suspend_unblock_stat_locked(blocker);
+
 	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
 		pr_info("suspend_unblock: %s\n", blocker->name);
 	list_del(&blocker->link);
@@ -201,6 +376,7 @@ void suspend_unblock(struct suspend_blocker *blocker)
 	if (blocker == &main_suspend_blocker) {
 		if (debug_mask & DEBUG_SUSPEND)
 			print_active_blockers_locked();
+		update_sleep_wait_stats_locked(false);
 	}
 	spin_unlock_irqrestore(&list_lock, irqflags);
 }
@@ -245,8 +421,13 @@ static int __init suspend_block_init(void)
 {
 	int ret;
 
+#ifdef CONFIG_SUSPEND_BLOCK_STAT
+	suspend_blocker_init(&deleted_suspend_blockers,
+				"deleted_suspend_blockers");
+#endif
 	suspend_blocker_init(&main_suspend_blocker, "main");
 	suspend_block(&main_suspend_blocker);
+	suspend_blocker_init(&unknown_wakeup, "unknown_wakeups");
 
 	ret = sysdev_class_register(&suspend_block_sysclass);
 	if (ret) {
@@ -265,6 +446,11 @@ static int __init suspend_block_init(void)
 		goto err_suspend_work_queue;
 	}
 
+#ifdef CONFIG_SUSPEND_BLOCK_STAT
+	create_proc_read_entry("suspend_blockers", S_IRUGO, NULL,
+				suspend_blocker_stats_read_proc, NULL);
+#endif
+
 	return 0;
 
 err_suspend_work_queue:
@@ -272,16 +458,27 @@ err_suspend_work_queue:
 err_sysdev_register:
 	sysdev_class_unregister(&suspend_block_sysclass);
 err_sysdev_class_register:
+	suspend_blocker_destroy(&unknown_wakeup);
 	suspend_blocker_destroy(&main_suspend_blocker);
+#ifdef CONFIG_SUSPEND_BLOCK_STAT
+	suspend_blocker_destroy(&deleted_suspend_blockers);
+#endif
 	return ret;
 }
 
 static void  __exit suspend_block_exit(void)
 {
+#ifdef CONFIG_SUSPEND_BLOCK_STAT
+	remove_proc_entry("suspend_blockers", NULL);
+#endif
 	destroy_workqueue(suspend_work_queue);
 	sysdev_unregister(&suspend_block_sysdev);
 	sysdev_class_unregister(&suspend_block_sysclass);
+	suspend_blocker_destroy(&unknown_wakeup);
 	suspend_blocker_destroy(&main_suspend_blocker);
+#ifdef CONFIG_SUSPEND_BLOCK_STAT
+	suspend_blocker_destroy(&deleted_suspend_blockers);
+#endif
 }
 
 core_initcall(suspend_block_init);
-- 
1.6.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* [PATCH 7/8] PM: suspend_block: Add timeout support.
  2009-04-15  1:41           ` [PATCH 6/8] PM: suspend_block: Add suspend_blocker stats Arve Hjønnevåg
@ 2009-04-15  1:41             ` Arve Hjønnevåg
  2009-04-15  1:41               ` [PATCH 8/8] PM: suspend_block: Add timeout support to user-space suspend_blockers Arve Hjønnevåg
  0 siblings, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-15  1:41 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, r-woodruff2, 

Add suspend_block_timeout to block suspend for a limited time.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/suspend-blockers.txt |   10 ++
 include/linux/suspend_block.h            |    7 +
 kernel/power/suspend_block.c             |  232 +++++++++++++++++++++++++-----
 3 files changed, 216 insertions(+), 33 deletions(-)

diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
index 35bc713..2781e05 100644
--- a/Documentation/power/suspend-blockers.txt
+++ b/Documentation/power/suspend-blockers.txt
@@ -74,6 +74,16 @@ if (list_empty(&state->pending_work))
 else
 	suspend_block(&state->suspend_blocker);
 
+A driver can also call suspend_block_timeout to release the suspend_blocker
+after a delay:
+	suspend_block_timeout(&state->suspend_blocker, HZ);
+
+This works whether the suspend_blocker is already active or not. It is useful if
+the driver woke up other parts of the system that do not use suspend_blockers
+but still need to run. Avoid this when possible, since it will waste power
+if the timeout is long or may fail to finish needed work if the timeout is
+short. Calling suspend_block or suspend_unblock will cancel the timeout.
+
 User-space API
 ==============
 
diff --git a/include/linux/suspend_block.h b/include/linux/suspend_block.h
index 31225c5..9cea772 100755
--- a/include/linux/suspend_block.h
+++ b/include/linux/suspend_block.h
@@ -27,9 +27,11 @@ struct suspend_blocker {
 	struct list_head    link;
 	int                 flags;
 	const char         *name;
+	unsigned long       expires;
 #ifdef CONFIG_SUSPEND_BLOCK_STAT
 	struct {
 		int             count;
+		int             expire_count;
 		int             wakeup_count;
 		ktime_t         total_time;
 		ktime_t         prevent_suspend_time;
@@ -45,9 +47,13 @@ struct suspend_blocker {
 void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
 void suspend_blocker_destroy(struct suspend_blocker *blocker);
 void suspend_block(struct suspend_blocker *blocker);
+void suspend_block_timeout(struct suspend_blocker *blocker, long timeout);
 void suspend_unblock(struct suspend_blocker *blocker);
 
 /* is_blocking_suspend returns true if the suspend_blocker is currently active.
+ * If the suspend_blocker has a timeout, it does not check the timeout, but if
+ * the timeout had already expired when it was checked elsewhere this function
+ * will return false.
  */
 bool is_blocking_suspend(struct suspend_blocker *blocker);
 
@@ -65,6 +71,7 @@ static inline void suspend_blocker_init(struct suspend_blocker *blocker,
 					const char *name) {}
 static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
 static inline void suspend_block(struct suspend_blocker *blocker) {}
+static inline void suspend_block_timeout(struct suspend_blocker *bl, long t) {}
 static inline void suspend_unblock(struct suspend_blocker *blocker) {}
 static inline bool is_blocking_suspend(struct suspend_blocker *bl) { return 0; }
 static inline bool suspend_is_blocked(void) { return 0; }
diff --git a/kernel/power/suspend_block.c b/kernel/power/suspend_block.c
index 78d5880..9414455 100644
--- a/kernel/power/suspend_block.c
+++ b/kernel/power/suspend_block.c
@@ -29,13 +29,15 @@ enum {
 	DEBUG_USER_STATE = 1U << 2,
 	DEBUG_SUSPEND = 1U << 3,
 	DEBUG_SUSPEND_BLOCKER = 1U << 4,
+	DEBUG_EXPIRE = 1U << 5,
 };
 static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
 module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
 
 #define SB_INITIALIZED            (1U << 8)
 #define SB_ACTIVE                 (1U << 9)
-#define SB_PREVENTING_SUSPEND     (1U << 10)
+#define SB_AUTO_EXPIRE            (1U << 10)
+#define SB_PREVENTING_SUSPEND     (1U << 11)
 
 static DEFINE_SPINLOCK(list_lock);
 static DEFINE_SPINLOCK(state_lock);
@@ -53,19 +55,53 @@ static struct suspend_blocker deleted_suspend_blockers;
 static ktime_t last_sleep_time_update;
 static bool wait_for_wakeup;
 
+static bool stats_get_expired_time(struct suspend_blocker *blocker,
+				   ktime_t *expire_time)
+{
+	struct timespec ts;
+	struct timespec kt;
+	struct timespec tomono;
+	struct timespec delta;
+	unsigned long seq;
+	long timeout;
+
+	if (!(blocker->flags & SB_AUTO_EXPIRE))
+		return false;
+	do {
+		seq = read_seqbegin(&xtime_lock);
+		timeout = blocker->expires - jiffies;
+		if (timeout > 0)
+			return false;
+		kt = current_kernel_time();
+		tomono = wall_to_monotonic;
+	} while (read_seqretry(&xtime_lock, seq));
+	jiffies_to_timespec(-timeout, &delta);
+	set_normalized_timespec(&ts, kt.tv_sec + tomono.tv_sec - delta.tv_sec,
+				kt.tv_nsec + tomono.tv_nsec - delta.tv_nsec);
+	*expire_time = timespec_to_ktime(ts);
+	return true;
+}
+
+
 static int print_blocker_stat(char *buf, struct suspend_blocker *blocker)
 {
 	int lock_count = blocker->stat.count;
+	int expire_count = blocker->stat.expire_count;
 	ktime_t active_time = ktime_set(0, 0);
 	ktime_t total_time = blocker->stat.total_time;
 	ktime_t max_time = blocker->stat.max_time;
 	ktime_t prevent_suspend_time = blocker->stat.prevent_suspend_time;
 	if (blocker->flags & SB_ACTIVE) {
 		ktime_t now, add_time;
-		now = ktime_get();
+		bool expired = stats_get_expired_time(blocker, &now);
+		if (!expired)
+			now = ktime_get();
 		add_time = ktime_sub(now, blocker->stat.last_time);
 		lock_count++;
-		active_time = add_time;
+		if (!expired)
+			active_time = add_time;
+		else
+			expire_count++;
 		total_time = ktime_add(total_time, add_time);
 		if (blocker->flags & SB_PREVENTING_SUSPEND)
 			prevent_suspend_time = ktime_add(prevent_suspend_time,
@@ -74,9 +110,10 @@ static int print_blocker_stat(char *buf, struct suspend_blocker *blocker)
 			max_time = add_time;
 	}
 
-	return sprintf(buf, "\"%s\"\t%d\t%d\t%lld\t%lld\t%lld\t%lld\t%lld\n",
-		       blocker->name, lock_count, blocker->stat.wakeup_count,
-		       ktime_to_ns(active_time), ktime_to_ns(total_time),
+	return sprintf(buf, "\"%s\"\t%d\t%d\t%d\t%lld\t%lld\t%lld\t%lld\t"
+		       "%lld\n", blocker->name, lock_count, expire_count,
+		       blocker->stat.wakeup_count, ktime_to_ns(active_time),
+		       ktime_to_ns(total_time),
 		       ktime_to_ns(prevent_suspend_time), ktime_to_ns(max_time),
 		       ktime_to_ns(blocker->stat.last_time));
 }
@@ -92,7 +129,7 @@ static int suspend_blocker_stats_read_proc(char *page, char **start, off_t off,
 
 	spin_lock_irqsave(&list_lock, irqflags);
 
-	p += sprintf(p, "name\tcount\twake_count\tactive_since"
+	p += sprintf(p, "name\tcount\texpire_count\twake_count\tactive_since"
 		     "\ttotal_time\tsleep_time\tmax_time\tlast_change\n");
 	list_for_each_entry(blocker, &inactive_blockers, link) {
 		p += print_blocker_stat(p, blocker);
@@ -115,6 +152,7 @@ static int suspend_blocker_stats_read_proc(char *page, char **start, off_t off,
 static void suspend_blocker_stat_init_locked(struct suspend_blocker *blocker)
 {
 	blocker->stat.count = 0;
+	blocker->stat.expire_count = 0;
 	blocker->stat.wakeup_count = 0;
 	blocker->stat.total_time = ktime_set(0, 0);
 	blocker->stat.prevent_suspend_time = ktime_set(0, 0);
@@ -127,6 +165,7 @@ static void suspend_blocker_stat_destroy_locked(struct suspend_blocker *bl)
 	if (!bl->stat.count)
 		return;
 	deleted_suspend_blockers.stat.count += bl->stat.count;
+	deleted_suspend_blockers.stat.expire_count += bl->stat.expire_count;
 	deleted_suspend_blockers.stat.total_time = ktime_add(
 		deleted_suspend_blockers.stat.total_time, bl->stat.total_time);
 	deleted_suspend_blockers.stat.prevent_suspend_time = ktime_add(
@@ -136,14 +175,20 @@ static void suspend_blocker_stat_destroy_locked(struct suspend_blocker *bl)
 		deleted_suspend_blockers.stat.max_time, bl->stat.max_time);
 }
 
-static void suspend_unblock_stat_locked(struct suspend_blocker *blocker)
+static void suspend_unblock_stat_locked(struct suspend_blocker *blocker,
+					bool expired)
 {
 	ktime_t duration;
 	ktime_t now;
 	if (!(blocker->flags & SB_ACTIVE))
 		return;
-	now = ktime_get();
+	if (stats_get_expired_time(blocker, &now))
+		expired = true;
+	else
+		now = ktime_get();
 	blocker->stat.count++;
+	if (expired)
+		blocker->stat.expire_count++;
 	duration = ktime_sub(now, blocker->stat.last_time);
 	blocker->stat.total_time =
 		ktime_add(blocker->stat.total_time, duration);
@@ -166,6 +211,12 @@ static void suspend_block_stat_locked(struct suspend_blocker *blocker)
 		wait_for_wakeup = false;
 		blocker->stat.wakeup_count++;
 	}
+	if ((blocker->flags & SB_AUTO_EXPIRE) &&
+	    time_is_before_eq_jiffies(blocker->expires)) {
+		suspend_unblock_stat_locked(blocker, false);
+		blocker->stat.last_time = ktime_get();
+	}
+
 	if (!(blocker->flags & SB_ACTIVE))
 		blocker->stat.last_time = ktime_get();
 }
@@ -173,17 +224,22 @@ static void suspend_block_stat_locked(struct suspend_blocker *blocker)
 static void update_sleep_wait_stats_locked(bool done)
 {
 	struct suspend_blocker *blocker;
-	ktime_t now, elapsed, add;
+	ktime_t now, etime, elapsed, add;
+	bool expired;
 
 	now = ktime_get();
 	elapsed = ktime_sub(now, last_sleep_time_update);
 	list_for_each_entry(blocker, &active_blockers, link) {
+		expired = stats_get_expired_time(blocker, &etime);
 		if (blocker->flags & SB_PREVENTING_SUSPEND) {
-			add = elapsed;
+			if (expired)
+				add = ktime_sub(etime, last_sleep_time_update);
+			else
+				add = elapsed;
 			blocker->stat.prevent_suspend_time = ktime_add(
 				blocker->stat.prevent_suspend_time, add);
 		}
-		if (done)
+		if (done || expired)
 			blocker->flags &= ~SB_PREVENTING_SUSPEND;
 		else
 			blocker->flags |= SB_PREVENTING_SUSPEND;
@@ -199,26 +255,69 @@ static inline void suspend_blocker_stat_destroy_locked(
 					struct suspend_blocker *blocker) {}
 static inline void suspend_block_stat_locked(
 					struct suspend_blocker *blocker) {}
-static inline void suspend_unblock_stat_locked(
-					struct suspend_blocker *blocker) {}
+static inline void suspend_unblock_stat_locked(struct suspend_blocker *blocker,
+					   bool expired) {}
 static inline void update_sleep_wait_stats_locked(bool done) {}
 
 #endif
 
 
+static void expire_suspend_blocker(struct suspend_blocker *blocker)
+{
+	suspend_unblock_stat_locked(blocker, true);
+	blocker->flags &= ~(SB_ACTIVE | SB_AUTO_EXPIRE);
+	list_del(&blocker->link);
+	list_add(&blocker->link, &inactive_blockers);
+	if (debug_mask & (DEBUG_SUSPEND_BLOCKER | DEBUG_EXPIRE))
+		pr_info("expired suspend blocker %s\n", blocker->name);
+}
+
 static void print_active_blockers_locked(void)
 {
 	struct suspend_blocker *blocker;
 
-	list_for_each_entry(blocker, &active_blockers, link)
-		pr_info("active suspend blocker %s\n", blocker->name);
+	list_for_each_entry(blocker, &active_blockers, link) {
+		if (blocker->flags & SB_AUTO_EXPIRE) {
+			long timeout = blocker->expires - jiffies;
+			if (timeout <= 0)
+				pr_info("suspend blocker %s, expired\n",
+					blocker->name);
+			else
+				pr_info("active suspend blocker %s, time left "
+					"%ld\n", blocker->name, timeout);
+		} else
+			pr_info("active suspend blocker %s\n", blocker->name);
+	}
+}
+
+static long max_suspend_blocker_timeout_locked(void)
+{
+	struct suspend_blocker *blocker, *n;
+	long max_timeout = 0;
+
+	list_for_each_entry_safe(blocker, n, &active_blockers, link) {
+		if (blocker->flags & SB_AUTO_EXPIRE) {
+			long timeout = blocker->expires - jiffies;
+			if (timeout <= 0)
+				expire_suspend_blocker(blocker);
+			else if (timeout > max_timeout)
+				max_timeout = timeout;
+		} else
+			return -1;
+	}
+	return max_timeout;
 }
 
 bool suspend_is_blocked(void)
 {
+	long ret;
+	unsigned long irqflags;
 	if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
 		return 0;
-	return !list_empty(&active_blockers);
+	spin_lock_irqsave(&list_lock, irqflags);
+	ret = !!max_suspend_blocker_timeout_locked();
+	spin_unlock_irqrestore(&list_lock, irqflags);
+	return ret;
 }
 
 static void suspend_worker(struct work_struct *work)
@@ -251,14 +350,49 @@ static void suspend_worker(struct work_struct *work)
 	if (current_event_num == entry_event_num) {
 		if (debug_mask & DEBUG_SUSPEND)
 			pr_info("suspend: pm_suspend returned with no event\n");
-		suspend_block(&unknown_wakeup);
-		suspend_unblock(&unknown_wakeup);
+		suspend_block_timeout(&unknown_wakeup, HZ / 2);
 	}
 abort:
 	enable_suspend_blockers = 0;
 }
 static DECLARE_WORK(suspend_work, suspend_worker);
 
+static void expire_suspend_blockers(unsigned long data)
+{
+	long timeout;
+	unsigned long irqflags;
+	if (debug_mask & DEBUG_EXPIRE)
+		pr_info("expire_suspend_blockers: start\n");
+	spin_lock_irqsave(&list_lock, irqflags);
+	if (debug_mask & DEBUG_SUSPEND)
+		print_active_blockers_locked();
+	timeout = max_suspend_blocker_timeout_locked();
+	if (debug_mask & DEBUG_EXPIRE)
+		pr_info("expire_suspend_blockers: done, timeout %ld\n",
+			timeout);
+	if (timeout == 0)
+		queue_work(suspend_work_queue, &suspend_work);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+static DEFINE_TIMER(expire_timer, expire_suspend_blockers, 0, 0);
+
+static void update_suspend(struct suspend_blocker *blocker, long max_timeout)
+{
+	if (max_timeout > 0) {
+		if (debug_mask & DEBUG_EXPIRE)
+			pr_info("suspend_blocker: %s, start expire timer, "
+				"%ld\n", blocker->name, max_timeout);
+		mod_timer(&expire_timer, jiffies + max_timeout);
+	} else {
+		if (del_timer(&expire_timer))
+			if (debug_mask & DEBUG_EXPIRE)
+				pr_info("suspend_blocker: %s, stop expire "
+					"timer\n", blocker->name);
+		if (max_timeout == 0)
+			queue_work(suspend_work_queue, &suspend_work);
+	}
+}
+
 static int suspend_block_suspend(struct sys_device *dev, pm_message_t state)
 {
 	int ret = suspend_is_blocked() ? -EAGAIN : 0;
@@ -318,17 +452,14 @@ void suspend_blocker_destroy(struct suspend_blocker *blocker)
 	suspend_blocker_stat_destroy_locked(blocker);
 	blocker->flags &= ~SB_INITIALIZED;
 	list_del(&blocker->link);
-	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
-		queue_work(suspend_work_queue, &suspend_work);
+	if (blocker->flags & SB_ACTIVE)
+		update_suspend(blocker, max_suspend_blocker_timeout_locked());
 	spin_unlock_irqrestore(&list_lock, irqflags);
 }
 EXPORT_SYMBOL(suspend_blocker_destroy);
 
-/**
- * suspend_block() - Block suspend
- * @blocker:	The suspend blocker to use
- */
-void suspend_block(struct suspend_blocker *blocker)
+static void __suspend_block(struct suspend_blocker *blocker, long timeout,
+			    bool has_timeout)
 {
 	unsigned long irqflags;
 
@@ -338,20 +469,56 @@ void suspend_block(struct suspend_blocker *blocker)
 	suspend_block_stat_locked(blocker);
 	blocker->flags |= SB_ACTIVE;
 	list_del(&blocker->link);
-	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
-		pr_info("suspend_block: %s\n", blocker->name);
-	list_add(&blocker->link, &active_blockers);
+	if (has_timeout) {
+		if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+			pr_info("suspend_block: %s, timeout %ld.%03lu\n",
+				blocker->name, timeout / HZ,
+				(timeout % HZ) * MSEC_PER_SEC / HZ);
+		blocker->expires = jiffies + timeout;
+		blocker->flags |= SB_AUTO_EXPIRE;
+		list_add_tail(&blocker->link, &active_blockers);
+	} else {
+		if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+			pr_info("suspend_block: %s\n", blocker->name);
+		blocker->expires = LONG_MAX;
+		blocker->flags &= ~SB_AUTO_EXPIRE;
+		/* Add to head so suspend_is_blocked only has to examine */
+		/* one entry */
+		list_add(&blocker->link, &active_blockers);
+	}
 
 	current_event_num++;
 	if (blocker == &main_suspend_blocker)
 		update_sleep_wait_stats_locked(true);
 	else if (!is_blocking_suspend(&main_suspend_blocker))
 		update_sleep_wait_stats_locked(false);
+	update_suspend(blocker, has_timeout ?
+			max_suspend_blocker_timeout_locked() : -1);
 	spin_unlock_irqrestore(&list_lock, irqflags);
 }
+
+/**
+ * suspend_block() - Block suspend
+ * @blocker:	The suspend blocker to use
+ */
+void suspend_block(struct suspend_blocker *blocker)
+{
+	__suspend_block(blocker, 0, false);
+}
 EXPORT_SYMBOL(suspend_block);
 
 /**
+ * suspend_block_timeout() - Block suspend for a limited time
+ * @blocker:	The suspend blocker to use.
+ * @timeout:	Timeout in jiffies before the suspend blocker auto-unblock
+ */
+void suspend_block_timeout(struct suspend_blocker *blocker, long timeout)
+{
+	__suspend_block(blocker, timeout, true);
+}
+EXPORT_SYMBOL(suspend_block_timeout);
+
+/**
  * suspend_unblock() - Unblock suspend
  * @blocker:	The suspend blocker to unblock.
  *
@@ -363,16 +530,15 @@ void suspend_unblock(struct suspend_blocker *blocker)
 
 	spin_lock_irqsave(&list_lock, irqflags);
 
-	suspend_unblock_stat_locked(blocker);
+	suspend_unblock_stat_locked(blocker, false);
 
 	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
 		pr_info("suspend_unblock: %s\n", blocker->name);
+	blocker->flags &= ~(SB_ACTIVE | SB_AUTO_EXPIRE);
 	list_del(&blocker->link);
 	list_add(&blocker->link, &inactive_blockers);
 
-	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
-		queue_work(suspend_work_queue, &suspend_work);
-	blocker->flags &= ~(SB_ACTIVE);
+	update_suspend(blocker, max_suspend_blocker_timeout_locked());
 	if (blocker == &main_suspend_blocker) {
 		if (debug_mask & DEBUG_SUSPEND)
 			print_active_blockers_locked();
-- 
1.6.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* [PATCH 8/8] PM: suspend_block: Add timeout support to user-space suspend_blockers
  2009-04-15  1:41             ` [PATCH 7/8] PM: suspend_block: Add timeout support Arve Hjønnevåg
@ 2009-04-15  1:41               ` Arve Hjønnevåg
  0 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-15  1:41 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, r-woodruff2, 

This also allows a grace period to be set where suspend is blocked for a while
if a user-space suspend_blocker is destroyed while it was active.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/suspend-blockers.txt |    7 +++++++
 include/linux/suspend_block_dev.h        |    1 +
 kernel/power/user_suspend_block.c        |   22 ++++++++++++++++++++++
 3 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
index 2781e05..890f057 100644
--- a/Documentation/power/suspend-blockers.txt
+++ b/Documentation/power/suspend-blockers.txt
@@ -94,6 +94,8 @@ then call:
 
 To activate a suspend_blocker call:
     ioctl(fd, SUSPEND_BLOCKER_IOCTL_BLOCK);
+or
+    ioctl(fd, SUSPEND_BLOCKER_IOCTL_BLOCK_TIMEOUT, &timespec_timeout);
 
 To unblock call:
     ioctl(fd, SUSPEND_BLOCKER_IOCTL_UNBLOCK);
@@ -101,3 +103,8 @@ To unblock call:
 To destroy the suspend_blocker, close the device:
     close(fd);
 
+A module parameter, unclean_exit_grace_period, can be set to allow servers
+some time to restart if they crash with an active suspend_blocker. If the
+process dies or the device is closed while the suspend_blocker is active, a
+suspend_blocker will be held for the number of seconds specified.
+
diff --git a/include/linux/suspend_block_dev.h b/include/linux/suspend_block_dev.h
index a7c0bf9..cff4943 100644
--- a/include/linux/suspend_block_dev.h
+++ b/include/linux/suspend_block_dev.h
@@ -20,6 +20,7 @@
 
 #define SUSPEND_BLOCKER_IOCTL_INIT(len)		_IOC(_IOC_WRITE, 's', 0, len)
 #define SUSPEND_BLOCKER_IOCTL_BLOCK		_IO('s', 1)
+#define SUSPEND_BLOCKER_IOCTL_BLOCK_TIMEOUT	_IOW('s', 2, struct timespec)
 #define SUSPEND_BLOCKER_IOCTL_UNBLOCK		_IO('s', 3)
 
 #endif
diff --git a/kernel/power/user_suspend_block.c b/kernel/power/user_suspend_block.c
index 782407c..1ba07dd 100644
--- a/kernel/power/user_suspend_block.c
+++ b/kernel/power/user_suspend_block.c
@@ -26,7 +26,12 @@ enum {
 static int debug_mask = DEBUG_FAILURE;
 module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
 
+static int unclean_exit_grace_period;
+module_param_named(unclean_exit_grace_period, unclean_exit_grace_period, int,
+						S_IRUGO | S_IWUSR | S_IWGRP);
+
 static DEFINE_MUTEX(ioctl_lock);
+static struct suspend_blocker unclean_exit_suspend_blocker;
 
 struct user_suspend_blocker {
 	struct suspend_blocker	blocker;
@@ -58,6 +63,8 @@ static long user_suspend_blocker_ioctl(struct file *file, unsigned int cmd,
 {
 	void __user *arg = (void __user *)_arg;
 	struct user_suspend_blocker *bl;
+	struct timespec ts;
+	unsigned long timeout;
 	long ret;
 
 	mutex_lock(&ioctl_lock);
@@ -75,6 +82,15 @@ static long user_suspend_blocker_ioctl(struct file *file, unsigned int cmd,
 		suspend_block(&bl->blocker);
 		ret = 0;
 		break;
+	case SUSPEND_BLOCKER_IOCTL_BLOCK_TIMEOUT:
+		if (copy_from_user(&ts, arg, sizeof(ts))) {
+			ret = -EFAULT;
+			goto done;
+		}
+		timeout  = timespec_to_jiffies(&ts);
+		suspend_block_timeout(&bl->blocker, timeout);
+		ret = 0;
+		break;
 	case SUSPEND_BLOCKER_IOCTL_UNBLOCK:
 		suspend_unblock(&bl->blocker);
 		ret = 0;
@@ -95,6 +111,9 @@ static int user_suspend_blocker_release(struct inode *inode, struct file *file)
 	struct user_suspend_blocker *bl = file->private_data;
 	if (!bl)
 		return 0;
+	if (is_blocking_suspend(&bl->blocker) && unclean_exit_grace_period)
+		suspend_block_timeout(&unclean_exit_suspend_blocker,
+					unclean_exit_grace_period * HZ);
 	suspend_blocker_destroy(&bl->blocker);
 	kfree(bl);
 	return 0;
@@ -113,12 +132,15 @@ struct miscdevice user_suspend_blocker_device = {
 
 static int __init user_suspend_blocker_init(void)
 {
+	suspend_blocker_init(&unclean_exit_suspend_blocker,
+				"user-unclean-exit");
 	return misc_register(&user_suspend_blocker_device);
 }
 
 static void __exit user_suspend_blocker_exit(void)
 {
 	misc_deregister(&user_suspend_blocker_device);
+	suspend_blocker_destroy(&unclean_exit_suspend_blocker);
 }
 
 module_init(user_suspend_blocker_init);
-- 
1.6.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-15  1:41 ` [PATCH 1/8] PM: Add suspend " Arve Hjønnevåg
  2009-04-15  1:41   ` [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space Arve Hjønnevåg
@ 2009-04-15 15:29   ` Alan Stern
  2009-04-15 19:08     ` mark gross
  2009-04-16  0:34     ` Arve Hjønnevåg
  2009-04-15 22:31   ` mark gross
                     ` (2 subsequent siblings)
  4 siblings, 2 replies; 195+ messages in thread
From: Alan Stern @ 2009-04-15 15:29 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: ncunningham, u.luckas, swetland, Linux-pm mailing list

On Tue, 14 Apr 2009, [utf-8] Arve Hjønnevåg wrote:

> +Suspend blockers can be used to allow user-space to decide which keys should
> +wake the full system up and turn the screen on.

This sentence still grates on me.  For readers not familiar with the 
embedded/phone environment, it won't make any sense.  Furthermore the 
rest of the text below doesn't even mention the screen, which will 
cause readers to wonder why it is mentioned here.

>  Use set_irq_wake or a platform
> +specific api to make sure the keypad interrupt wakes up the cpu. Once the keypad
> +driver has resumed, the sequence of events can look like this:
> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> +- The keypad-scan code detects a key change and reports it to the input-event
> +  driver.
> +- The input-event driver sees the key change, enqueues an event, and calls
> +  suspend_block on the input-event-queue suspend_blocker.
> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> +  on the keypad-scan suspend_blocker.
> +- The user-space input-event thread returns from select/poll, calls
> +  suspend_block on the process-input-events suspend_blocker and then calls read
> +  on the input-event device.
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> +- The user-space input-event thread returns from read. It determines that the
> +  key should not wake up the full system, calls suspend_unblock on the
> +  process-input-events suspend_blocker and calls select or poll.
> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++

How about replacing that first sentence with something like this:

	In cell phones or other embedded systems where powering the
	screen is a significant drain on the battery, suspend blockers 
	can be used to allow user-space to decide whether a keystroke
	received while the system is suspended should cause the screen 
	to be turned back on or allow the system to go back into 
	suspend.

Then in the last section, say this:

      - The user-space input-event thread returns from read. If it 
	determines that the key should leave the screen off, it
	calls suspend_unblock on the process_input_events
	suspend_blocker and then calls select or poll.  The system
	will automatically suspend again, since now no suspend blockers
	are active.

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-15 15:29   ` [PATCH 1/8] PM: Add suspend block api Alan Stern
@ 2009-04-15 19:08     ` mark gross
  2009-04-16  0:40       ` Arve Hjønnevåg
  2009-04-16  0:34     ` Arve Hjønnevåg
  1 sibling, 1 reply; 195+ messages in thread
From: mark gross @ 2009-04-15 19:08 UTC (permalink / raw)
  To: Alan Stern; +Cc: ncunningham, u.luckas, swetland, Linux-pm mailing list

On Wed, Apr 15, 2009 at 11:29:01AM -0400, Alan Stern wrote:
> On Tue, 14 Apr 2009, [utf-8] Arve Hjønnevåg wrote:
> 
> > +Suspend blockers can be used to allow user-space to decide which keys should
> > +wake the full system up and turn the screen on.
> 
> This sentence still grates on me.  For readers not familiar with the 
> embedded/phone environment, it won't make any sense.  Furthermore the 
> rest of the text below doesn't even mention the screen, which will 
> cause readers to wonder why it is mentioned here.
> 
> >  Use set_irq_wake or a platform
> > +specific api to make sure the keypad interrupt wakes up the cpu. Once the keypad
> > +driver has resumed, the sequence of events can look like this:
> > +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> > +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> > +- The keypad-scan code detects a key change and reports it to the input-event
> > +  driver.
> > +- The input-event driver sees the key change, enqueues an event, and calls
> > +  suspend_block on the input-event-queue suspend_blocker.
> > +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> > +  on the keypad-scan suspend_blocker.
> > +- The user-space input-event thread returns from select/poll, calls
> > +  suspend_block on the process-input-events suspend_blocker and then calls read
> > +  on the input-event device.
> > +- The input-event driver dequeues the key-event and, since the queue is now
> > +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> > +- The user-space input-event thread returns from read. It determines that the
> > +  key should not wake up the full system, calls suspend_unblock on the
> > +  process-input-events suspend_blocker and calls select or poll.
> > +
> > +                 Key pressed   Key released
> > +                     |             |
> > +keypad-scan          ++++++++++++++++++
> > +input-event-queue        +++          +++
> > +process-input-events       +++          +++
> 
> How about replacing that first sentence with something like this:
> 
> 	In cell phones or other embedded systems where powering the
> 	screen is a significant drain on the battery, suspend blockers 
> 	can be used to allow user-space to decide whether a keystroke
> 	received while the system is suspended should cause the screen 
> 	to be turned back on or allow the system to go back into 
> 	suspend.
> 
> Then in the last section, say this:
> 
>       - The user-space input-event thread returns from read. If it 
> 	determines that the key should leave the screen off, it
> 	calls suspend_unblock on the process_input_events
> 	suspend_blocker and then calls select or poll.  The system
> 	will automatically suspend again, since now no suspend blockers
> 	are active.

I'm still re-reviewing the design so I probably should keep my mouth
shut, but I only see code for constraining entry into suspend state (S3
if you will)  I didn't see code that would get called to block device
entry into higher power states just constraints on entire system entry
into sleep state.  (I apologize if I missed that code and will happily
eat my words if needed.)

more comments after I finish looking over the code.

--mgross

 

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-15  1:41 ` [PATCH 1/8] PM: Add suspend " Arve Hjønnevåg
  2009-04-15  1:41   ` [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space Arve Hjønnevåg
  2009-04-15 15:29   ` [PATCH 1/8] PM: Add suspend block api Alan Stern
@ 2009-04-15 22:31   ` mark gross
  2009-04-16  1:45     ` Arve Hjønnevåg
  2009-04-20  9:29   ` Pavel Machek
  2009-04-29 22:34   ` Rafael J. Wysocki
  4 siblings, 1 reply; 195+ messages in thread
From: mark gross @ 2009-04-15 22:31 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: ncunningham, u.luckas, swetland, linux-pm

I'm sure you are sick of this question, but couldn't you do this same
functionality (constraining entry to Suspend) using existing
infrastructure.  You are clearly aggregating a block-count and triggering
on an zero aggregate value to enter Suspend.

I'm thinking you could do something like CPUIDLE to push the implicit
suspend call and use a new pmqos parameter to keep the system alive.
We'd have to look at the API to make sure it would work in interrupt
mode.

The only thing I got from the earlier threads asking why not PMQOS, was
some complaint about the strcmp the constraint aggregation does WRT
performance.  I don't think it would be too hard to address the perceived
performance issue (even if its yet to be proven as an issue anywhere)

FWIW there is some talk of re-factoring some of pmqos to sit on top of a
constraint framework (to generalize the code a little and support
constraining things that are not really measurable in terms of latencies
or throughputs)

anyway a few comments below.

On Tue, Apr 14, 2009 at 06:41:25PM -0700, Arve Hjønnevåg wrote:
> Adds /sys/power/request_state, a non-blocking interface that specifies
> which suspend state to enter when no suspend blockers are active. A
> special state, "on", stops the process by activating the "main" suspend
> blocker.
> 
> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> ---
>  Documentation/power/suspend-blockers.txt |   76 +++++++++
>  include/linux/suspend_block.h            |   61 +++++++
>  kernel/power/Kconfig                     |   10 ++
>  kernel/power/Makefile                    |    1 +
>  kernel/power/main.c                      |   62 +++++++
>  kernel/power/power.h                     |    6 +
>  kernel/power/suspend_block.c             |  257 ++++++++++++++++++++++++++++++
>  7 files changed, 473 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/power/suspend-blockers.txt
>  create mode 100755 include/linux/suspend_block.h
>  create mode 100644 kernel/power/suspend_block.c
> 
> diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
> new file mode 100644
> index 0000000..743b870
> --- /dev/null
> +++ b/Documentation/power/suspend-blockers.txt
> @@ -0,0 +1,76 @@
> +Suspend blockers
> +================
> +
> +A suspend_blocker prevents the system from entering suspend.

And trigger implicit entry into suspend when last blocker is released.

> +
> +If the suspend operation has already started when calling suspend_block on a
> +suspend_blocker, it will abort the suspend operation as long it has not already
> +reached the sysdev suspend stage. This means that calling suspend_block from an
> +interrupt handler or a freezeable thread always works, but if you call
> +block_suspend from a sysdev suspend handler you must also return an error from
> +that handler to abort suspend.
> +
> +Suspend blockers can be used to allow user-space to decide which keys should
> +wake the full system up and turn the screen on. 

Provided the user mode process controlling the screen power is in the calling
path of user-space input-event thread.

Basically it should be made clear that this is not a device PM
implementation.  Its a global system suspend constraint system, where
the count of blockers is aggregated in a static variable,
suspend_block_count, within suspend_block.c  with implicit suspend
called when block count gets to zero.

i.e. only useful in platform specific code for platforms that can do
~10ms suspends and resumes.
 
> Use set_irq_wake or a platform
> +specific api to make sure the keypad interrupt wakes up the cpu. Once the keypad
> +driver has resumed, the sequence of events can look like this:
> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
count = 1
> +- The keypad-scan code detects a key change and reports it to the input-event
> +  driver.
> +- The input-event driver sees the key change, enqueues an event, and calls
> +  suspend_block on the input-event-queue suspend_blocker.
count = 2
> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> +  on the keypad-scan suspend_blocker.
count = 1
> +- The user-space input-event thread returns from select/poll, calls
> +  suspend_block on the process-input-events suspend_blocker and then calls read
> +  on the input-event device.
count = 2
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
count = 1
> +- The user-space input-event thread returns from read. It determines that the
> +  key should not wake up the full system, calls suspend_unblock on the
> +  process-input-events suspend_blocker and calls select or poll.
count = 0 + race between dropping into Suspend state and the execution
of the select or poll.  (should probably be ok)

> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++
> +
> +
> +Driver API
> +==========
> +
> +A driver can use the suspend block api by adding a suspend_blocker variable to
> +its state and calling suspend_blocker_init. For instance:
> +struct state {
> +	struct suspend_blocker suspend_blocker;
> +}
> +
> +init() {
> +	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
> +}
> +
> +Before freeing the memory, suspend_blocker_destroy must be called:
> +
> +uninit() {
> +	suspend_blocker_destroy(&state->suspend_blocker);
> +}
> +
> +When the driver determines that it needs to run (usually in an interrupt
> +handler) it calls suspend_block:
> +	suspend_block(&state->suspend_blocker);
> +
> +When it no longer needs to run it calls suspend_unblock:
> +	suspend_unblock(&state->suspend_blocker);
> +
> +Calling suspend_block when the suspend blocker is active or suspend_unblock when
> +it is not active has no effect. This allows drivers to update their state and
> +call suspend suspend_block or suspend_unblock based on the result.
> +For instance:
> +
> +if (list_empty(&state->pending_work))
> +	suspend_unblock(&state->suspend_blocker);
> +else
> +	suspend_block(&state->suspend_blocker);
> +
> diff --git a/include/linux/suspend_block.h b/include/linux/suspend_block.h
> new file mode 100755
> index 0000000..7820c60
> --- /dev/null
> +++ b/include/linux/suspend_block.h
> @@ -0,0 +1,61 @@
> +/* include/linux/suspend_block.h
> + *
> + * Copyright (C) 2007-2009 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _LINUX_SUSPEND_BLOCK_H
> +#define _LINUX_SUSPEND_BLOCK_H
> +
> +/* A suspend_blocker prevents the system from entering suspend when active.
> + */
> +
> +struct suspend_blocker {
> +#ifdef CONFIG_SUSPEND_BLOCK
> +	atomic_t            flags;
> +	const char         *name;
> +#endif
> +};
> +
> +#ifdef CONFIG_SUSPEND_BLOCK
> +
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
> +void suspend_blocker_destroy(struct suspend_blocker *blocker);
> +void suspend_block(struct suspend_blocker *blocker);
> +void suspend_unblock(struct suspend_blocker *blocker);
> +
> +/* is_blocking_suspend returns true if the suspend_blocker is currently active.
> + */
> +bool is_blocking_suspend(struct suspend_blocker *blocker);
> +
> +/* suspend_is_blocked can be used by generic power management code to abort
> + * suspend.
> + *
> + * To preserve backward compatibility suspend_is_blocked returns 0 unless it
> + * is called during suspend initiated from the suspend_block code.
> + */
> +bool suspend_is_blocked(void);
> +
> +#else
> +
> +static inline void suspend_blocker_init(struct suspend_blocker *blocker,
> +					const char *name) {}
> +static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
> +static inline void suspend_block(struct suspend_blocker *blocker) {}
> +static inline void suspend_unblock(struct suspend_blocker *blocker) {}
> +static inline bool is_blocking_suspend(struct suspend_blocker *bl) { return 0; }
> +static inline bool suspend_is_blocked(void) { return 0; }
> +
> +#endif
> +
> +#endif
> +
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 23bd4da..9d1df13 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -116,6 +116,16 @@ config SUSPEND_FREEZER
>  
>  	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
>  
> +config SUSPEND_BLOCK
> +	bool "Suspend block"
> +	depends on PM
> +	select RTC_LIB
> +	default n
> +	---help---
> +	  Enable suspend_block. When user space requests a sleep state through
> +	  /sys/power/request_state, the requested sleep state will be entered
> +	  when no suspend_blockers are active.
> +
>  config HIBERNATION
>  	bool "Hibernation (aka 'suspend to disk')"
>  	depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
> index 720ea4f..29cdc9e 100644
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -6,6 +6,7 @@ endif
>  obj-$(CONFIG_PM)		+= main.o
>  obj-$(CONFIG_PM_SLEEP)		+= console.o
>  obj-$(CONFIG_FREEZER)		+= process.o
> +obj-$(CONFIG_SUSPEND_BLOCK)	+= suspend_block.o
>  obj-$(CONFIG_HIBERNATION)	+= swsusp.o disk.o snapshot.o swap.o user.o
>  
>  obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
> diff --git a/kernel/power/main.c b/kernel/power/main.c
> index c9632f8..e4c6b20 100644
> --- a/kernel/power/main.c
> +++ b/kernel/power/main.c
> @@ -22,6 +22,7 @@
>  #include <linux/freezer.h>
>  #include <linux/vmstat.h>
>  #include <linux/syscalls.h>
> +#include <linux/suspend_block.h>
>  
>  #include "power.h"
>  
> @@ -392,6 +393,9 @@ static void suspend_finish(void)
>  
>  
>  static const char * const pm_states[PM_SUSPEND_MAX] = {
> +#ifdef CONFIG_SUSPEND_BLOCK
> +	[PM_SUSPEND_ON]		= "on",
> +#endif
>  	[PM_SUSPEND_STANDBY]	= "standby",
>  	[PM_SUSPEND_MEM]	= "mem",
>  };
> @@ -540,6 +544,61 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  
>  power_attr(state);
>  
> +/**
> + *	request_state - control system power state.
> + *
> + *	This is similar to state, but it does not block until the system
> + *	resumes, and it will try to re-enter the state until another state is
> + *	requested. Suspend blockers are respected and the requested state will
> + *	only be entered when no suspend blockers are active.
> + *	Write "on" to cancel.
> + */
> +
> +#ifdef CONFIG_SUSPEND_BLOCK
> +static ssize_t request_state_show(struct kobject *kobj,
> +				  struct kobj_attribute *attr, char *buf)
> +{
> +	char *s = buf;
> +	int i;
> +
> +	for (i = 0; i < PM_SUSPEND_MAX; i++) {
> +		if (pm_states[i] && (i == PM_SUSPEND_ON || valid_state(i)))
> +			s += sprintf(s, "%s ", pm_states[i]);
> +	}
> +	if (s != buf)
> +		/* convert the last space to a newline */
> +		*(s-1) = '\n';
> +	return (s - buf);
> +}
> +
> +static ssize_t request_state_store(struct kobject *kobj,
> +				   struct kobj_attribute *attr,
> +				   const char *buf, size_t n)
> +{
> +	suspend_state_t state = PM_SUSPEND_ON;
> +	const char * const *s;
> +	char *p;
> +	int len;
> +	int error = -EINVAL;
> +
> +	p = memchr(buf, '\n', n);
> +	len = p ? p - buf : n;
> +
> +	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
> +		if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
> +			break;
> +	}
> +	if (state < PM_SUSPEND_MAX && *s)
> +		if (state == PM_SUSPEND_ON || valid_state(state)) {
> +			error = 0;
> +			request_suspend_state(state);
> +		}
> +	return error ? error : n;
> +}
> +
> +power_attr(request_state);
> +#endif /* CONFIG_SUSPEND_BLOCK */
> +
>  #ifdef CONFIG_PM_TRACE
>  int pm_trace_enabled;
>  
> @@ -567,6 +626,9 @@ power_attr(pm_trace);
>  
>  static struct attribute * g[] = {
>  	&state_attr.attr,
> +#ifdef CONFIG_SUSPEND_BLOCK
> +	&request_state_attr.attr,
> +#endif
>  #ifdef CONFIG_PM_TRACE
>  	&pm_trace_attr.attr,
>  #endif
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index 46b5ec7..2414a74 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -223,3 +223,9 @@ static inline void suspend_thaw_processes(void)
>  {
>  }
>  #endif
> +
> +#ifdef CONFIG_SUSPEND_BLOCK
> +/* kernel/power/suspend_block.c */
> +void request_suspend_state(suspend_state_t state);
> +#endif
> +
> diff --git a/kernel/power/suspend_block.c b/kernel/power/suspend_block.c
> new file mode 100644
> index 0000000..b4f2191
> --- /dev/null
> +++ b/kernel/power/suspend_block.c
> @@ -0,0 +1,257 @@
> +/* kernel/power/suspend_block.c
> + *
> + * Copyright (C) 2005-2008 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/suspend.h>
> +#include <linux/suspend_block.h>
> +#include <linux/sysdev.h>
> +#include "power.h"
> +
> +enum {
> +	DEBUG_EXIT_SUSPEND = 1U << 0,
> +	DEBUG_WAKEUP = 1U << 1,
> +	DEBUG_USER_STATE = 1U << 2,
> +	DEBUG_SUSPEND = 1U << 3,
> +	DEBUG_SUSPEND_BLOCKER = 1U << 4,
> +};
> +static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
> +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
> +
> +#define SB_INITIALIZED            (1U << 8)
> +#define SB_ACTIVE                 (1U << 9)
> +
> +static DEFINE_SPINLOCK(state_lock);
> +static atomic_t suspend_block_count;

So if suspend_block_count is the lynch pin of the design, why have all
the suspend_blocker structs?  Why have all the logic around the
different suspend blocker instances?

It seems like a lot of extra effort for an atomic "stay awake" counter.


--mgross


> +static atomic_t current_event_num;
> +struct workqueue_struct *suspend_work_queue;
> +struct suspend_blocker main_suspend_blocker;
> +static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
> +static bool enable_suspend_blockers;
> +
> +bool suspend_is_blocked(void)
> +{
> +	if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
> +		return 0;
> +	return !!atomic_read(&suspend_block_count);
> +}
> +
> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = 1;
> +
> +retry:
> +	if (suspend_is_blocked()) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: abort suspend\n");
> +		goto abort;
> +	}
> +
> +	entry_event_num = atomic_read(&current_event_num);
> +	if (debug_mask & DEBUG_SUSPEND)
> +		pr_info("suspend: enter suspend\n");
> +	ret = pm_suspend(requested_suspend_state);
> +	if (debug_mask & DEBUG_EXIT_SUSPEND) {
> +		struct timespec ts;
> +		struct rtc_time tm;
> +		getnstimeofday(&ts);
> +		rtc_time_to_tm(ts.tv_sec, &tm);
> +		pr_info("suspend: exit suspend, ret = %d "
> +			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
> +	}
> +	if (atomic_read(&current_event_num) == entry_event_num) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +		goto retry;
> +	}
> +abort:
> +	enable_suspend_blockers = 0;
> +}
> +static DECLARE_WORK(suspend_work, suspend_worker);
> +
> +static int suspend_block_suspend(struct sys_device *dev, pm_message_t state)
> +{
> +	int ret = suspend_is_blocked() ? -EAGAIN : 0;
> +	if (debug_mask & DEBUG_SUSPEND)
> +		pr_info("suspend_block_suspend return %d\n", ret);
> +	return ret;
> +}
> +
> +static struct sysdev_class suspend_block_sysclass = {
> +	.name = "suspend_block",
> +	.suspend = suspend_block_suspend,
> +};
> +static struct sys_device suspend_block_sysdev = {
> +	.cls = &suspend_block_sysclass,
> +};
> +
> +/**
> + * suspend_blocker_init() - Initialize a suspend blocker
> + * @blocker:	The suspend blocker to initialize.
> + * @name:	The name of the suspend blocker to show in
> + *		/proc/suspend_blockers
> + */
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
> +{
> +	if (name)
> +		blocker->name = name;
> +	BUG_ON(!blocker->name);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_init name=%s\n", blocker->name);
> +
> +	atomic_set(&blocker->flags, SB_INITIALIZED);
> +}
> +EXPORT_SYMBOL(suspend_blocker_init);
> +
> +/**
> + * suspend_blocker_destroy() - Destroy a suspend blocker
> + * @blocker:	The suspend blocker to destroy.
> + */
> +void suspend_blocker_destroy(struct suspend_blocker *blocker)
> +{
> +	int flags;
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
> +	flags = atomic_xchg(&blocker->flags, 0);
> +	WARN(!(flags & SB_INITIALIZED), "suspend_blocker_destroy called on "
> +					"uninitialized suspend_blocker\n");
> +	if (flags == (SB_INITIALIZED | SB_ACTIVE))
> +		if (atomic_dec_and_test(&suspend_block_count))
> +			queue_work(suspend_work_queue, &suspend_work);
> +}
> +EXPORT_SYMBOL(suspend_blocker_destroy);
> +
> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	BUG_ON(!(atomic_read(&blocker->flags) & SB_INITIALIZED));
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +	if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED,
> +	    SB_INITIALIZED | SB_ACTIVE) == SB_INITIALIZED)
> +		atomic_inc(&suspend_block_count);
> +
> +	atomic_inc(&current_event_num);
> +}
> +EXPORT_SYMBOL(suspend_block);
> +
> +/**
> + * suspend_unblock() - Unblock suspend
> + * @blocker:	The suspend blocker to unblock.
> + *
> + * If no other suspend blockers block suspend, the system will suspend.
> + */
> +void suspend_unblock(struct suspend_blocker *blocker)
> +{
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_unblock: %s\n", blocker->name);
> +
> +	if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED | SB_ACTIVE,
> +	    SB_INITIALIZED) == (SB_INITIALIZED | SB_ACTIVE))
> +		if (atomic_dec_and_test(&suspend_block_count))
> +			queue_work(suspend_work_queue, &suspend_work);
> +}
> +EXPORT_SYMBOL(suspend_unblock);
> +
> +/**
> + * is_blocking_suspend() - Test if a suspend blocker is blocking suspend
> + * @blocker:	The suspend blocker to check.
> + */
> +bool is_blocking_suspend(struct suspend_blocker *blocker)
> +{
> +	return !!(atomic_read(&blocker->flags) & SB_ACTIVE);
> +}
> +EXPORT_SYMBOL(is_blocking_suspend);
> +
> +void request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +	spin_lock_irqsave(&state_lock, irqflags);
> +	if (debug_mask & DEBUG_USER_STATE) {
> +		struct timespec ts;
> +		struct rtc_time tm;
> +		getnstimeofday(&ts);
> +		rtc_time_to_tm(ts.tv_sec, &tm);
> +		pr_info("request_suspend_state: %s (%d->%d) at %lld "
> +			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
> +			state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> +			requested_suspend_state, state,
> +			ktime_to_ns(ktime_get()),
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
> +	}
> +	requested_suspend_state = state;
> +	if (state == PM_SUSPEND_ON)
> +		suspend_block(&main_suspend_blocker);
> +	else
> +		suspend_unblock(&main_suspend_blocker);
> +	spin_unlock_irqrestore(&state_lock, irqflags);
> +}
> +
> +static int __init suspend_block_init(void)
> +{
> +	int ret;
> +
> +	suspend_blocker_init(&main_suspend_blocker, "main");
> +	suspend_block(&main_suspend_blocker);
> +
> +	ret = sysdev_class_register(&suspend_block_sysclass);
> +	if (ret) {
> +		pr_err("suspend_block_init: sysdev_class_register failed\n");
> +		goto err_sysdev_class_register;
> +	}
> +	ret = sysdev_register(&suspend_block_sysdev);
> +	if (ret) {
> +		pr_err("suspend_block_init: suspend_block_sysdev failed\n");
> +		goto err_sysdev_register;
> +	}
> +
> +	suspend_work_queue = create_singlethread_workqueue("suspend");
> +	if (suspend_work_queue == NULL) {
> +		ret = -ENOMEM;
> +		goto err_suspend_work_queue;
> +	}
> +
> +	return 0;
> +
> +err_suspend_work_queue:
> +	sysdev_unregister(&suspend_block_sysdev);
> +err_sysdev_register:
> +	sysdev_class_unregister(&suspend_block_sysclass);
> +err_sysdev_class_register:
> +	suspend_blocker_destroy(&main_suspend_blocker);
> +	return ret;
> +}
> +
> +static void  __exit suspend_block_exit(void)
> +{
> +	destroy_workqueue(suspend_work_queue);
> +	sysdev_unregister(&suspend_block_sysdev);
> +	sysdev_class_unregister(&suspend_block_sysclass);
> +	suspend_blocker_destroy(&main_suspend_blocker);
> +}
> +
> +core_initcall(suspend_block_init);
> +module_exit(suspend_block_exit);
> -- 
> 1.6.1

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [RFC][PATCH 0/8] Suspend block api
  2009-04-15  1:41 [RFC][PATCH 0/8] Suspend block api Arve Hjønnevåg
  2009-04-15  1:41 ` [PATCH 1/8] PM: Add suspend " Arve Hjønnevåg
@ 2009-04-15 23:04 ` Rafael J. Wysocki
  1 sibling, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2009-04-15 23:04 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Wednesday 15 April 2009, Arve Hjønnevåg wrote:
> This patch series adds a suspend-block api that provides the same
> functionality as the android wakelock api from my last patch series,
> Android PM extensions (version 3). As requested, wake_lock has been
> renamed to suspend_block(er), and timeout support has been separated
> into its own patch.

Sounds good.

Unfortunately, I don't have the time to review the patches in detail at the
moment.  Will to that as soon as I can.

Thanks,
Rafael
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-15 15:29   ` [PATCH 1/8] PM: Add suspend block api Alan Stern
  2009-04-15 19:08     ` mark gross
@ 2009-04-16  0:34     ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-16  0:34 UTC (permalink / raw)
  To: Alan Stern; +Cc: ncunningham, u.luckas, swetland, Linux-pm mailing list

2009/4/15 Alan Stern <stern@rowland.harvard.edu>:
> On Tue, 14 Apr 2009, [utf-8] Arve Hjønnevåg wrote:
>
>> +Suspend blockers can be used to allow user-space to decide which keys should
>> +wake the full system up and turn the screen on.
>
> This sentence still grates on me.  For readers not familiar with the
> embedded/phone environment, it won't make any sense.  Furthermore the
> rest of the text below doesn't even mention the screen, which will
> cause readers to wonder why it is mentioned here.
>
>>  Use set_irq_wake or a platform
>> +specific api to make sure the keypad interrupt wakes up the cpu. Once the keypad
>> +driver has resumed, the sequence of events can look like this:
>> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
>> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
>> +- The keypad-scan code detects a key change and reports it to the input-event
>> +  driver.
>> +- The input-event driver sees the key change, enqueues an event, and calls
>> +  suspend_block on the input-event-queue suspend_blocker.
>> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
>> +  on the keypad-scan suspend_blocker.
>> +- The user-space input-event thread returns from select/poll, calls
>> +  suspend_block on the process-input-events suspend_blocker and then calls read
>> +  on the input-event device.
>> +- The input-event driver dequeues the key-event and, since the queue is now
>> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
>> +- The user-space input-event thread returns from read. It determines that the
>> +  key should not wake up the full system, calls suspend_unblock on the
>> +  process-input-events suspend_blocker and calls select or poll.
>> +
>> +                 Key pressed   Key released
>> +                     |             |
>> +keypad-scan          ++++++++++++++++++
>> +input-event-queue        +++          +++
>> +process-input-events       +++          +++
>
> How about replacing that first sentence with something like this:
>
>        In cell phones or other embedded systems where powering the
>        screen is a significant drain on the battery, suspend blockers
>        can be used to allow user-space to decide whether a keystroke
>        received while the system is suspended should cause the screen
>        to be turned back on or allow the system to go back into
>        suspend.
>
> Then in the last section, say this:
>
>      - The user-space input-event thread returns from read. If it
>        determines that the key should leave the screen off, it
>        calls suspend_unblock on the process_input_events
>        suspend_blocker and then calls select or poll.  The system
>        will automatically suspend again, since now no suspend blockers
>        are active.
>

This sounds reasonable too me.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-15 19:08     ` mark gross
@ 2009-04-16  0:40       ` Arve Hjønnevåg
  0 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-16  0:40 UTC (permalink / raw)
  To: mgross; +Cc: ncunningham, u.luckas, swetland, Linux-pm mailing list

On Wed, Apr 15, 2009 at 12:08 PM, mark gross <mgross@linux.intel.com> wrote:
> I'm still re-reviewing the design so I probably should keep my mouth
> shut, but I only see code for constraining entry into suspend state (S3
> if you will)  I didn't see code that would get called to block device
> entry into higher power states just constraints on entire system entry
> into sleep state.  (I apologize if I missed that code and will happily
> eat my words if needed.)
>

I removed the idle wake lock type since it had too much overlap with
pm_qos. Are there any references to them left in the documentation?

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-15 22:31   ` mark gross
@ 2009-04-16  1:45     ` Arve Hjønnevåg
  2009-04-16 17:49       ` mark gross
  0 siblings, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-16  1:45 UTC (permalink / raw)
  To: mgross; +Cc: ncunningham, u.luckas, swetland, linux-pm

2009/4/15 mark gross <mgross@linux.intel.com>:
> I'm sure you are sick of this question, but couldn't you do this same
> functionality (constraining entry to Suspend) using existing
> infrastructure.  You are clearly aggregating a block-count and triggering
> on an zero aggregate value to enter Suspend.

The only reason I have a block count, is that the feedback on this
list was that it is too expensive to use a spin-lock every time we
want to block or unblock suspend. The later patches that enable stats
and timeout support uses inactive and active lists instead.

>
> I'm thinking you could do something like CPUIDLE to push the implicit
> suspend call and use a new pmqos parameter to keep the system alive.
> We'd have to look at the API to make sure it would work in interrupt
> mode.
>
> The only thing I got from the earlier threads asking why not PMQOS, was
> some complaint about the strcmp the constraint aggregation does WRT
> performance.  I don't think it would be too hard to address the perceived
> performance issue (even if its yet to be proven as an issue anywhere)

There was a question about why we did not use pmqos instead othe the
idle wake locks. One reason was that we mainly needed to prevent a
specific state to not loose interrupts, not because the latency was
too high. The other reason was that the pm qos interface uses strings
instead of handles. Using strings to search to a list for the item you
want to update has an impact on performance that is not hard to
measure, but it also require the clients to create unique strings.

> FWIW there is some talk of re-factoring some of pmqos to sit on top of a
> constraint framework (to generalize the code a little and support
> constraining things that are not really measurable in terms of latencies
> or throughputs)
>

That should help with replacing the idle-wake-locks.

> anyway a few comments below.
>
> On Tue, Apr 14, 2009 at 06:41:25PM -0700, Arve Hjønnevåg wrote:
>> Adds /sys/power/request_state, a non-blocking interface that specifies
>> which suspend state to enter when no suspend blockers are active. A
>> special state, "on", stops the process by activating the "main" suspend
>> blocker.
>>
>> Signed-off-by: Arve Hjønnevåg <arve@android.com>
>> ---
>>  Documentation/power/suspend-blockers.txt |   76 +++++++++
>>  include/linux/suspend_block.h            |   61 +++++++
>>  kernel/power/Kconfig                     |   10 ++
>>  kernel/power/Makefile                    |    1 +
>>  kernel/power/main.c                      |   62 +++++++
>>  kernel/power/power.h                     |    6 +
>>  kernel/power/suspend_block.c             |  257 ++++++++++++++++++++++++++++++
>>  7 files changed, 473 insertions(+), 0 deletions(-)
>>  create mode 100644 Documentation/power/suspend-blockers.txt
>>  create mode 100755 include/linux/suspend_block.h
>>  create mode 100644 kernel/power/suspend_block.c
>>
>> diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
>> new file mode 100644
>> index 0000000..743b870
>> --- /dev/null
>> +++ b/Documentation/power/suspend-blockers.txt
>> @@ -0,0 +1,76 @@
>> +Suspend blockers
>> +================
>> +
>> +A suspend_blocker prevents the system from entering suspend.
>
> And trigger implicit entry into suspend when last blocker is released.
>
>> +
>> +If the suspend operation has already started when calling suspend_block on a
>> +suspend_blocker, it will abort the suspend operation as long it has not already
>> +reached the sysdev suspend stage. This means that calling suspend_block from an
>> +interrupt handler or a freezeable thread always works, but if you call
>> +block_suspend from a sysdev suspend handler you must also return an error from
>> +that handler to abort suspend.
>> +
>> +Suspend blockers can be used to allow user-space to decide which keys should
>> +wake the full system up and turn the screen on.
>
> Provided the user mode process controlling the screen power is in the calling
> path of user-space input-event thread.

What you mean by this? Our user-space input event thread does not turn
the screen on, but translates the events and queues the translated
event on another queue. As long as all queues are protected by a
suspend blocker, there is no requirement about which thread turns the
screen on.

>
> Basically it should be made clear that this is not a device PM
> implementation.  Its a global system suspend constraint system, where
> the count of blockers is aggregated in a static variable,
> suspend_block_count, within suspend_block.c  with implicit suspend
> called when block count gets to zero.
>
> i.e. only useful in platform specific code for platforms that can do
> ~10ms suspends and resumes.

I disagree. If I have a desktop system that takes 5 seconds to wake up
from sleep, I still want the ability to auto suspend after user
inactivity, and to prevent suspend while some tasks are running.

>
>> Use set_irq_wake or a platform
>> +specific api to make sure the keypad interrupt wakes up the cpu. Once the keypad
>> +driver has resumed, the sequence of events can look like this:
>> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
>> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> count = 1
>> +- The keypad-scan code detects a key change and reports it to the input-event
>> +  driver.
>> +- The input-event driver sees the key change, enqueues an event, and calls
>> +  suspend_block on the input-event-queue suspend_blocker.
> count = 2
>> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
>> +  on the keypad-scan suspend_blocker.
> count = 1
>> +- The user-space input-event thread returns from select/poll, calls
>> +  suspend_block on the process-input-events suspend_blocker and then calls read
>> +  on the input-event device.
> count = 2
>> +- The input-event driver dequeues the key-event and, since the queue is now
>> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> count = 1
>> +- The user-space input-event thread returns from read. It determines that the
>> +  key should not wake up the full system, calls suspend_unblock on the
>> +  process-input-events suspend_blocker and calls select or poll.
> count = 0 + race between dropping into Suspend state and the execution
> of the select or poll.  (should probably be ok)

This is not a race condition, select or poll does not affect the
suspend blocker in the input driver to it does not matter if the poll
is called before or after suspend.

>
>> +
>> +                 Key pressed   Key released
>> +                     |             |
>> +keypad-scan          ++++++++++++++++++
>> +input-event-queue        +++          +++
>> +process-input-events       +++          +++
>> +
>> +

>> +struct suspend_blocker {
>> +#ifdef CONFIG_SUSPEND_BLOCK
>> +     atomic_t            flags;
>> +     const char         *name;
>> +#endif
>> +};
>> +
...
>> +
>> +#define SB_INITIALIZED            (1U << 8)
>> +#define SB_ACTIVE                 (1U << 9)
>> +
>> +static DEFINE_SPINLOCK(state_lock);
>> +static atomic_t suspend_block_count;
>
> So if suspend_block_count is the lynch pin of the design, why have all
> the suspend_blocker structs?  Why have all the logic around the
> different suspend blocker instances?

The suspend block structs need to be part of the api to allow stats
and/or debugging. It also ensures that a single suspend_blocker does
not change the global count by more than one. Also,
suspend_block_count is not part of the design, it is an optimization
that becomes useless when you enable stats.

>
> It seems like a lot of extra effort for an atomic "stay awake" counter.
>


-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-16  1:45     ` Arve Hjønnevåg
@ 2009-04-16 17:49       ` mark gross
  0 siblings, 0 replies; 195+ messages in thread
From: mark gross @ 2009-04-16 17:49 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Wed, Apr 15, 2009 at 06:45:00PM -0700, Arve Hjønnevåg wrote:
> 2009/4/15 mark gross <mgross@linux.intel.com>:
> > I'm sure you are sick of this question, but couldn't you do this same
> > functionality (constraining entry to Suspend) using existing
> > infrastructure.  You are clearly aggregating a block-count and triggering
> > on an zero aggregate value to enter Suspend.
> 
> The only reason I have a block count, is that the feedback on this
> list was that it is too expensive to use a spin-lock every time we
> want to block or unblock suspend. The later patches that enable stats
> and timeout support uses inactive and active lists instead.
> 
> >
> > I'm thinking you could do something like CPUIDLE to push the implicit
> > suspend call and use a new pmqos parameter to keep the system alive.
> > We'd have to look at the API to make sure it would work in interrupt
> > mode.
> >
> > The only thing I got from the earlier threads asking why not PMQOS, was
> > some complaint about the strcmp the constraint aggregation does WRT
> > performance.  I don't think it would be too hard to address the perceived
> > performance issue (even if its yet to be proven as an issue anywhere)
> 
> There was a question about why we did not use pmqos instead othe the
> idle wake locks. One reason was that we mainly needed to prevent a
> specific state to not loose interrupts, not because the latency was
> too high. The other reason was that the pm qos interface uses strings
> instead of handles. Using strings to search to a list for the item you
> want to update has an impact on performance that is not hard to
> measure, but it also require the clients to create unique strings.

really?  How many times / second was your system changing the pmqos
parameter?  If that turns out to be realistic, non-abusive use of the api
then perhaps I need to migrate pmqos to include / use handles.  

when pmqos was designed we assumed it wouldn't be hit with high
parameter change rates.  What could be a realistic upper bound on
change rate for the parameters?

> 
> > FWIW there is some talk of re-factoring some of pmqos to sit on top of a
> > constraint framework (to generalize the code a little and support
> > constraining things that are not really measurable in terms of latencies
> > or throughputs)
> >
> 
> That should help with replacing the idle-wake-locks.
> 
> > anyway a few comments below.
> >
> > On Tue, Apr 14, 2009 at 06:41:25PM -0700, Arve Hjønnevåg wrote:
> >> Adds /sys/power/request_state, a non-blocking interface that specifies
> >> which suspend state to enter when no suspend blockers are active. A
> >> special state, "on", stops the process by activating the "main" suspend
> >> blocker.
> >>
> >> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> >> ---
> >>  Documentation/power/suspend-blockers.txt |   76 +++++++++
> >>  include/linux/suspend_block.h            |   61 +++++++
> >>  kernel/power/Kconfig                     |   10 ++
> >>  kernel/power/Makefile                    |    1 +
> >>  kernel/power/main.c                      |   62 +++++++
> >>  kernel/power/power.h                     |    6 +
> >>  kernel/power/suspend_block.c             |  257 ++++++++++++++++++++++++++++++
> >>  7 files changed, 473 insertions(+), 0 deletions(-)
> >>  create mode 100644 Documentation/power/suspend-blockers.txt
> >>  create mode 100755 include/linux/suspend_block.h
> >>  create mode 100644 kernel/power/suspend_block.c
> >>
> >> diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
> >> new file mode 100644
> >> index 0000000..743b870
> >> --- /dev/null
> >> +++ b/Documentation/power/suspend-blockers.txt
> >> @@ -0,0 +1,76 @@
> >> +Suspend blockers
> >> +================
> >> +
> >> +A suspend_blocker prevents the system from entering suspend.
> >
> > And trigger implicit entry into suspend when last blocker is released.
> >
> >> +
> >> +If the suspend operation has already started when calling suspend_block on a
> >> +suspend_blocker, it will abort the suspend operation as long it has not already
> >> +reached the sysdev suspend stage. This means that calling suspend_block from an
> >> +interrupt handler or a freezeable thread always works, but if you call
> >> +block_suspend from a sysdev suspend handler you must also return an error from
> >> +that handler to abort suspend.
> >> +
> >> +Suspend blockers can be used to allow user-space to decide which keys should
> >> +wake the full system up and turn the screen on.
> >
> > Provided the user mode process controlling the screen power is in the calling
> > path of user-space input-event thread.
> 
> What you mean by this? Our user-space input event thread does not turn
> the screen on, but translates the events and queues the translated
> event on another queue. As long as all queues are protected by a
> suspend blocker, there is no requirement about which thread turns the
> screen on.

I mean that to enable the promised partial system wake up ( "Suspend
blockers can be used to allow user-space to decide which keys should
wake the full system up and turn the screen on.") there is more to the
story as this API is about system level pm not device PM needed to keep
the screen off while partially waking things up before suspending again.

Perhaps an explanation on how suspend blocker can be used to partially
wake up a system without the screen coming on would help me.  Right now
I'm not seeing how that can be implemented in the context of this patch.


 
> >
> > Basically it should be made clear that this is not a device PM
> > implementation.  Its a global system suspend constraint system, where
> > the count of blockers is aggregated in a static variable,
> > suspend_block_count, within suspend_block.c  with implicit suspend
> > called when block count gets to zero.
> >
> > i.e. only useful in platform specific code for platforms that can do
> > ~10ms suspends and resumes.
> 
> I disagree. If I have a desktop system that takes 5 seconds to wake up
> from sleep, I still want the ability to auto suspend after user
> inactivity, and to prevent suspend while some tasks are running.

I guess we have this now, but the user mode implementations don't scale
to low latency and frequent suspend applications.


--mgross


> >
> >> Use set_irq_wake or a platform
> >> +specific api to make sure the keypad interrupt wakes up the cpu. Once the keypad
> >> +driver has resumed, the sequence of events can look like this:
> >> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> >> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> > count = 1
> >> +- The keypad-scan code detects a key change and reports it to the input-event
> >> +  driver.
> >> +- The input-event driver sees the key change, enqueues an event, and calls
> >> +  suspend_block on the input-event-queue suspend_blocker.
> > count = 2
> >> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> >> +  on the keypad-scan suspend_blocker.
> > count = 1
> >> +- The user-space input-event thread returns from select/poll, calls
> >> +  suspend_block on the process-input-events suspend_blocker and then calls read
> >> +  on the input-event device.
> > count = 2
> >> +- The input-event driver dequeues the key-event and, since the queue is now
> >> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> > count = 1
> >> +- The user-space input-event thread returns from read. It determines that the
> >> +  key should not wake up the full system, calls suspend_unblock on the
> >> +  process-input-events suspend_blocker and calls select or poll.
> > count = 0 + race between dropping into Suspend state and the execution
> > of the select or poll.  (should probably be ok)
> 
> This is not a race condition, select or poll does not affect the
> suspend blocker in the input driver to it does not matter if the poll
> is called before or after suspend.
> 
> >
> >> +
> >> +                 Key pressed   Key released
> >> +                     |             |
> >> +keypad-scan          ++++++++++++++++++
> >> +input-event-queue        +++          +++
> >> +process-input-events       +++          +++
> >> +
> >> +
> 
> >> +struct suspend_blocker {
> >> +#ifdef CONFIG_SUSPEND_BLOCK
> >> +     atomic_t            flags;
> >> +     const char         *name;
> >> +#endif
> >> +};
> >> +
> ...
> >> +
> >> +#define SB_INITIALIZED            (1U << 8)
> >> +#define SB_ACTIVE                 (1U << 9)
> >> +
> >> +static DEFINE_SPINLOCK(state_lock);
> >> +static atomic_t suspend_block_count;
> >
> > So if suspend_block_count is the lynch pin of the design, why have all
> > the suspend_blocker structs?  Why have all the logic around the
> > different suspend blocker instances?
> 
> The suspend block structs need to be part of the api to allow stats
> and/or debugging. It also ensures that a single suspend_blocker does
> not change the global count by more than one. Also,
> suspend_block_count is not part of the design, it is an optimization
> that becomes useless when you enable stats.
> 
> >
> > It seems like a lot of extra effort for an atomic "stay awake" counter.
> >
> 
> 
> -- 
> Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-15  1:41 ` [PATCH 1/8] PM: Add suspend " Arve Hjønnevåg
                     ` (2 preceding siblings ...)
  2009-04-15 22:31   ` mark gross
@ 2009-04-20  9:29   ` Pavel Machek
  2009-04-21  4:44     ` Arve Hjønnevåg
  2009-04-29 22:34   ` Rafael J. Wysocki
  4 siblings, 1 reply; 195+ messages in thread
From: Pavel Machek @ 2009-04-20  9:29 UTC (permalink / raw)
  To: Arve Hj??nnev??g; +Cc: ncunningham, u.luckas, swetland, linux-pm

Hi!


> +Driver API
> +==========
> +
> +A driver can use the suspend block api by adding a suspend_blocker variable to
> +its state and calling suspend_blocker_init. For instance:
> +struct state {
> +	struct suspend_blocker suspend_blocker;
> +}
> +
> +init() {
> +	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
> +}
> +
> +Before freeing the memory, suspend_blocker_destroy must be called:
> +
> +uninit() {
> +	suspend_blocker_destroy(&state->suspend_blocker);
> +}
> +
> +When the driver determines that it needs to run (usually in an interrupt
> +handler) it calls suspend_block:
> +	suspend_block(&state->suspend_blocker);
> +
> +When it no longer needs to run it calls suspend_unblock:
> +	suspend_unblock(&state->suspend_blocker);

Sounds reasonably sane. Maybe it should be block_suspend(), because
suspend_block() sounds like... something that builds suspend, but I
guess it is good enough.

> +/* A suspend_blocker prevents the system from entering suspend when active.
> + */
> +

linuxdoc?

> +struct suspend_blocker {
> +#ifdef CONFIG_SUSPEND_BLOCK
> +	atomic_t            flags;
> +	const char         *name;
> +#endif
> +};
> +
> +#ifdef CONFIG_SUSPEND_BLOCK
> +
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
> +void suspend_blocker_destroy(struct suspend_blocker *blocker);
> +void suspend_block(struct suspend_blocker *blocker);
> +void suspend_unblock(struct suspend_blocker *blocker);
> +
> +/* is_blocking_suspend returns true if the suspend_blocker is currently active.
> + */
> +bool is_blocking_suspend(struct suspend_blocker *blocker);

Same here?

> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 23bd4da..9d1df13 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -116,6 +116,16 @@ config SUSPEND_FREEZER
>  
>  	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
>  
> +config SUSPEND_BLOCK
> +	bool "Suspend block"
> +	depends on PM
> +	select RTC_LIB

Is RTC_LIB okay to select?

> @@ -392,6 +393,9 @@ static void suspend_finish(void)
>  
>  
>  static const char * const pm_states[PM_SUSPEND_MAX] = {
> +#ifdef CONFIG_SUSPEND_BLOCK
> +	[PM_SUSPEND_ON]		= "on",
> +#endif
>  	[PM_SUSPEND_STANDBY]	= "standby",
>  	[PM_SUSPEND_MEM]	= "mem",
>  };

This allows you to write "on" to the other interfaces where that makes
no sense.... right?

> +static bool enable_suspend_blockers;

Uhuh... What is this good for... except debugging?

> +bool suspend_is_blocked(void)
> +{
> +	if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
> +		return 0;
> +	return !!atomic_read(&suspend_block_count);
> +}

Yes, if enable_suspend_blockers is one, we are in deep trouble.

> +/**
> + * suspend_blocker_init() - Initialize a suspend blocker
> + * @blocker:	The suspend blocker to initialize.
> + * @name:	The name of the suspend blocker to show in
> + *		/proc/suspend_blockers

I don't think suspend_blockers should go to /proc. They are not process-related.

> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	BUG_ON(!(atomic_read(&blocker->flags) & SB_INITIALIZED));
As blocker->flags seem to be used mostly for debugging; do they have
to use "atomic"? Normally, "magic" variable is used, instead. (Single
bit is probably not enough to guard against using uninitialized
memory.)

> + * suspend_unblock() - Unblock suspend
> + * @blocker:	The suspend blocker to unblock.
> + *
> + * If no other suspend blockers block suspend, the system will suspend.
> + */
> +void suspend_unblock(struct suspend_blocker *blocker)
> +{

...BUG_ON(!(atomic_read(&blocker->flags) & SB_INITIALIZED)); ?

> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_unblock: %s\n", blocker->name);
> +
> +	if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED | SB_ACTIVE,
> +	    SB_INITIALIZED) == (SB_INITIALIZED | SB_ACTIVE))
> +		if (atomic_dec_and_test(&suspend_block_count))
> +			queue_work(suspend_work_queue, &suspend_work);
> +}
> +EXPORT_SYMBOL(suspend_unblock);

Uhuh... can we "loose a oportunity to suspend" here? If someone is
just updating the flags...? Is it guaranteed that if two people do
suspend_unblock at the same time, one of them will pass?

> +void request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +	spin_lock_irqsave(&state_lock, irqflags);
> +	if (debug_mask & DEBUG_USER_STATE) {
> +		struct timespec ts;
> +		struct rtc_time tm;
> +		getnstimeofday(&ts);
> +		rtc_time_to_tm(ts.tv_sec, &tm);
> +		pr_info("request_suspend_state: %s (%d->%d) at %lld "
> +			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
> +			state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> +			requested_suspend_state, state,
> +			ktime_to_ns(ktime_get()),
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);

Introduce macro for printing time?

Otherwise, it looks good...
									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-20  9:29   ` Pavel Machek
@ 2009-04-21  4:44     ` Arve Hjønnevåg
  2009-04-24 20:59       ` Pavel Machek
  0 siblings, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-21  4:44 UTC (permalink / raw)
  To: Pavel Machek; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Mon, Apr 20, 2009 at 2:29 AM, Pavel Machek <pavel@ucw.cz> wrote:

>> +When the driver determines that it needs to run (usually in an interrupt
>> +handler) it calls suspend_block:
>> +     suspend_block(&state->suspend_blocker);
>> +
>> +When it no longer needs to run it calls suspend_unblock:
>> +     suspend_unblock(&state->suspend_blocker);
>
> Sounds reasonably sane. Maybe it should be block_suspend(), because
> suspend_block() sounds like... something that builds suspend, but I
> guess it is good enough.
>

I was trying to keep a common prefix.

>> +/* A suspend_blocker prevents the system from entering suspend when active.
>> + */
>> +
>
> linuxdoc?
>
>> +struct suspend_blocker {
>> +#ifdef CONFIG_SUSPEND_BLOCK
>> +     atomic_t            flags;
>> +     const char         *name;
>> +#endif
>> +};
>> +
>> +#ifdef CONFIG_SUSPEND_BLOCK
>> +
>> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
>> +void suspend_blocker_destroy(struct suspend_blocker *blocker);
>> +void suspend_block(struct suspend_blocker *blocker);
>> +void suspend_unblock(struct suspend_blocker *blocker);
>> +
>> +/* is_blocking_suspend returns true if the suspend_blocker is currently active.
>> + */
>> +bool is_blocking_suspend(struct suspend_blocker *blocker);
>
> Same here?

I moved this to the c file, see diff below.

>
>> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
>> index 23bd4da..9d1df13 100644
>> --- a/kernel/power/Kconfig
>> +++ b/kernel/power/Kconfig
>> @@ -116,6 +116,16 @@ config SUSPEND_FREEZER
>>
>>         Turning OFF this setting is NOT recommended! If in doubt, say Y.
>>
>> +config SUSPEND_BLOCK
>> +     bool "Suspend block"
>> +     depends on PM
>> +     select RTC_LIB
>
> Is RTC_LIB okay to select?

It should be. It cannot be enabled manually.

>> @@ -392,6 +393,9 @@ static void suspend_finish(void)
>>
>>
>>  static const char * const pm_states[PM_SUSPEND_MAX] = {
>> +#ifdef CONFIG_SUSPEND_BLOCK
>> +     [PM_SUSPEND_ON]         = "on",
>> +#endif
>>       [PM_SUSPEND_STANDBY]    = "standby",
>>       [PM_SUSPEND_MEM]        = "mem",
>>  };
>
> This allows you to write "on" to the other interfaces where that makes
> no sense.... right?

state_store starts iterating at PM_SUSPEND_STANDBY, so don't think so.

>
>> +static bool enable_suspend_blockers;
>
> Uhuh... What is this good for... except debugging?

It prevents suspend blockers from affecting existing suspend and
freezing interfaces (while auto suspend is not active).

>
>> +bool suspend_is_blocked(void)
>> +{
>> +     if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
>> +             return 0;
>> +     return !!atomic_read(&suspend_block_count);
>> +}
>
> Yes, if enable_suspend_blockers is one, we are in deep trouble.

It just means that you compiled suspend blocker support into the
kernel, but used an old interface to enter suspend. It warns if
enable_suspend_blockers is 0 not 1.

>
>> +/**
>> + * suspend_blocker_init() - Initialize a suspend blocker
>> + * @blocker: The suspend blocker to initialize.
>> + * @name:    The name of the suspend blocker to show in
>> + *           /proc/suspend_blockers
>
> I don't think suspend_blockers should go to /proc. They are not process-related.

I moved that comment to the stats patch. It is still in procfs though,
as it is most similar to other procfs entries. It is not allowed under
the sysfs rules, and we do not want to enable debugfs on a production
system since it exposes some unsafe interfaces.

>
>> +/**
>> + * suspend_block() - Block suspend
>> + * @blocker: The suspend blocker to use
>> + */
>> +void suspend_block(struct suspend_blocker *blocker)
>> +{
>> +     BUG_ON(!(atomic_read(&blocker->flags) & SB_INITIALIZED));
> As blocker->flags seem to be used mostly for debugging; do they have
> to use "atomic"? Normally, "magic" variable is used, instead. (Single
> bit is probably not enough to guard against using uninitialized
> memory.)

It is mostly used to determine if the suspend blocker is already
active, so yes it has to be atomic. I could add a magic value, but a
single bit is enough to catch uninitialized global and kzalloced
members.

>
>> + * suspend_unblock() - Unblock suspend
>> + * @blocker: The suspend blocker to unblock.
>> + *
>> + * If no other suspend blockers block suspend, the system will suspend.
>> + */
>> +void suspend_unblock(struct suspend_blocker *blocker)
>> +{
>
> ...BUG_ON(!(atomic_read(&blocker->flags) & SB_INITIALIZED)); ?

Perhaps. This function does not do anything unless you have already
called suspend_block though.

>
>> +     if (debug_mask & DEBUG_SUSPEND_BLOCKER)
>> +             pr_info("suspend_unblock: %s\n", blocker->name);
>> +
>> +     if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED | SB_ACTIVE,
>> +         SB_INITIALIZED) == (SB_INITIALIZED | SB_ACTIVE))
>> +             if (atomic_dec_and_test(&suspend_block_count))
>> +                     queue_work(suspend_work_queue, &suspend_work);
>> +}
>> +EXPORT_SYMBOL(suspend_unblock);
>
> Uhuh... can we "loose a oportunity to suspend" here? If someone is
> just updating the flags...? Is it guaranteed that if two people do
> suspend_unblock at the same time, one of them will pass?

What do you mean by just updating the flags? SB_ACTIVE was either
cleared or not, if it was, then atomic_dec_and_test is called.

>
>> +void request_suspend_state(suspend_state_t state)
>> +{
>> +     unsigned long irqflags;
>> +     spin_lock_irqsave(&state_lock, irqflags);
>> +     if (debug_mask & DEBUG_USER_STATE) {
>> +             struct timespec ts;
>> +             struct rtc_time tm;
>> +             getnstimeofday(&ts);
>> +             rtc_time_to_tm(ts.tv_sec, &tm);
>> +             pr_info("request_suspend_state: %s (%d->%d) at %lld "
>> +                     "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
>> +                     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
>> +                     requested_suspend_state, state,
>> +                     ktime_to_ns(ktime_get()),
>> +                     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
>> +                     tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
>
> Introduce macro for printing time?

OK.

-- 
Arve Hjønnevåg

----
diff --git a/Documentation/power/suspend-blockers.txt
b/Documentation/power/suspend-blockers.txt
index 743b870..e95a507 100644
--- a/Documentation/power/suspend-blockers.txt
+++ b/Documentation/power/suspend-blockers.txt
@@ -10,10 +10,14 @@ interrupt handler or a freezeable thread always
works, but if you call
 block_suspend from a sysdev suspend handler you must also return an error from
 that handler to abort suspend.

-Suspend blockers can be used to allow user-space to decide which keys should
-wake the full system up and turn the screen on. Use set_irq_wake or a platform
-specific api to make sure the keypad interrupt wakes up the cpu. Once
the keypad
-driver has resumed, the sequence of events can look like this:
+In cell phones or other embedded systems where powering the screen is a
+significant drain on the battery, suspend blockers can be used to allow
+user-space to decide whether a keystroke received while the system is suspended
+should cause the screen to be turned back on or allow the system to
go back into
+suspend. Use set_irq_wake or a platform specific api to make sure the keypad
+interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
+events can look like this:
+
 - The Keypad driver gets an interrupt. It then calls suspend_block on the
   keypad-scan suspend_blocker and starts scanning the keypad matrix.
 - The keypad-scan code detects a key change and reports it to the input-event
@@ -27,9 +31,11 @@ driver has resumed, the sequence of events can look
like this:
   on the input-event device.
 - The input-event driver dequeues the key-event and, since the queue is now
   empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
-- The user-space input-event thread returns from read. It determines that the
-  key should not wake up the full system, calls suspend_unblock on the
-  process-input-events suspend_blocker and calls select or poll.
+- The user-space input-event thread returns from read. If it determines that
+  the key should leave the screen off, it calls suspend_unblock on the
+  process_input_events suspend_blocker and then calls select or poll. The
+  system will automatically suspend again, since now no suspend blockers are
+  active.

                  Key pressed   Key released
                      |             |
diff --git a/include/linux/suspend_block.h b/include/linux/suspend_block.h
index 7820c60..54bf9c4 100755
--- a/include/linux/suspend_block.h
+++ b/include/linux/suspend_block.h
@@ -16,7 +16,15 @@
 #ifndef _LINUX_SUSPEND_BLOCK_H
 #define _LINUX_SUSPEND_BLOCK_H

-/* A suspend_blocker prevents the system from entering suspend when active.
+/**
+ * struct suspend_blocker - the basic suspend_blocker structure
+ * @flags:     Tracks initialized and active state.
+ * @name:      Name used for debugging.
+ *
+ * When a suspend_blocker is active it prevents the system from entering
+ * suspend.
+ *
+ * The suspend_blocker structure must be initialized by suspend_blocker_init()
  */

 struct suspend_blocker {
@@ -32,17 +40,7 @@ void suspend_blocker_init(struct suspend_blocker
*blocker, const char *name);
 void suspend_blocker_destroy(struct suspend_blocker *blocker);
 void suspend_block(struct suspend_blocker *blocker);
 void suspend_unblock(struct suspend_blocker *blocker);
-
-/* is_blocking_suspend returns true if the suspend_blocker is currently active.
- */
 bool is_blocking_suspend(struct suspend_blocker *blocker);
-
-/* suspend_is_blocked can be used by generic power management code to abort
- * suspend.
- *
- * To preserve backward compatibility suspend_is_blocked returns 0 unless it
- * is called during suspend initiated from the suspend_block code.
- */
 bool suspend_is_blocked(void);

 #else
diff --git a/kernel/power/suspend_block.c b/kernel/power/suspend_block.c
index b4f2191..1adf075 100644
--- a/kernel/power/suspend_block.c
+++ b/kernel/power/suspend_block.c
@@ -41,6 +41,27 @@ struct suspend_blocker main_suspend_blocker;
 static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
 static bool enable_suspend_blockers;

+#define pr_info_time(fmt, args...) \
+       do { \
+               struct timespec ts; \
+               struct rtc_time tm; \
+               getnstimeofday(&ts); \
+               rtc_time_to_tm(ts.tv_sec, &tm); \
+               pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
+                       args, \
+                       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
+                       tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
+       } while (0);
+
+/**
+ * suspend_is_blocked() - Check if suspend should be blocked
+ *
+ * suspend_is_blocked can be used by generic power management code to abort
+ * suspend.
+ *
+ * To preserve backward compatibility suspend_is_blocked returns 0 unless it
+ * is called during suspend initiated from the suspend_block code.
+ */
 bool suspend_is_blocked(void)
 {
        if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
@@ -66,16 +87,8 @@ retry:
        if (debug_mask & DEBUG_SUSPEND)
                pr_info("suspend: enter suspend\n");
        ret = pm_suspend(requested_suspend_state);
-       if (debug_mask & DEBUG_EXIT_SUSPEND) {
-               struct timespec ts;
-               struct rtc_time tm;
-               getnstimeofday(&ts);
-               rtc_time_to_tm(ts.tv_sec, &tm);
-               pr_info("suspend: exit suspend, ret = %d "
-                       "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
-                       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
-                       tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
-       }
+       if (debug_mask & DEBUG_EXIT_SUSPEND)
+               pr_info_time("suspend: exit suspend, ret = %d ", ret);
        if (atomic_read(&current_event_num) == entry_event_num) {
                if (debug_mask & DEBUG_SUSPEND)
                        pr_info("suspend: pm_suspend returned with no event\n");
@@ -105,8 +118,7 @@ static struct sys_device suspend_block_sysdev = {
 /**
  * suspend_blocker_init() - Initialize a suspend blocker
  * @blocker:   The suspend blocker to initialize.
- * @name:      The name of the suspend blocker to show in
- *             /proc/suspend_blockers
+ * @name:      The name of the suspend blocker to show in debug messages.
  */
 void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
 {
@@ -178,6 +190,8 @@ EXPORT_SYMBOL(suspend_unblock);
 /**
  * is_blocking_suspend() - Test if a suspend blocker is blocking suspend
  * @blocker:   The suspend blocker to check.
+ *
+ * Returns true if the suspend_blocker is currently active.
  */
 bool is_blocking_suspend(struct suspend_blocker *blocker)
 {
@@ -189,19 +203,11 @@ void request_suspend_state(suspend_state_t state)
 {
        unsigned long irqflags;
        spin_lock_irqsave(&state_lock, irqflags);
-       if (debug_mask & DEBUG_USER_STATE) {
-               struct timespec ts;
-               struct rtc_time tm;
-               getnstimeofday(&ts);
-               rtc_time_to_tm(ts.tv_sec, &tm);
-               pr_info("request_suspend_state: %s (%d->%d) at %lld "
-                       "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
-                       state != PM_SUSPEND_ON ? "sleep" : "wakeup",
-                       requested_suspend_state, state,
-                       ktime_to_ns(ktime_get()),
-                       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
-                       tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
-       }
+       if (debug_mask & DEBUG_USER_STATE)
+               pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
+                            state != PM_SUSPEND_ON ? "sleep" : "wakeup",
+                            requested_suspend_state, state,
+                            ktime_to_ns(ktime_get()));
        requested_suspend_state = state;
        if (state == PM_SUSPEND_ON)
                suspend_block(&main_suspend_blocker);

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-21  4:44     ` Arve Hjønnevåg
@ 2009-04-24 20:59       ` Pavel Machek
  2009-04-29 21:24         ` Rafael J. Wysocki
  0 siblings, 1 reply; 195+ messages in thread
From: Pavel Machek @ 2009-04-24 20:59 UTC (permalink / raw)
  To: Arve Hj?nnev?g; +Cc: ncunningham, u.luckas, swetland, linux-pm

Hi!

> >> @@ -392,6 +393,9 @@ static void suspend_finish(void)
> >>
> >>
> >>  static const char * const pm_states[PM_SUSPEND_MAX] = {
> >> +#ifdef CONFIG_SUSPEND_BLOCK
> >> +     [PM_SUSPEND_ON]         = "on",
> >> +#endif
> >>       [PM_SUSPEND_STANDBY]    = "standby",
> >>       [PM_SUSPEND_MEM]        = "mem",
> >>  };
> >
> > This allows you to write "on" to the other interfaces where that makes
> > no sense.... right?
> 
> state_store starts iterating at PM_SUSPEND_STANDBY, so don't think so.

Good.


> >> +bool suspend_is_blocked(void)
> >> +{
> >> +     if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
> >> +             return 0;
> >> +     return !!atomic_read(&suspend_block_count);
> >> +}
> >
> > Yes, if enable_suspend_blockers is one, we are in deep trouble.
> 
> It just means that you compiled suspend blocker support into the
> kernel, but used an old interface to enter suspend. It warns if
> enable_suspend_blockers is 0 not 1.

I do not think WARN_ONCE() should be that easy to trigger. WARN is
very scary animal; better use normal printk?



> >> +/**
> >> + * suspend_blocker_init() - Initialize a suspend blocker
> >> + * @blocker: The suspend blocker to initialize.
> >> + * @name:    The name of the suspend blocker to show in
> >> + *           /proc/suspend_blockers
> >
> > I don't think suspend_blockers should go to /proc. They are not process-related.
> 
> I moved that comment to the stats patch. It is still in procfs though,
> as it is most similar to other procfs entries. It is not allowed under
> the sysfs rules, and we do not want to enable debugfs on a production
> system since it exposes some unsafe interfaces.

You have to sort this out somehow. It does not belong to /proc.

									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-30  0:49     ` Arve Hjønnevåg
@ 2009-04-26  9:42       ` Pavel Machek
  2009-05-02 12:17         ` Rafael J. Wysocki
  2009-05-02 12:14       ` Rafael J. Wysocki
  1 sibling, 1 reply; 195+ messages in thread
From: Pavel Machek @ 2009-04-26  9:42 UTC (permalink / raw)
  To: Arve Hj?nnev?g; +Cc: ncunningham, u.luckas, swetland, linux-pm

Hi!

> >> --- a/kernel/power/power.h
> >> +++ b/kernel/power/power.h
> >> @@ -223,3 +223,9 @@ static inline void suspend_thaw_processes(void)
> >>  {
> >>  }
> >>  #endif
> >> +
> >> +#ifdef CONFIG_SUSPEND_BLOCK
> >> +/* kernel/power/suspend_block.c */
> >> +void request_suspend_state(suspend_state_t state);
> >> +#endif
> >
> > Is the #ifdef really necessary?
> >
> 
> No, but it gives a compile error instead of a link error if you call
> request_suspend_state when it is not available so it is useful.

I believe #ifdef is too ugly and link error is really good enough.

  	  	    	     	      	       	      	   Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-24 20:59       ` Pavel Machek
@ 2009-04-29 21:24         ` Rafael J. Wysocki
  2009-04-29 22:52           ` Arve Hjønnevåg
  0 siblings, 1 reply; 195+ messages in thread
From: Rafael J. Wysocki @ 2009-04-29 21:24 UTC (permalink / raw)
  To: Pavel Machek; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Friday 24 April 2009, Pavel Machek wrote:
> Hi!
> 
> > >> @@ -392,6 +393,9 @@ static void suspend_finish(void)
> > >>
> > >>
> > >>  static const char * const pm_states[PM_SUSPEND_MAX] = {
> > >> +#ifdef CONFIG_SUSPEND_BLOCK
> > >> +     [PM_SUSPEND_ON]         = "on",
> > >> +#endif
> > >>       [PM_SUSPEND_STANDBY]    = "standby",
> > >>       [PM_SUSPEND_MEM]        = "mem",
> > >>  };
> > >
> > > This allows you to write "on" to the other interfaces where that makes
> > > no sense.... right?
> > 
> > state_store starts iterating at PM_SUSPEND_STANDBY, so don't think so.
> 
> Good.
> 
> 
> > >> +bool suspend_is_blocked(void)
> > >> +{
> > >> +     if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
> > >> +             return 0;
> > >> +     return !!atomic_read(&suspend_block_count);
> > >> +}
> > >
> > > Yes, if enable_suspend_blockers is one, we are in deep trouble.
> > 
> > It just means that you compiled suspend blocker support into the
> > kernel, but used an old interface to enter suspend. It warns if
> > enable_suspend_blockers is 0 not 1.
> 
> I do not think WARN_ONCE() should be that easy to trigger. WARN is
> very scary animal; better use normal printk?

Yes, printk() please.

> > >> +/**
> > >> + * suspend_blocker_init() - Initialize a suspend blocker
> > >> + * @blocker: The suspend blocker to initialize.
> > >> + * @name:    The name of the suspend blocker to show in
> > >> + *           /proc/suspend_blockers
> > >
> > > I don't think suspend_blockers should go to /proc. They are not process-related.
> > 
> > I moved that comment to the stats patch. It is still in procfs though,
> > as it is most similar to other procfs entries. It is not allowed under
> > the sysfs rules, and we do not want to enable debugfs on a production
> > system since it exposes some unsafe interfaces.
> 
> You have to sort this out somehow. It does not belong to /proc.

Agreed.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-15  1:41 ` [PATCH 1/8] PM: Add suspend " Arve Hjønnevåg
                     ` (3 preceding siblings ...)
  2009-04-20  9:29   ` Pavel Machek
@ 2009-04-29 22:34   ` Rafael J. Wysocki
  2009-04-29 23:45     ` Arve Hjønnevåg
  2009-04-30  0:49     ` Arve Hjønnevåg
  4 siblings, 2 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2009-04-29 22:34 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Wednesday 15 April 2009, Arve Hjønnevåg wrote:
> Adds /sys/power/request_state, a non-blocking interface that specifies
> which suspend state to enter when no suspend blockers are active. A
> special state, "on", stops the process by activating the "main" suspend
> blocker.

Finally I have some time to look at this more thoroughly, sorry for the long
delay.

I'll try not to repeate the others' comments, but I guess that may not be
totally avoidable.

> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> ---
>  Documentation/power/suspend-blockers.txt |   76 +++++++++
>  include/linux/suspend_block.h            |   61 +++++++
>  kernel/power/Kconfig                     |   10 ++
>  kernel/power/Makefile                    |    1 +
>  kernel/power/main.c                      |   62 +++++++
>  kernel/power/power.h                     |    6 +
>  kernel/power/suspend_block.c             |  257 ++++++++++++++++++++++++++++++
>  7 files changed, 473 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/power/suspend-blockers.txt
>  create mode 100755 include/linux/suspend_block.h
>  create mode 100644 kernel/power/suspend_block.c
> 
> diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
> new file mode 100644
> index 0000000..743b870
> --- /dev/null
> +++ b/Documentation/power/suspend-blockers.txt
> @@ -0,0 +1,76 @@
> +Suspend blockers
> +================
> +
> +A suspend_blocker prevents the system from entering suspend.

I'd say "suspend blocker", without the underscore.

Also, I'd try to provide a general explanation of how this is supposed to
work here, as an introduction.  Something like:

"Suspend blockers provide a mechanism allowing device drivers and user
space processes to prevent the system from entering a sleep state, such as
the ACPI S3 state.

To use a suspend blocker, a device driver needs a struct suspend_blocker object
that has to be initialized with the help of suspend_blocker_init().  When no
longer needed, the suspend blocker must be garbage collected with the help
of suspend_blocker_destroy().

A suspend blocker is activated using suspend_block(), which prevents the
system from entering any sleep state until the suspend blocker is deactivated
with suspend_unblock().  Many suspend blockers may be used simultaneously
and the system is prevented from entering sleep states as long as at least one
of them is active."

BTW, I don't really like the names "suspend_block" and "suspend_unblock".
See below for possible alternatives.

> +If the suspend operation has already started when calling suspend_block on a
> +suspend_blocker, it will abort the suspend operation as long it has not already
> +reached the sysdev suspend stage. This means that calling suspend_block from an
> +interrupt handler or a freezeable thread always works, but if you call
> +block_suspend

Surely suspend_block() ?

> from a sysdev suspend handler you must also return an error from
> +that handler to abort suspend.

Well, if a sysdev suspend handler returns an error code, suspend will be
aborted anyway, so I'm not sure what the point in using suspend blockers from
sysdev suspend handlers really is.  Perhaps it's better to say "don't use
suspend blockers from withing sysdev suspend handlers"?

> +

I'd say "For example" here.

> +Suspend blockers can be used to allow user-space to decide which keys should
> +wake the full system up and turn the screen on. Use set_irq_wake or a platform
> +specific api to make sure the keypad interrupt wakes up the cpu. Once the keypad
> +driver has resumed, the sequence of events can look like this:
> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> +- The keypad-scan code detects a key change and reports it to the input-event
> +  driver.
> +- The input-event driver sees the key change, enqueues an event, and calls
> +  suspend_block on the input-event-queue suspend_blocker.
> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> +  on the keypad-scan suspend_blocker.
> +- The user-space input-event thread returns from select/poll, calls
> +  suspend_block on the process-input-events suspend_blocker and then calls read
> +  on the input-event device.
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> +- The user-space input-event thread returns from read. It determines that the
> +  key should not wake up the full system, calls suspend_unblock on the
> +  process-input-events suspend_blocker and calls select or poll.
> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++
> +
> +
> +Driver API
> +==========
> +
> +A driver can use the suspend block api by adding a suspend_blocker variable to
> +its state and calling suspend_blocker_init. For instance:
> +struct state {
> +	struct suspend_blocker suspend_blocker;
> +}
> +
> +init() {
> +	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
> +}
> +
> +Before freeing the memory, suspend_blocker_destroy must be called:
> +
> +uninit() {
> +	suspend_blocker_destroy(&state->suspend_blocker);
> +}
> +
> +When the driver determines that it needs to run (usually in an interrupt
> +handler) it calls suspend_block:
> +	suspend_block(&state->suspend_blocker);

suspend_blocker_activate() ?
suspend_blocker_enable() ?
suspend_blocker_start() ?

> +
> +When it no longer needs to run it calls suspend_unblock:
> +	suspend_unblock(&state->suspend_blocker);

suspend_blocker_deactivate() ?
suspend_blocker_disable() ?
suspend_blocker_finish() ?

> +
> +Calling suspend_block when the suspend blocker is active or suspend_unblock when
> +it is not active has no effect. This allows drivers to update their state and
> +call suspend suspend_block or suspend_unblock based on the result.
> +For instance:
> +
> +if (list_empty(&state->pending_work))
> +	suspend_unblock(&state->suspend_blocker);
> +else
> +	suspend_block(&state->suspend_blocker);
> +
> diff --git a/include/linux/suspend_block.h b/include/linux/suspend_block.h
> new file mode 100755
> index 0000000..7820c60
> --- /dev/null
> +++ b/include/linux/suspend_block.h

suspend_blockers.h perhaps?

> @@ -0,0 +1,61 @@
> +/* include/linux/suspend_block.h
> + *
> + * Copyright (C) 2007-2009 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _LINUX_SUSPEND_BLOCK_H
> +#define _LINUX_SUSPEND_BLOCK_H

SUSPEND_BLOCKERS_H ?

> +
> +/* A suspend_blocker prevents the system from entering suspend when active.
> + */
> +
> +struct suspend_blocker {
> +#ifdef CONFIG_SUSPEND_BLOCK

CONFIG_SUSPEND_BLOCKERS ?

> +	atomic_t            flags;
> +	const char         *name;
> +#endif
> +};
> +
> +#ifdef CONFIG_SUSPEND_BLOCK
> +
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
> +void suspend_blocker_destroy(struct suspend_blocker *blocker);
> +void suspend_block(struct suspend_blocker *blocker);
> +void suspend_unblock(struct suspend_blocker *blocker);
> +
> +/* is_blocking_suspend returns true if the suspend_blocker is currently active.
> + */
> +bool is_blocking_suspend(struct suspend_blocker *blocker);

suspend_blocker_is_active() ?
suspend_blocker_enabled() ?

> +
> +/* suspend_is_blocked can be used by generic power management code to abort
> + * suspend.
> + *
> + * To preserve backward compatibility suspend_is_blocked returns 0 unless it
> + * is called during suspend initiated from the suspend_block code.
> + */
> +bool suspend_is_blocked(void);
> +
> +#else
> +
> +static inline void suspend_blocker_init(struct suspend_blocker *blocker,
> +					const char *name) {}
> +static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
> +static inline void suspend_block(struct suspend_blocker *blocker) {}
> +static inline void suspend_unblock(struct suspend_blocker *blocker) {}
> +static inline bool is_blocking_suspend(struct suspend_blocker *bl) { return 0; }
> +static inline bool suspend_is_blocked(void) { return 0; }
> +
> +#endif
> +
> +#endif
> +
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 23bd4da..9d1df13 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -116,6 +116,16 @@ config SUSPEND_FREEZER
>  
>  	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
>  
> +config SUSPEND_BLOCK
> +	bool "Suspend block"
> +	depends on PM
> +	select RTC_LIB

depends on RTC_LIB

select doesn't really work and I don't think it ever will.

> +	default n
> +	---help---
> +	  Enable suspend_block.

Enable suspend blockers.

> When user space requests a sleep state through
> +	  /sys/power/request_state, the requested sleep state will be entered
> +	  when no suspend_blockers are active.

Well, if you don't want suspend blockers to block suspend started via
/sys/power/state, it's worth making it crystal clear in the documentation too.

> +
>  config HIBERNATION
>  	bool "Hibernation (aka 'suspend to disk')"
>  	depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
> index 720ea4f..29cdc9e 100644
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -6,6 +6,7 @@ endif
>  obj-$(CONFIG_PM)		+= main.o
>  obj-$(CONFIG_PM_SLEEP)		+= console.o
>  obj-$(CONFIG_FREEZER)		+= process.o
> +obj-$(CONFIG_SUSPEND_BLOCK)	+= suspend_block.o
>  obj-$(CONFIG_HIBERNATION)	+= swsusp.o disk.o snapshot.o swap.o user.o
>  
>  obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
> diff --git a/kernel/power/main.c b/kernel/power/main.c
> index c9632f8..e4c6b20 100644
> --- a/kernel/power/main.c
> +++ b/kernel/power/main.c
> @@ -22,6 +22,7 @@
>  #include <linux/freezer.h>
>  #include <linux/vmstat.h>
>  #include <linux/syscalls.h>
> +#include <linux/suspend_block.h>
>  
>  #include "power.h"
>  
> @@ -392,6 +393,9 @@ static void suspend_finish(void)
>  
>  
>  static const char * const pm_states[PM_SUSPEND_MAX] = {
> +#ifdef CONFIG_SUSPEND_BLOCK
> +	[PM_SUSPEND_ON]		= "on",
> +#endif

Why #ifdef?

>  	[PM_SUSPEND_STANDBY]	= "standby",
>  	[PM_SUSPEND_MEM]	= "mem",
>  };
> @@ -540,6 +544,61 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  
>  power_attr(state);
>  
> +/**
> + *	request_state - control system power state.
> + *
> + *	This is similar to state, but it does not block until the system
> + *	resumes, and it will try to re-enter the state until another state is
> + *	requested. Suspend blockers are respected and the requested state will
> + *	only be entered when no suspend blockers are active.
> + *	Write "on" to cancel.
> + */
> +
> +#ifdef CONFIG_SUSPEND_BLOCK
> +static ssize_t request_state_show(struct kobject *kobj,
> +				  struct kobj_attribute *attr, char *buf)
> +{
> +	char *s = buf;
> +	int i;
> +
> +	for (i = 0; i < PM_SUSPEND_MAX; i++) {
> +		if (pm_states[i] && (i == PM_SUSPEND_ON || valid_state(i)))
> +			s += sprintf(s, "%s ", pm_states[i]);
> +	}
> +	if (s != buf)
> +		/* convert the last space to a newline */
> +		*(s-1) = '\n';
> +	return (s - buf);
> +}
> +
> +static ssize_t request_state_store(struct kobject *kobj,
> +				   struct kobj_attribute *attr,
> +				   const char *buf, size_t n)
> +{
> +	suspend_state_t state = PM_SUSPEND_ON;
> +	const char * const *s;
> +	char *p;
> +	int len;
> +	int error = -EINVAL;
> +
> +	p = memchr(buf, '\n', n);
> +	len = p ? p - buf : n;
> +
> +	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
> +		if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
> +			break;
> +	}
> +	if (state < PM_SUSPEND_MAX && *s)
> +		if (state == PM_SUSPEND_ON || valid_state(state)) {
> +			error = 0;
> +			request_suspend_state(state);
> +		}
> +	return error ? error : n;
> +}
> +
> +power_attr(request_state);
> +#endif /* CONFIG_SUSPEND_BLOCK */
> +
>  #ifdef CONFIG_PM_TRACE
>  int pm_trace_enabled;
>  
> @@ -567,6 +626,9 @@ power_attr(pm_trace);
>  
>  static struct attribute * g[] = {
>  	&state_attr.attr,
> +#ifdef CONFIG_SUSPEND_BLOCK
> +	&request_state_attr.attr,
> +#endif
>  #ifdef CONFIG_PM_TRACE
>  	&pm_trace_attr.attr,
>  #endif
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index 46b5ec7..2414a74 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -223,3 +223,9 @@ static inline void suspend_thaw_processes(void)
>  {
>  }
>  #endif
> +
> +#ifdef CONFIG_SUSPEND_BLOCK
> +/* kernel/power/suspend_block.c */
> +void request_suspend_state(suspend_state_t state);
> +#endif

Is the #ifdef really necessary?

> +
> diff --git a/kernel/power/suspend_block.c b/kernel/power/suspend_block.c
> new file mode 100644
> index 0000000..b4f2191
> --- /dev/null
> +++ b/kernel/power/suspend_block.c
> @@ -0,0 +1,257 @@
> +/* kernel/power/suspend_block.c
> + *
> + * Copyright (C) 2005-2008 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/suspend.h>
> +#include <linux/suspend_block.h>
> +#include <linux/sysdev.h>
> +#include "power.h"
> +
> +enum {
> +	DEBUG_EXIT_SUSPEND = 1U << 0,
> +	DEBUG_WAKEUP = 1U << 1,
> +	DEBUG_USER_STATE = 1U << 2,
> +	DEBUG_SUSPEND = 1U << 3,
> +	DEBUG_SUSPEND_BLOCKER = 1U << 4,
> +};
> +static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
> +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
> +
> +#define SB_INITIALIZED            (1U << 8)
> +#define SB_ACTIVE                 (1U << 9)
> +
> +static DEFINE_SPINLOCK(state_lock);
> +static atomic_t suspend_block_count;
> +static atomic_t current_event_num;
> +struct workqueue_struct *suspend_work_queue;
> +struct suspend_blocker main_suspend_blocker;
> +static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
> +static bool enable_suspend_blockers;
> +
> +bool suspend_is_blocked(void)
> +{
> +	if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
> +		return 0;
> +	return !!atomic_read(&suspend_block_count);
> +}
> +
> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = 1;

'true' instead of '1', please.

> +
> +retry:
> +	if (suspend_is_blocked()) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: abort suspend\n");
> +		goto abort;

If that were in a loop you could just use 'break' here.

> +	}
> +
> +	entry_event_num = atomic_read(&current_event_num);
> +	if (debug_mask & DEBUG_SUSPEND)
> +		pr_info("suspend: enter suspend\n");
> +	ret = pm_suspend(requested_suspend_state);
> +	if (debug_mask & DEBUG_EXIT_SUSPEND) {
> +		struct timespec ts;
> +		struct rtc_time tm;
> +		getnstimeofday(&ts);
> +		rtc_time_to_tm(ts.tv_sec, &tm);
> +		pr_info("suspend: exit suspend, ret = %d "
> +			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
> +	}
> +	if (atomic_read(&current_event_num) == entry_event_num) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +		goto retry;

Put that into a loop and use 'continue' here?  Or use 'break' to go out of
the loop?

> +	}
> +abort:
> +	enable_suspend_blockers = 0;

'false' instead of '0', please.

> +}
> +static DECLARE_WORK(suspend_work, suspend_worker);
> +
> +static int suspend_block_suspend(struct sys_device *dev, pm_message_t state)
> +{
> +	int ret = suspend_is_blocked() ? -EAGAIN : 0;
> +	if (debug_mask & DEBUG_SUSPEND)
> +		pr_info("suspend_block_suspend return %d\n", ret);
> +	return ret;
> +}
> +
> +static struct sysdev_class suspend_block_sysclass = {
> +	.name = "suspend_block",
> +	.suspend = suspend_block_suspend,
> +};
> +static struct sys_device suspend_block_sysdev = {
> +	.cls = &suspend_block_sysclass,
> +};
> +

Hmm.  Perhaps add the suspend_is_blocked() check at the beginning of
sysdev_suspend() instead of this?  Surely you don't want to suspend
any sysdevs with any suspend blockers active, right?

> +/**
> + * suspend_blocker_init() - Initialize a suspend blocker
> + * @blocker:	The suspend blocker to initialize.
> + * @name:	The name of the suspend blocker to show in
> + *		/proc/suspend_blockers
> + */
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
> +{
> +	if (name)
> +		blocker->name = name;
> +	BUG_ON(!blocker->name);

Do you really want to crash the kernel in this case?  WARN_ON() might be better IMO.

Also, this assumes that the caller won't release the memory used to store the
'name' string.  it might be a good idea to point that out in the kerneldoc
comment.

> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_init name=%s\n", blocker->name);
> +
> +	atomic_set(&blocker->flags, SB_INITIALIZED);
> +}
> +EXPORT_SYMBOL(suspend_blocker_init);
> +
> +/**
> + * suspend_blocker_destroy() - Destroy a suspend blocker
> + * @blocker:	The suspend blocker to destroy.
> + */
> +void suspend_blocker_destroy(struct suspend_blocker *blocker)
> +{
> +	int flags;
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
> +	flags = atomic_xchg(&blocker->flags, 0);
> +	WARN(!(flags & SB_INITIALIZED), "suspend_blocker_destroy called on "
> +					"uninitialized suspend_blocker\n");
> +	if (flags == (SB_INITIALIZED | SB_ACTIVE))
> +		if (atomic_dec_and_test(&suspend_block_count))
> +			queue_work(suspend_work_queue, &suspend_work);
> +}
> +EXPORT_SYMBOL(suspend_blocker_destroy);
> +
> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	BUG_ON(!(atomic_read(&blocker->flags) & SB_INITIALIZED));

WARN_ON() and exit instead?  BUG_ON() is a bit drastic IMHO.

> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +	if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED,
> +	    SB_INITIALIZED | SB_ACTIVE) == SB_INITIALIZED)
> +		atomic_inc(&suspend_block_count);
> +
> +	atomic_inc(&current_event_num);
> +}
> +EXPORT_SYMBOL(suspend_block);
> +
> +/**
> + * suspend_unblock() - Unblock suspend
> + * @blocker:	The suspend blocker to unblock.
> + *
> + * If no other suspend blockers block suspend, the system will suspend.
> + */
> +void suspend_unblock(struct suspend_blocker *blocker)
> +{
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_unblock: %s\n", blocker->name);
> +
> +	if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED | SB_ACTIVE,
> +	    SB_INITIALIZED) == (SB_INITIALIZED | SB_ACTIVE))
> +		if (atomic_dec_and_test(&suspend_block_count))
> +			queue_work(suspend_work_queue, &suspend_work);
> +}
> +EXPORT_SYMBOL(suspend_unblock);
> +
> +/**
> + * is_blocking_suspend() - Test if a suspend blocker is blocking suspend
> + * @blocker:	The suspend blocker to check.
> + */
> +bool is_blocking_suspend(struct suspend_blocker *blocker)
> +{
> +	return !!(atomic_read(&blocker->flags) & SB_ACTIVE);

Check SB_INITIALIZED too, perhaps?  WARN_ON(!SB_INITIALIZED)?

> +}
> +EXPORT_SYMBOL(is_blocking_suspend);
> +
> +void request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +	spin_lock_irqsave(&state_lock, irqflags);
> +	if (debug_mask & DEBUG_USER_STATE) {
> +		struct timespec ts;
> +		struct rtc_time tm;
> +		getnstimeofday(&ts);
> +		rtc_time_to_tm(ts.tv_sec, &tm);
> +		pr_info("request_suspend_state: %s (%d->%d) at %lld "
> +			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
> +			state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> +			requested_suspend_state, state,
> +			ktime_to_ns(ktime_get()),
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
> +	}
> +	requested_suspend_state = state;
> +	if (state == PM_SUSPEND_ON)
> +		suspend_block(&main_suspend_blocker);
> +	else
> +		suspend_unblock(&main_suspend_blocker);
> +	spin_unlock_irqrestore(&state_lock, irqflags);
> +}
> +
> +static int __init suspend_block_init(void)
> +{
> +	int ret;
> +
> +	suspend_blocker_init(&main_suspend_blocker, "main");
> +	suspend_block(&main_suspend_blocker);
> +
> +	ret = sysdev_class_register(&suspend_block_sysclass);
> +	if (ret) {
> +		pr_err("suspend_block_init: sysdev_class_register failed\n");
> +		goto err_sysdev_class_register;
> +	}
> +	ret = sysdev_register(&suspend_block_sysdev);
> +	if (ret) {
> +		pr_err("suspend_block_init: suspend_block_sysdev failed\n");
> +		goto err_sysdev_register;
> +	}
> +
> +	suspend_work_queue = create_singlethread_workqueue("suspend");

Do we need a separate workqueue for this purpose?

> +	if (suspend_work_queue == NULL) {
> +		ret = -ENOMEM;
> +		goto err_suspend_work_queue;
> +	}
> +
> +	return 0;
> +
> +err_suspend_work_queue:
> +	sysdev_unregister(&suspend_block_sysdev);
> +err_sysdev_register:
> +	sysdev_class_unregister(&suspend_block_sysclass);
> +err_sysdev_class_register:
> +	suspend_blocker_destroy(&main_suspend_blocker);
> +	return ret;
> +}
> +
> +static void  __exit suspend_block_exit(void)
> +{
> +	destroy_workqueue(suspend_work_queue);
> +	sysdev_unregister(&suspend_block_sysdev);
> +	sysdev_class_unregister(&suspend_block_sysclass);
> +	suspend_blocker_destroy(&main_suspend_blocker);
> +}
> +
> +core_initcall(suspend_block_init);
> +module_exit(suspend_block_exit);

Thanks,
Rafael
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-29 21:24         ` Rafael J. Wysocki
@ 2009-04-29 22:52           ` Arve Hjønnevåg
  0 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-29 22:52 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Wed, Apr 29, 2009 at 2:24 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Friday 24 April 2009, Pavel Machek wrote:
>> Hi!
>>
>> > >> @@ -392,6 +393,9 @@ static void suspend_finish(void)
>> > >>
>> > >>
>> > >>  static const char * const pm_states[PM_SUSPEND_MAX] = {
>> > >> +#ifdef CONFIG_SUSPEND_BLOCK
>> > >> +     [PM_SUSPEND_ON]         = "on",
>> > >> +#endif
>> > >>       [PM_SUSPEND_STANDBY]    = "standby",
>> > >>       [PM_SUSPEND_MEM]        = "mem",
>> > >>  };
>> > >
>> > > This allows you to write "on" to the other interfaces where that makes
>> > > no sense.... right?
>> >
>> > state_store starts iterating at PM_SUSPEND_STANDBY, so don't think so.
>>
>> Good.
>>
>>
>> > >> +bool suspend_is_blocked(void)
>> > >> +{
>> > >> +     if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
>> > >> +             return 0;
>> > >> +     return !!atomic_read(&suspend_block_count);
>> > >> +}
>> > >
>> > > Yes, if enable_suspend_blockers is one, we are in deep trouble.
>> >
>> > It just means that you compiled suspend blocker support into the
>> > kernel, but used an old interface to enter suspend. It warns if
>> > enable_suspend_blockers is 0 not 1.
>>
>> I do not think WARN_ONCE() should be that easy to trigger. WARN is
>> very scary animal; better use normal printk?
>
> Yes, printk() please.

Using printk instead of WARN_ONCE would be much more verbose, so I
removed it instead.

>
>> > >> +/**
>> > >> + * suspend_blocker_init() - Initialize a suspend blocker
>> > >> + * @blocker: The suspend blocker to initialize.
>> > >> + * @name:    The name of the suspend blocker to show in
>> > >> + *           /proc/suspend_blockers
>> > >
>> > > I don't think suspend_blockers should go to /proc. They are not process-related.
>> >
>> > I moved that comment to the stats patch. It is still in procfs though,
>> > as it is most similar to other procfs entries. It is not allowed under
>> > the sysfs rules, and we do not want to enable debugfs on a production
>> > system since it exposes some unsafe interfaces.
>>
>> You have to sort this out somehow. It does not belong to /proc.
>
> Agreed.

OK. I moved it to debugfs.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space
  2009-04-15  1:41   ` [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space Arve Hjønnevåg
  2009-04-15  1:41     ` [PATCH 3/8] PM: suspend_block: Abort task freezing if a suspend_blocker is active Arve Hjønnevåg
@ 2009-04-29 22:52     ` Rafael J. Wysocki
  1 sibling, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2009-04-29 22:52 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Wednesday 15 April 2009, Arve Hjønnevåg wrote:
> Adds ioctls to create a suspend_blocker, and to block and unblock suspend.
> To delete the suspend_blocker, close the device.

The changelog is minimalistic, so to speak.

Can you please describe the idea in more detail in it?

Also, perhaps you could use write() to initialize the suspend blocker?
I don't know really, it's just a thought.

You might not need ioctls() at all if you used write() like
write(name) -> initialize
write(1) -> activate
write(0) -> deactivate
write(1) -> activate
write(0) -> deactivate
etc.
read() -> check status (0 -> inactive, 1 -> active)
or something like this.

It might be extended for timeouts too. ;-)

> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> ---
>  Documentation/power/suspend-blockers.txt |   17 ++++
>  include/linux/suspend_block_dev.h        |   25 ++++++
>  kernel/power/Kconfig                     |    9 ++
>  kernel/power/Makefile                    |    1 +
>  kernel/power/user_suspend_block.c        |  125 ++++++++++++++++++++++++++++++
>  5 files changed, 177 insertions(+), 0 deletions(-)
>  create mode 100644 include/linux/suspend_block_dev.h
>  create mode 100644 kernel/power/user_suspend_block.c
> 
> diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
> index 743b870..35bc713 100644
> --- a/Documentation/power/suspend-blockers.txt
> +++ b/Documentation/power/suspend-blockers.txt
> @@ -74,3 +74,20 @@ if (list_empty(&state->pending_work))
>  else
>  	suspend_block(&state->suspend_blocker);
>  
> +User-space API
> +==============
> +
> +To create a suspend_blocker from user-space, open the suspend_blocker device:
> +    fd = open("/dev/suspend_blocker", O_RDWR | O_CLOEXEC);
> +then call:
> +    ioctl(fd, SUSPEND_BLOCKER_IOCTL_INIT(strlen(name)), name);
> +
> +To activate a suspend_blocker call:
> +    ioctl(fd, SUSPEND_BLOCKER_IOCTL_BLOCK);
> +
> +To unblock call:
> +    ioctl(fd, SUSPEND_BLOCKER_IOCTL_UNBLOCK);
> +
> +To destroy the suspend_blocker, close the device:
> +    close(fd);
> +
> diff --git a/include/linux/suspend_block_dev.h b/include/linux/suspend_block_dev.h
> new file mode 100644
> index 0000000..a7c0bf9
> --- /dev/null
> +++ b/include/linux/suspend_block_dev.h
> @@ -0,0 +1,25 @@
> +/* include/linux/suspend_block_dev.h
> + *
> + * Copyright (C) 2009 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _LINUX_SUSPEND_BLOCK_DEV_H
> +#define _LINUX_SUSPEND_BLOCK_DEV_H
> +
> +#include <linux/ioctl.h>
> +
> +#define SUSPEND_BLOCKER_IOCTL_INIT(len)		_IOC(_IOC_WRITE, 's', 0, len)
> +#define SUSPEND_BLOCKER_IOCTL_BLOCK		_IO('s', 1)
> +#define SUSPEND_BLOCKER_IOCTL_UNBLOCK		_IO('s', 3)
> +
> +#endif
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 9d1df13..2cf4c4d 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -126,6 +126,15 @@ config SUSPEND_BLOCK
>  	  /sys/power/request_state, the requested sleep state will be entered
>  	  when no suspend_blockers are active.
>  
> +config USER_SUSPEND_BLOCK
> +	bool "Userspace suspend blockers"
> +	depends on SUSPEND_BLOCK
> +	default y
> +	---help---
> +	  User-space suspend block api. Creates a misc device with ioctls
> +	  to create, block and unblock a suspend_blocker. The suspend_blocker
> +	  will be deleted when the device is closed.
> +
>  config HIBERNATION
>  	bool "Hibernation (aka 'suspend to disk')"
>  	depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
> index 29cdc9e..5c524f1 100644
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
>  obj-$(CONFIG_PM_SLEEP)		+= console.o
>  obj-$(CONFIG_FREEZER)		+= process.o
>  obj-$(CONFIG_SUSPEND_BLOCK)	+= suspend_block.o
> +obj-$(CONFIG_USER_SUSPEND_BLOCK)	+= user_suspend_block.o
>  obj-$(CONFIG_HIBERNATION)	+= swsusp.o disk.o snapshot.o swap.o user.o
>  
>  obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
> diff --git a/kernel/power/user_suspend_block.c b/kernel/power/user_suspend_block.c
> new file mode 100644
> index 0000000..782407c
> --- /dev/null
> +++ b/kernel/power/user_suspend_block.c
> @@ -0,0 +1,125 @@
> +/* kernel/power/user_suspend_block.c
> + *
> + * Copyright (C) 2009 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/uaccess.h>
> +#include <linux/suspend_block.h>
> +#include <linux/suspend_block_dev.h>
> +
> +enum {
> +	DEBUG_FAILURE	= BIT(0),
> +};
> +static int debug_mask = DEBUG_FAILURE;
> +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
> +
> +static DEFINE_MUTEX(ioctl_lock);
> +
> +struct user_suspend_blocker {
> +	struct suspend_blocker	blocker;
> +	char			name[0];
> +};
> +
> +static int create_user_suspend_blocker(struct file *file, void __user *name,
> +				 size_t name_len)
> +{
> +	struct user_suspend_blocker *bl;
> +	if (file->private_data)
> +		return -EBUSY;
> +	bl = kzalloc(sizeof(*bl) + name_len + 1, GFP_KERNEL);
> +	if (!bl)
> +		return -ENOMEM;
> +	if (copy_from_user(bl->name, name, name_len))
> +		goto err_fault;
> +	suspend_blocker_init(&bl->blocker, bl->name);
> +	file->private_data = bl;
> +	return 0;
> +
> +err_fault:
> +	kfree(bl);
> +	return -EFAULT;
> +}
> +
> +static long user_suspend_blocker_ioctl(struct file *file, unsigned int cmd,
> +				unsigned long _arg)
> +{
> +	void __user *arg = (void __user *)_arg;
> +	struct user_suspend_blocker *bl;
> +	long ret;
> +
> +	mutex_lock(&ioctl_lock);
> +	if ((cmd & ~IOCSIZE_MASK) == SUSPEND_BLOCKER_IOCTL_INIT(0)) {
> +		ret = create_user_suspend_blocker(file, arg, _IOC_SIZE(cmd));
> +		goto done;
> +	}
> +	bl = file->private_data;
> +	if (!bl) {
> +		ret = -ENOENT;
> +		goto done;
> +	}
> +	switch (cmd) {
> +	case SUSPEND_BLOCKER_IOCTL_BLOCK:
> +		suspend_block(&bl->blocker);
> +		ret = 0;
> +		break;
> +	case SUSPEND_BLOCKER_IOCTL_UNBLOCK:
> +		suspend_unblock(&bl->blocker);
> +		ret = 0;
> +		break;
> +	default:
> +		ret = -ENOTSUPP;
> +	}
> +done:
> +	if (ret && debug_mask & DEBUG_FAILURE)
> +		pr_err("user_suspend_blocker_ioctl: cmd %x failed, %ld\n",
> +			cmd, ret);
> +	mutex_unlock(&ioctl_lock);
> +	return ret;
> +}
> +
> +static int user_suspend_blocker_release(struct inode *inode, struct file *file)
> +{
> +	struct user_suspend_blocker *bl = file->private_data;
> +	if (!bl)
> +		return 0;
> +	suspend_blocker_destroy(&bl->blocker);
> +	kfree(bl);
> +	return 0;
> +}
> +
> +const struct file_operations user_suspend_blocker_fops = {
> +	.release = user_suspend_blocker_release,
> +	.unlocked_ioctl = user_suspend_blocker_ioctl,
> +};
> +
> +struct miscdevice user_suspend_blocker_device = {
> +	.minor = MISC_DYNAMIC_MINOR,
> +	.name = "suspend_blocker",
> +	.fops = &user_suspend_blocker_fops,
> +};
> +
> +static int __init user_suspend_blocker_init(void)
> +{
> +	return misc_register(&user_suspend_blocker_device);
> +}
> +
> +static void __exit user_suspend_blocker_exit(void)
> +{
> +	misc_deregister(&user_suspend_blocker_device);
> +}
> +
> +module_init(user_suspend_blocker_init);
> +module_exit(user_suspend_blocker_exit);


-- 
Everyone knows that debugging is twice as hard as writing a program
in the first place.  So if you're as clever as you can be when you write it,
how will you ever debug it? --- Brian Kernighan
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 3/8] PM: suspend_block: Abort task freezing if a suspend_blocker is active.
  2009-04-15  1:41     ` [PATCH 3/8] PM: suspend_block: Abort task freezing if a suspend_blocker is active Arve Hjønnevåg
  2009-04-15  1:41       ` [PATCH 4/8] Input: Block suspend while event queue is not empty Arve Hjønnevåg
@ 2009-04-29 22:56       ` Rafael J. Wysocki
  1 sibling, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2009-04-29 22:56 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Wednesday 15 April 2009, Arve Hjønnevåg wrote:
> If a suspend_blocker is active, suspend will fail anyway. Since
> try_to_freeze_tasks can take up to 20 seconds to complete or fail, aborting
> as soon as someone blocks suspend (e.g. from an interrupt handler) improves
> the worst case wakeup latency.
> 
> On an older kernel where task freezing could fail for processes attached
> to a debugger, this fixed a problem where the device sometimes hung for
> 20 seconds before the screen turned on.
> 
> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> ---
>  kernel/power/process.c |   23 ++++++++++++++++++-----
>  1 files changed, 18 insertions(+), 5 deletions(-)
> 
> diff --git a/kernel/power/process.c b/kernel/power/process.c
> index ca63401..76d7cdb 100644
> --- a/kernel/power/process.c
> +++ b/kernel/power/process.c
> @@ -13,6 +13,7 @@
>  #include <linux/module.h>
>  #include <linux/syscalls.h>
>  #include <linux/freezer.h>
> +#include <linux/suspend_block.h>
>  
>  /* 
>   * Timeout for stopping processes
> @@ -36,6 +37,7 @@ static int try_to_freeze_tasks(bool sig_only)
>  	struct timeval start, end;
>  	u64 elapsed_csecs64;
>  	unsigned int elapsed_csecs;
> +	unsigned int wakeup = 0;

Make it a bool, please.

>  	do_gettimeofday(&start);
>  
> @@ -62,6 +64,10 @@ static int try_to_freeze_tasks(bool sig_only)
>  		} while_each_thread(g, p);
>  		read_unlock(&tasklist_lock);
>  		yield();			/* Yield is okay here */
> +		if (todo && suspend_is_blocked()) {
> +			wakeup = 1;

'true', please.

> +			break;
> +		}
>  		if (time_after(jiffies, end_time))
>  			break;
>  	} while (todo);
> @@ -77,11 +83,18 @@ static int try_to_freeze_tasks(bool sig_only)
>  		 * and caller must call thaw_processes() if something fails),
>  		 * but it cleans up leftover PF_FREEZE requests.
>  		 */
> -		printk("\n");
> -		printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds "
> -				"(%d tasks refusing to freeze):\n",
> -				elapsed_csecs / 100, elapsed_csecs % 100, todo);
> -		show_state();
> +		if (wakeup) {
> +			printk("\n");

Why is the separate printk() necessary ?

> +			printk(KERN_ERR "Freezing of %s aborted\n",
> +					sig_only ? "user space " : "tasks ");
> +		} else {
> +			printk("\n");

Why is the separate printk() necessary ?

> +			printk(KERN_ERR
> +			       "Freezing of tasks failed after %d.%02d seconds "
> +			       "(%d tasks refusing to freeze):\n",
> +			       elapsed_csecs / 100, elapsed_csecs % 100, todo);
> +			show_state();
> +		}
>  		read_lock(&tasklist_lock);
>  		do_each_thread(g, p) {
>  			task_lock(p);


-- 
Everyone knows that debugging is twice as hard as writing a program
in the first place.  So if you're as clever as you can be when you write it,
how will you ever debug it? --- Brian Kernighan
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-29 22:34   ` Rafael J. Wysocki
@ 2009-04-29 23:45     ` Arve Hjønnevåg
  2009-04-30  0:49     ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-29 23:45 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: ncunningham, u.luckas, swetland, linux-pm

2009/4/29 Rafael J. Wysocki <rjw@sisk.pl>:
> On Wednesday 15 April 2009, Arve Hjønnevåg wrote:
>> diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
>> new file mode 100644
>> index 0000000..743b870
>> --- /dev/null
>> +++ b/Documentation/power/suspend-blockers.txt
>> @@ -0,0 +1,76 @@
>> +Suspend blockers
>> +================
>> +
>> +A suspend_blocker prevents the system from entering suspend.
>
> I'd say "suspend blocker", without the underscore.
>
> Also, I'd try to provide a general explanation of how this is supposed to
> work here, as an introduction.  Something like:
>
> "Suspend blockers provide a mechanism allowing device drivers and user
> space processes to prevent the system from entering a sleep state, such as
> the ACPI S3 state.
>
> To use a suspend blocker, a device driver needs a struct suspend_blocker object
> that has to be initialized with the help of suspend_blocker_init().  When no
> longer needed, the suspend blocker must be garbage collected with the help
> of suspend_blocker_destroy().
>
> A suspend blocker is activated using suspend_block(), which prevents the
> system from entering any sleep state until the suspend blocker is deactivated
> with suspend_unblock().  Many suspend blockers may be used simultaneously
> and the system is prevented from entering sleep states as long as at least one
> of them is active."
>

I added most of this, see new text below.

> BTW, I don't really like the names "suspend_block" and "suspend_unblock".
> See below for possible alternatives.
>
>> +If the suspend operation has already started when calling suspend_block on a
>> +suspend_blocker, it will abort the suspend operation as long it has not already
>> +reached the sysdev suspend stage. This means that calling suspend_block from an
>> +interrupt handler or a freezeable thread always works, but if you call
>> +block_suspend
>
> Surely suspend_block() ?

Yes.

>
>> from a sysdev suspend handler you must also return an error from
>> +that handler to abort suspend.
>
> Well, if a sysdev suspend handler returns an error code, suspend will be
> aborted anyway, so I'm not sure what the point in using suspend blockers from
> sysdev suspend handlers really is.  Perhaps it's better to say "don't use
> suspend blockers from withing sysdev suspend handlers"?

You still need the suspend blocker to prevent the system from trying
to enter suspend again. I changed the text to "to abort a suspend
sequence that was already started".

>
>> +
>
> I'd say "For example" here.
>
>> +Suspend blockers can be used to allow user-space to decide which keys should
>> +wake the full system up and turn the screen on. Use set_irq_wake or a platform
>> +specific api to make sure the keypad interrupt wakes up the cpu. Once the keypad
>> +driver has resumed, the sequence of events can look like this:
>> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
>> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
>> +- The keypad-scan code detects a key change and reports it to the input-event
>> +  driver.
>> +- The input-event driver sees the key change, enqueues an event, and calls
>> +  suspend_block on the input-event-queue suspend_blocker.
>> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
>> +  on the keypad-scan suspend_blocker.
>> +- The user-space input-event thread returns from select/poll, calls
>> +  suspend_block on the process-input-events suspend_blocker and then calls read
>> +  on the input-event device.
>> +- The input-event driver dequeues the key-event and, since the queue is now
>> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
>> +- The user-space input-event thread returns from read. It determines that the
>> +  key should not wake up the full system, calls suspend_unblock on the
>> +  process-input-events suspend_blocker and calls select or poll.
>> +
>> +                 Key pressed   Key released
>> +                     |             |
>> +keypad-scan          ++++++++++++++++++
>> +input-event-queue        +++          +++
>> +process-input-events       +++          +++
>> +
>> +
>> +Driver API
>> +==========
>> +
>> +A driver can use the suspend block api by adding a suspend_blocker variable to
>> +its state and calling suspend_blocker_init. For instance:
>> +struct state {
>> +     struct suspend_blocker suspend_blocker;
>> +}
>> +
>> +init() {
>> +     suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
>> +}
>> +
>> +Before freeing the memory, suspend_blocker_destroy must be called:
>> +
>> +uninit() {
>> +     suspend_blocker_destroy(&state->suspend_blocker);
>> +}
>> +
>> +When the driver determines that it needs to run (usually in an interrupt
>> +handler) it calls suspend_block:
>> +     suspend_block(&state->suspend_blocker);
>
> suspend_blocker_activate() ?
> suspend_blocker_enable() ?
> suspend_blocker_start() ?
>

I don't like any of those better than suspend_block, but if there is
some agreement on what it should be changed to, I'll change it.

>> +
>> +When it no longer needs to run it calls suspend_unblock:
>> +     suspend_unblock(&state->suspend_blocker);
>
> suspend_blocker_deactivate() ?
> suspend_blocker_disable() ?
> suspend_blocker_finish() ?
>
>> +
>> +Calling suspend_block when the suspend blocker is active or suspend_unblock when
>> +it is not active has no effect. This allows drivers to update their state and
>> +call suspend suspend_block or suspend_unblock based on the result.
>> +For instance:
>> +
>> +if (list_empty(&state->pending_work))
>> +     suspend_unblock(&state->suspend_blocker);
>> +else
>> +     suspend_block(&state->suspend_blocker);
>> +


-- 
Arve Hjønnevåg


----

Suspend blockers
================

Suspend blockers provide a mechanism for device drivers and user-space processes
to prevent the system from entering suspend. Only suspend states requested
through /sys/power/request_state are prevented. Writing to /sys/power/state
will ignore suspend blockers, and sleep states entered from idle are unaffected.

To use a suspend blocker, a device driver needs a struct suspend_blocker object
that has to be initialized with suspend_blocker_init. When no longer needed,
the suspend blocker must be destroyed with suspend_blocker_destroy.

A suspend blocker is activated using suspend_block, which prevents the system
from entering suspend until the suspend blocker is deactivated with
suspend_unblock. Many suspend blockers may be used simultaneously, and the
system is prevented from entering suspend as long as at least one of them is
active.

If the suspend operation has already started when calling suspend_block on a
suspend_blocker, it will abort the suspend operation as long it has not already
reached the sysdev suspend stage. This means that calling suspend_block from an
interrupt handler or a freezeable thread always works, but if you call
suspend_block from a sysdev suspend handler you must also return an error from
that handler to abort a suspend sequence that was already started.

In cell phones or other embedded systems where powering the screen is a
significant drain on the battery, suspend blockers can be used to allow
user-space to decide whether a keystroke received while the system is suspended
should cause the screen to be turned back on or allow the system to go back into
suspend. Use set_irq_wake or a platform specific api to make sure the keypad
interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
events can look like this:

- The Keypad driver gets an interrupt. It then calls suspend_block on the
  keypad-scan suspend_blocker and starts scanning the keypad matrix.
- The keypad-scan code detects a key change and reports it to the input-event
  driver.
- The input-event driver sees the key change, enqueues an event, and calls
  suspend_block on the input-event-queue suspend_blocker.
- The keypad-scan code detects that no keys are held and calls suspend_unblock
  on the keypad-scan suspend_blocker.
- The user-space input-event thread returns from select/poll, calls
  suspend_block on the process-input-events suspend_blocker and then calls read
  on the input-event device.
- The input-event driver dequeues the key-event and, since the queue is now
  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
- The user-space input-event thread returns from read. If it determines that
  the key should leave the screen off, it calls suspend_unblock on the
  process_input_events suspend_blocker and then calls select or poll. The
  system will automatically suspend again, since now no suspend blockers are
  active.

                 Key pressed   Key released
                     |             |
keypad-scan          ++++++++++++++++++
input-event-queue        +++          +++
process-input-events       +++          +++


Driver API
==========

A driver can use the suspend block api by adding a suspend_blocker variable to
its state and calling suspend_blocker_init. For instance:
struct state {
        struct suspend_blocker suspend_blocker;
}

init() {
        suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
}

Before freeing the memory, suspend_blocker_destroy must be called:

uninit() {
        suspend_blocker_destroy(&state->suspend_blocker);
}

When the driver determines that it needs to run (usually in an interrupt
handler) it calls suspend_block:
        suspend_block(&state->suspend_blocker);

When it no longer needs to run it calls suspend_unblock:
        suspend_unblock(&state->suspend_blocker);

Calling suspend_block when the suspend blocker is active or suspend_unblock when
it is not active has no effect. This allows drivers to update their state and
call suspend suspend_block or suspend_unblock based on the result.
For instance:

if (list_empty(&state->pending_work))
        suspend_unblock(&state->suspend_blocker);
else
        suspend_block(&state->suspend_blocker);

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-29 22:34   ` Rafael J. Wysocki
  2009-04-29 23:45     ` Arve Hjønnevåg
@ 2009-04-30  0:49     ` Arve Hjønnevåg
  2009-04-26  9:42       ` Pavel Machek
  2009-05-02 12:14       ` Rafael J. Wysocki
  1 sibling, 2 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-04-30  0:49 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: ncunningham, u.luckas, swetland, linux-pm

2009/4/29 Rafael J. Wysocki <rjw@sisk.pl>:
> On Wednesday 15 April 2009, Arve Hjønnevåg wrote:
>> diff --git a/include/linux/suspend_block.h b/include/linux/suspend_block.h
>> new file mode 100755
>> index 0000000..7820c60
>> --- /dev/null
>> +++ b/include/linux/suspend_block.h
>
> suspend_blockers.h perhaps?

suspend_blocker.h?

>
>> @@ -0,0 +1,61 @@
>> +/* include/linux/suspend_block.h
>> + *
>> + * Copyright (C) 2007-2009 Google, Inc.
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#ifndef _LINUX_SUSPEND_BLOCK_H
>> +#define _LINUX_SUSPEND_BLOCK_H
>
> SUSPEND_BLOCKERS_H ?
>
>> +
>> +/* A suspend_blocker prevents the system from entering suspend when active.
>> + */
>> +
>> +struct suspend_blocker {
>> +#ifdef CONFIG_SUSPEND_BLOCK
>
> CONFIG_SUSPEND_BLOCKERS ?

OK.

>
>> +     atomic_t            flags;
>> +     const char         *name;
>> +#endif
>> +};
>> +
>> +#ifdef CONFIG_SUSPEND_BLOCK
>> +
>> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
>> +void suspend_blocker_destroy(struct suspend_blocker *blocker);
>> +void suspend_block(struct suspend_blocker *blocker);
>> +void suspend_unblock(struct suspend_blocker *blocker);
>> +
>> +/* is_blocking_suspend returns true if the suspend_blocker is currently active.
>> + */
>> +bool is_blocking_suspend(struct suspend_blocker *blocker);
>
> suspend_blocker_is_active() ?
OK.
> suspend_blocker_enabled() ?
>
>> +
>> +/* suspend_is_blocked can be used by generic power management code to abort
>> + * suspend.
>> + *
>> + * To preserve backward compatibility suspend_is_blocked returns 0 unless it
>> + * is called during suspend initiated from the suspend_block code.
>> + */
>> +bool suspend_is_blocked(void);
>> +
>> +#else
>> +
>> +static inline void suspend_blocker_init(struct suspend_blocker *blocker,
>> +                                     const char *name) {}
>> +static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
>> +static inline void suspend_block(struct suspend_blocker *blocker) {}
>> +static inline void suspend_unblock(struct suspend_blocker *blocker) {}
>> +static inline bool is_blocking_suspend(struct suspend_blocker *bl) { return 0; }
>> +static inline bool suspend_is_blocked(void) { return 0; }
>> +
>> +#endif
>> +
>> +#endif
>> +
>> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
>> index 23bd4da..9d1df13 100644
>> --- a/kernel/power/Kconfig
>> +++ b/kernel/power/Kconfig
>> @@ -116,6 +116,16 @@ config SUSPEND_FREEZER
>>
>>         Turning OFF this setting is NOT recommended! If in doubt, say Y.
>>
>> +config SUSPEND_BLOCK
>> +     bool "Suspend block"
>> +     depends on PM
>> +     select RTC_LIB
>
> depends on RTC_LIB
>
> select doesn't really work and I don't think it ever will.

Nothing depends on RTC_LIB, it is only selected.

>
>> +     default n
>> +     ---help---
>> +       Enable suspend_block.
>
> Enable suspend blockers.

OK.

>
>> When user space requests a sleep state through
>> +       /sys/power/request_state, the requested sleep state will be entered
>> +       when no suspend_blockers are active.
>
> Well, if you don't want suspend blockers to block suspend started via
> /sys/power/state, it's worth making it crystal clear in the documentation too.

I added this to the intro.

>
>> +
>>  config HIBERNATION
>>       bool "Hibernation (aka 'suspend to disk')"
>>       depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
>> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
>> index 720ea4f..29cdc9e 100644
>> --- a/kernel/power/Makefile
>> +++ b/kernel/power/Makefile
>> @@ -6,6 +6,7 @@ endif
>>  obj-$(CONFIG_PM)             += main.o
>>  obj-$(CONFIG_PM_SLEEP)               += console.o
>>  obj-$(CONFIG_FREEZER)                += process.o
>> +obj-$(CONFIG_SUSPEND_BLOCK)  += suspend_block.o
>>  obj-$(CONFIG_HIBERNATION)    += swsusp.o disk.o snapshot.o swap.o user.o
>>
>>  obj-$(CONFIG_MAGIC_SYSRQ)    += poweroff.o
>> diff --git a/kernel/power/main.c b/kernel/power/main.c
>> index c9632f8..e4c6b20 100644
>> --- a/kernel/power/main.c
>> +++ b/kernel/power/main.c
>> @@ -22,6 +22,7 @@
>>  #include <linux/freezer.h>
>>  #include <linux/vmstat.h>
>>  #include <linux/syscalls.h>
>> +#include <linux/suspend_block.h>
>>
>>  #include "power.h"
>>
>> @@ -392,6 +393,9 @@ static void suspend_finish(void)
>>
>>
>>  static const char * const pm_states[PM_SUSPEND_MAX] = {
>> +#ifdef CONFIG_SUSPEND_BLOCK
>> +     [PM_SUSPEND_ON]         = "on",
>> +#endif
>
> Why #ifdef?

Removed.

>
>>       [PM_SUSPEND_STANDBY]    = "standby",
>>       [PM_SUSPEND_MEM]        = "mem",
>>  };
>> @@ -540,6 +544,61 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>>
>>  power_attr(state);
>>
>> +/**
>> + *   request_state - control system power state.
>> + *
>> + *   This is similar to state, but it does not block until the system
>> + *   resumes, and it will try to re-enter the state until another state is
>> + *   requested. Suspend blockers are respected and the requested state will
>> + *   only be entered when no suspend blockers are active.
>> + *   Write "on" to cancel.
>> + */
>> +
>> +#ifdef CONFIG_SUSPEND_BLOCK
>> +static ssize_t request_state_show(struct kobject *kobj,
>> +                               struct kobj_attribute *attr, char *buf)
>> +{
>> +     char *s = buf;
>> +     int i;
>> +
>> +     for (i = 0; i < PM_SUSPEND_MAX; i++) {
>> +             if (pm_states[i] && (i == PM_SUSPEND_ON || valid_state(i)))
>> +                     s += sprintf(s, "%s ", pm_states[i]);
>> +     }
>> +     if (s != buf)
>> +             /* convert the last space to a newline */
>> +             *(s-1) = '\n';
>> +     return (s - buf);
>> +}
>> +
>> +static ssize_t request_state_store(struct kobject *kobj,
>> +                                struct kobj_attribute *attr,
>> +                                const char *buf, size_t n)
>> +{
>> +     suspend_state_t state = PM_SUSPEND_ON;
>> +     const char * const *s;
>> +     char *p;
>> +     int len;
>> +     int error = -EINVAL;
>> +
>> +     p = memchr(buf, '\n', n);
>> +     len = p ? p - buf : n;
>> +
>> +     for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
>> +             if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
>> +                     break;
>> +     }
>> +     if (state < PM_SUSPEND_MAX && *s)
>> +             if (state == PM_SUSPEND_ON || valid_state(state)) {
>> +                     error = 0;
>> +                     request_suspend_state(state);
>> +             }
>> +     return error ? error : n;
>> +}
>> +
>> +power_attr(request_state);
>> +#endif /* CONFIG_SUSPEND_BLOCK */
>> +
>>  #ifdef CONFIG_PM_TRACE
>>  int pm_trace_enabled;
>>
>> @@ -567,6 +626,9 @@ power_attr(pm_trace);
>>
>>  static struct attribute * g[] = {
>>       &state_attr.attr,
>> +#ifdef CONFIG_SUSPEND_BLOCK
>> +     &request_state_attr.attr,
>> +#endif
>>  #ifdef CONFIG_PM_TRACE
>>       &pm_trace_attr.attr,
>>  #endif
>> diff --git a/kernel/power/power.h b/kernel/power/power.h
>> index 46b5ec7..2414a74 100644
>> --- a/kernel/power/power.h
>> +++ b/kernel/power/power.h
>> @@ -223,3 +223,9 @@ static inline void suspend_thaw_processes(void)
>>  {
>>  }
>>  #endif
>> +
>> +#ifdef CONFIG_SUSPEND_BLOCK
>> +/* kernel/power/suspend_block.c */
>> +void request_suspend_state(suspend_state_t state);
>> +#endif
>
> Is the #ifdef really necessary?
>

No, but it gives a compile error instead of a link error if you call
request_suspend_state when it is not available so it is useful.

>> +
>> diff --git a/kernel/power/suspend_block.c b/kernel/power/suspend_block.c
>> new file mode 100644
>> index 0000000..b4f2191
>> --- /dev/null
>> +++ b/kernel/power/suspend_block.c
>> @@ -0,0 +1,257 @@
>> +/* kernel/power/suspend_block.c
>> + *
>> + * Copyright (C) 2005-2008 Google, Inc.
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/rtc.h>
>> +#include <linux/suspend.h>
>> +#include <linux/suspend_block.h>
>> +#include <linux/sysdev.h>
>> +#include "power.h"
>> +
>> +enum {
>> +     DEBUG_EXIT_SUSPEND = 1U << 0,
>> +     DEBUG_WAKEUP = 1U << 1,
>> +     DEBUG_USER_STATE = 1U << 2,
>> +     DEBUG_SUSPEND = 1U << 3,
>> +     DEBUG_SUSPEND_BLOCKER = 1U << 4,
>> +};
>> +static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
>> +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
>> +
>> +#define SB_INITIALIZED            (1U << 8)
>> +#define SB_ACTIVE                 (1U << 9)
>> +
>> +static DEFINE_SPINLOCK(state_lock);
>> +static atomic_t suspend_block_count;
>> +static atomic_t current_event_num;
>> +struct workqueue_struct *suspend_work_queue;
>> +struct suspend_blocker main_suspend_blocker;
>> +static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
>> +static bool enable_suspend_blockers;
>> +
>> +bool suspend_is_blocked(void)
>> +{
>> +     if (WARN_ONCE(!enable_suspend_blockers, "ignoring suspend blockers\n"))
>> +             return 0;
>> +     return !!atomic_read(&suspend_block_count);
>> +}
>> +
>> +static void suspend_worker(struct work_struct *work)
>> +{
>> +     int ret;
>> +     int entry_event_num;
>> +
>> +     enable_suspend_blockers = 1;
>
> 'true' instead of '1', please.

OK.

>
>> +
>> +retry:
>> +     if (suspend_is_blocked()) {
>> +             if (debug_mask & DEBUG_SUSPEND)
>> +                     pr_info("suspend: abort suspend\n");
>> +             goto abort;
>
> If that were in a loop you could just use 'break' here.

do you think this is better:
while(1) {
        if (exception1) {
                ...
                break;
        }
        ...
        if (exception2) {
                ...
                continue;
        }
        break;
}
>
>> +     }
>> +
>> +     entry_event_num = atomic_read(&current_event_num);
>> +     if (debug_mask & DEBUG_SUSPEND)
>> +             pr_info("suspend: enter suspend\n");
>> +     ret = pm_suspend(requested_suspend_state);
>> +     if (debug_mask & DEBUG_EXIT_SUSPEND) {
>> +             struct timespec ts;
>> +             struct rtc_time tm;
>> +             getnstimeofday(&ts);
>> +             rtc_time_to_tm(ts.tv_sec, &tm);
>> +             pr_info("suspend: exit suspend, ret = %d "
>> +                     "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
>> +                     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
>> +                     tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
>> +     }
>> +     if (atomic_read(&current_event_num) == entry_event_num) {
>> +             if (debug_mask & DEBUG_SUSPEND)
>> +                     pr_info("suspend: pm_suspend returned with no event\n");
>> +             goto retry;
>
> Put that into a loop and use 'continue' here?  Or use 'break' to go out of
> the loop?

The retry is a workaround for the lack of suspend_block_timeout.

>
>> +     }
>> +abort:
>> +     enable_suspend_blockers = 0;
>
> 'false' instead of '0', please.

OK.

>
>> +}
>> +static DECLARE_WORK(suspend_work, suspend_worker);
>> +
>> +static int suspend_block_suspend(struct sys_device *dev, pm_message_t state)
>> +{
>> +     int ret = suspend_is_blocked() ? -EAGAIN : 0;
>> +     if (debug_mask & DEBUG_SUSPEND)
>> +             pr_info("suspend_block_suspend return %d\n", ret);
>> +     return ret;
>> +}
>> +
>> +static struct sysdev_class suspend_block_sysclass = {
>> +     .name = "suspend_block",
>> +     .suspend = suspend_block_suspend,
>> +};
>> +static struct sys_device suspend_block_sysdev = {
>> +     .cls = &suspend_block_sysclass,
>> +};
>> +
>
> Hmm.  Perhaps add the suspend_is_blocked() check at the beginning of
> sysdev_suspend() instead of this?  Surely you don't want to suspend
> any sysdevs with any suspend blockers active, right?

You could make the same argument for any device. Using a sysdev makes
the patch easier to apply.

>
>> +/**
>> + * suspend_blocker_init() - Initialize a suspend blocker
>> + * @blocker: The suspend blocker to initialize.
>> + * @name:    The name of the suspend blocker to show in
>> + *           /proc/suspend_blockers
>> + */
>> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
>> +{
>> +     if (name)
>> +             blocker->name = name;
>> +     BUG_ON(!blocker->name);
>
> Do you really want to crash the kernel in this case?  WARN_ON() might be better IMO.

OK.

>
> Also, this assumes that the caller won't release the memory used to store the
> 'name' string.  it might be a good idea to point that out in the kerneldoc
> comment.
>
>> +
>> +     if (debug_mask & DEBUG_SUSPEND_BLOCKER)
>> +             pr_info("suspend_blocker_init name=%s\n", blocker->name);
>> +
>> +     atomic_set(&blocker->flags, SB_INITIALIZED);
>> +}
>> +EXPORT_SYMBOL(suspend_blocker_init);
>> +
>> +/**
>> + * suspend_blocker_destroy() - Destroy a suspend blocker
>> + * @blocker: The suspend blocker to destroy.
>> + */
>> +void suspend_blocker_destroy(struct suspend_blocker *blocker)
>> +{
>> +     int flags;
>> +     if (debug_mask & DEBUG_SUSPEND_BLOCKER)
>> +             pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
>> +     flags = atomic_xchg(&blocker->flags, 0);
>> +     WARN(!(flags & SB_INITIALIZED), "suspend_blocker_destroy called on "
>> +                                     "uninitialized suspend_blocker\n");
>> +     if (flags == (SB_INITIALIZED | SB_ACTIVE))
>> +             if (atomic_dec_and_test(&suspend_block_count))
>> +                     queue_work(suspend_work_queue, &suspend_work);
>> +}
>> +EXPORT_SYMBOL(suspend_blocker_destroy);
>> +
>> +/**
>> + * suspend_block() - Block suspend
>> + * @blocker: The suspend blocker to use
>> + */
>> +void suspend_block(struct suspend_blocker *blocker)
>> +{
>> +     BUG_ON(!(atomic_read(&blocker->flags) & SB_INITIALIZED));
>
> WARN_ON() and exit instead?  BUG_ON() is a bit drastic IMHO.
>
>> +
>> +     if (debug_mask & DEBUG_SUSPEND_BLOCKER)
>> +             pr_info("suspend_block: %s\n", blocker->name);
>> +     if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED,
>> +         SB_INITIALIZED | SB_ACTIVE) == SB_INITIALIZED)
>> +             atomic_inc(&suspend_block_count);
>> +
>> +     atomic_inc(&current_event_num);
>> +}
>> +EXPORT_SYMBOL(suspend_block);
>> +
>> +/**
>> + * suspend_unblock() - Unblock suspend
>> + * @blocker: The suspend blocker to unblock.
>> + *
>> + * If no other suspend blockers block suspend, the system will suspend.
>> + */
>> +void suspend_unblock(struct suspend_blocker *blocker)
>> +{
>> +     if (debug_mask & DEBUG_SUSPEND_BLOCKER)
>> +             pr_info("suspend_unblock: %s\n", blocker->name);
>> +
>> +     if (atomic_cmpxchg(&blocker->flags, SB_INITIALIZED | SB_ACTIVE,
>> +         SB_INITIALIZED) == (SB_INITIALIZED | SB_ACTIVE))
>> +             if (atomic_dec_and_test(&suspend_block_count))
>> +                     queue_work(suspend_work_queue, &suspend_work);
>> +}
>> +EXPORT_SYMBOL(suspend_unblock);
>> +
>> +/**
>> + * is_blocking_suspend() - Test if a suspend blocker is blocking suspend
>> + * @blocker: The suspend blocker to check.
>> + */
>> +bool is_blocking_suspend(struct suspend_blocker *blocker)
>> +{
>> +     return !!(atomic_read(&blocker->flags) & SB_ACTIVE);
>
> Check SB_INITIALIZED too, perhaps?  WARN_ON(!SB_INITIALIZED)?

Added WARN_ON.

>
>> +}
>> +EXPORT_SYMBOL(is_blocking_suspend);
>> +
>> +void request_suspend_state(suspend_state_t state)
>> +{
>> +     unsigned long irqflags;
>> +     spin_lock_irqsave(&state_lock, irqflags);
>> +     if (debug_mask & DEBUG_USER_STATE) {
>> +             struct timespec ts;
>> +             struct rtc_time tm;
>> +             getnstimeofday(&ts);
>> +             rtc_time_to_tm(ts.tv_sec, &tm);
>> +             pr_info("request_suspend_state: %s (%d->%d) at %lld "
>> +                     "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
>> +                     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
>> +                     requested_suspend_state, state,
>> +                     ktime_to_ns(ktime_get()),
>> +                     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
>> +                     tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
>> +     }
>> +     requested_suspend_state = state;
>> +     if (state == PM_SUSPEND_ON)
>> +             suspend_block(&main_suspend_blocker);
>> +     else
>> +             suspend_unblock(&main_suspend_blocker);
>> +     spin_unlock_irqrestore(&state_lock, irqflags);
>> +}
>> +
>> +static int __init suspend_block_init(void)
>> +{
>> +     int ret;
>> +
>> +     suspend_blocker_init(&main_suspend_blocker, "main");
>> +     suspend_block(&main_suspend_blocker);
>> +
>> +     ret = sysdev_class_register(&suspend_block_sysclass);
>> +     if (ret) {
>> +             pr_err("suspend_block_init: sysdev_class_register failed\n");
>> +             goto err_sysdev_class_register;
>> +     }
>> +     ret = sysdev_register(&suspend_block_sysdev);
>> +     if (ret) {
>> +             pr_err("suspend_block_init: suspend_block_sysdev failed\n");
>> +             goto err_sysdev_register;
>> +     }
>> +
>> +     suspend_work_queue = create_singlethread_workqueue("suspend");
>
> Do we need a separate workqueue for this purpose?
>

Yes. Some drivers wait on work that runs on the main workqueue in
their suspend handler.

>> +     if (suspend_work_queue == NULL) {
>> +             ret = -ENOMEM;
>> +             goto err_suspend_work_queue;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_suspend_work_queue:
>> +     sysdev_unregister(&suspend_block_sysdev);
>> +err_sysdev_register:
>> +     sysdev_class_unregister(&suspend_block_sysclass);
>> +err_sysdev_class_register:
>> +     suspend_blocker_destroy(&main_suspend_blocker);
>> +     return ret;
>> +}
>> +
>> +static void  __exit suspend_block_exit(void)
>> +{
>> +     destroy_workqueue(suspend_work_queue);
>> +     sysdev_unregister(&suspend_block_sysdev);
>> +     sysdev_class_unregister(&suspend_block_sysclass);
>> +     suspend_blocker_destroy(&main_suspend_blocker);
>> +}
>> +
>> +core_initcall(suspend_block_init);
>> +module_exit(suspend_block_exit);
>
> Thanks,
> Rafael
>



-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-30  0:49     ` Arve Hjønnevåg
  2009-04-26  9:42       ` Pavel Machek
@ 2009-05-02 12:14       ` Rafael J. Wysocki
  2009-05-02 20:51         ` Pavel Machek
  2009-05-05  3:48         ` Arve Hjønnevåg
  1 sibling, 2 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2009-05-02 12:14 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Thursday 30 April 2009, Arve Hjønnevåg wrote:
> 2009/4/29 Rafael J. Wysocki <rjw@sisk.pl>:
> > On Wednesday 15 April 2009, Arve Hjønnevåg wrote:
> >> diff --git a/include/linux/suspend_block.h b/include/linux/suspend_block.h
> >> new file mode 100755
> >> index 0000000..7820c60
> >> --- /dev/null
> >> +++ b/include/linux/suspend_block.h
> >
> > suspend_blockers.h perhaps?
> 
> suspend_blocker.h?

Fine by me.

[--snip--]
> >> +config SUSPEND_BLOCK
> >> +     bool "Suspend block"
> >> +     depends on PM
> >> +     select RTC_LIB
> >
> > depends on RTC_LIB
> >
> > select doesn't really work and I don't think it ever will.
> 
> Nothing depends on RTC_LIB, it is only selected.

If nothing in your code needs anything depending on RTC_LIB, why select it?

[--snip--]
> >
> >> +
> >> +retry:
> >> +     if (suspend_is_blocked()) {
> >> +             if (debug_mask & DEBUG_SUSPEND)
> >> +                     pr_info("suspend: abort suspend\n");
> >> +             goto abort;
> >
> > If that were in a loop you could just use 'break' here.
> 
> do you think this is better:
> while(1) {
>         if (exception1) {
>                 ...
>                 break;
>         }
>         ...
>         if (exception2) {
>                 ...
>                 continue;
>         }
>         break;
> }

I rather thought of

for (;;) {
         if (exception1) {
                 ...
                 break;
         }
         ...
         if (!exception2)
                 break;
         ...
}

[--snip--]
> >> +static int suspend_block_suspend(struct sys_device *dev, pm_message_t state)
> >> +{
> >> +     int ret = suspend_is_blocked() ? -EAGAIN : 0;
> >> +     if (debug_mask & DEBUG_SUSPEND)
> >> +             pr_info("suspend_block_suspend return %d\n", ret);
> >> +     return ret;
> >> +}
> >> +
> >> +static struct sysdev_class suspend_block_sysclass = {
> >> +     .name = "suspend_block",
> >> +     .suspend = suspend_block_suspend,
> >> +};
> >> +static struct sys_device suspend_block_sysdev = {
> >> +     .cls = &suspend_block_sysclass,
> >> +};
> >> +
> >
> > Hmm.  Perhaps add the suspend_is_blocked() check at the beginning of
> > sysdev_suspend() instead of this?  Surely you don't want to suspend
> > any sysdevs with any suspend blockers active, right?
> 
> You could make the same argument for any device. Using a sysdev makes
> the patch easier to apply.

You've lost me here. :-)

AFAICT, the only purpose of the sysdev class above is to abort suspend if
suspend_is_blocked() returns 'true'.  If that is correct, then IMO you could
achieve the same goal by calling suspend_is_blocked() directly from
sysdev_suspend(), right before check_wakeup_irqs().  Or am I missing anything?

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-04-26  9:42       ` Pavel Machek
@ 2009-05-02 12:17         ` Rafael J. Wysocki
  0 siblings, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2009-05-02 12:17 UTC (permalink / raw)
  To: Pavel Machek, Arve Hj?nnev?g; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Sunday 26 April 2009, Pavel Machek wrote:
> Hi!
> 
> > >> --- a/kernel/power/power.h
> > >> +++ b/kernel/power/power.h
> > >> @@ -223,3 +223,9 @@ static inline void suspend_thaw_processes(void)
> > >>  {
> > >>  }
> > >>  #endif
> > >> +
> > >> +#ifdef CONFIG_SUSPEND_BLOCK
> > >> +/* kernel/power/suspend_block.c */
> > >> +void request_suspend_state(suspend_state_t state);
> > >> +#endif
> > >
> > > Is the #ifdef really necessary?
> > >
> > 
> > No, but it gives a compile error instead of a link error if you call
> > request_suspend_state when it is not available so it is useful.
> 
> I believe #ifdef is too ugly and link error is really good enough.

Well, I agree with this FWIW.

We don't ususally use #ifdefs around function headers, unless they are defined
conditionally.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-05-02 12:14       ` Rafael J. Wysocki
@ 2009-05-02 20:51         ` Pavel Machek
  2009-05-05  3:48         ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2009-05-02 20:51 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: ncunningham, u.luckas, swetland, linux-pm

Hi!

> > >> +config SUSPEND_BLOCK
> > >> +     bool "Suspend block"
> > >> +     depends on PM
> > >> +     select RTC_LIB
> > >
> > > depends on RTC_LIB
> > >
> > > select doesn't really work and I don't think it ever will.
> > 
> > Nothing depends on RTC_LIB, it is only selected.
> 
> If nothing in your code needs anything depending on RTC_LIB, why select it?

He wants to say nothing in _kernel proper_ depends on RTC_LIB:

pavel@amd:/data/l/linux-good$ grep 'select RTC_LIB' `find . -name
"Kconfig"`
./arch/arm/Kconfig:	select RTC_LIB
./arch/mips/Kconfig:	select RTC_LIB
./drivers/rtc/Kconfig:	select RTC_LIB
pavel@amd:/data/l/linux-good$ grep 'depends.*RTC_LIB' `find . -name
"Kconfig"`
pavel@amd:/data/l/linux-good$ 

RTC_LIB seems to be designed for select.
									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2009-05-02 12:14       ` Rafael J. Wysocki
  2009-05-02 20:51         ` Pavel Machek
@ 2009-05-05  3:48         ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2009-05-05  3:48 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: ncunningham, u.luckas, swetland, linux-pm

2009/5/2 Rafael J. Wysocki <rjw@sisk.pl>:
> [--snip--]
>> >
>> >> +
>> >> +retry:
>> >> +     if (suspend_is_blocked()) {
>> >> +             if (debug_mask & DEBUG_SUSPEND)
>> >> +                     pr_info("suspend: abort suspend\n");
>> >> +             goto abort;
>> >
>> > If that were in a loop you could just use 'break' here.
>>
>> do you think this is better:
>> while(1) {
>>         if (exception1) {
>>                 ...
>>                 break;
>>         }
>>         ...
>>         if (exception2) {
>>                 ...
>>                 continue;
>>         }
>>         break;
>> }
>
> I rather thought of
>
> for (;;) {
>         if (exception1) {
>                 ...
>                 break;
>         }
>         ...
>         if (!exception2)
>                 break;
>         ...
> }

I don't think that is much better, but I changed the first patch to
use a loop anyway. The loop is then removed (as before) in the patch
that adds the stats.

>
> [--snip--]
>> >> +static int suspend_block_suspend(struct sys_device *dev, pm_message_t state)
>> >> +{
>> >> +     int ret = suspend_is_blocked() ? -EAGAIN : 0;
>> >> +     if (debug_mask & DEBUG_SUSPEND)
>> >> +             pr_info("suspend_block_suspend return %d\n", ret);
>> >> +     return ret;
>> >> +}
>> >> +
>> >> +static struct sysdev_class suspend_block_sysclass = {
>> >> +     .name = "suspend_block",
>> >> +     .suspend = suspend_block_suspend,
>> >> +};
>> >> +static struct sys_device suspend_block_sysdev = {
>> >> +     .cls = &suspend_block_sysclass,
>> >> +};
>> >> +
>> >
>> > Hmm.  Perhaps add the suspend_is_blocked() check at the beginning of
>> > sysdev_suspend() instead of this?  Surely you don't want to suspend
>> > any sysdevs with any suspend blockers active, right?
>>
>> You could make the same argument for any device. Using a sysdev makes
>> the patch easier to apply.
>
> You've lost me here. :-)

You have been making other changes to those files which made it a moving target.

> AFAICT, the only purpose of the sysdev class above is to abort suspend if
> suspend_is_blocked() returns 'true'.  If that is correct, then IMO you could
> achieve the same goal by calling suspend_is_blocked() directly from
> sysdev_suspend(), right before check_wakeup_irqs().  Or am I missing anything?

The stats patch also used it to track wakeup events, but I have added
another call for this to get rid of the sysdev.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-28 13:29                   ` [linux-pm] " Pavel Machek
@ 2010-05-28 13:42                     ` Brian Swetland
  0 siblings, 0 replies; 195+ messages in thread
From: Brian Swetland @ 2010-05-28 13:42 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Len Brown, markgross, linux-doc, Kernel development list,
	Jesse Barnes, Oleg Nesterov, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton, Matthew Garrett

On Fri, May 28, 2010 at 6:29 AM, Pavel Machek <pavel@ucw.cz> wrote:
> Hi!
>
>> > > Why would you need to constantly try to suspend in that case?
>> >
>> > Because otherwise you're awake for longer than you need to be.
>>
>> If your system is idle and your hardware supports off-while-idle,
>> then that really does not matter. There's not much of a difference
>> in power savings, we're already talking over 10 days on batteries
>> with just off-while-idle on omaps.
>
> Makes me wish g1 was omap based... it looks like you have superior hw.

G1 will happily do 10 days idle (radio on) under typical network
conditions (roughly 4-5mA draw at the battery average in paging mode)
if you have data disabled and there's no reason for it to wake up,
process events, chat on the data network etc.  It'll go 25-30 days in
"airplane mode" (radio off) provided there are not excessive wakeups.

If you happen to be running a perfect userspace where every thread in
every process is blocked on something, it'll hit the exact same power
state out of idle.  If you have a less optimal userspace or random
third party nonoptimal apps, this becomes much harder, of course.
Which is why we do the wakelock thing.

OMAP does have a lot of nice auto-clock-down features compared to some
other SoCs, sometimes simplifying other parts of power management.

Brian

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:38                 ` [linux-pm] " Tony Lindgren
                                     ` (2 preceding siblings ...)
  2010-05-28 13:29                   ` [linux-pm] " Pavel Machek
@ 2010-05-28 13:29                   ` Pavel Machek
  3 siblings, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-28 13:29 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

Hi!

> > > Why would you need to constantly try to suspend in that case?
> > 
> > Because otherwise you're awake for longer than you need to be.
> 
> If your system is idle and your hardware supports off-while-idle,
> then that really does not matter. There's not much of a difference
> in power savings, we're already talking over 10 days on batteries
> with just off-while-idle on omaps.

Makes me wish g1 was omap based... it looks like you have superior hw.

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-28  6:43                 ` [linux-pm] " Pavel Machek
@ 2010-05-28  7:01                   ` Arve Hjønnevåg
  0 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-28  7:01 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Thu, May 27, 2010 at 11:43 PM, Pavel Machek <pavel@ucw.cz> wrote:
> Hi!
>
>> >> >> How often would we retry suspending?
>> >> >
>> >> > Well based on some timer, the same way the screen blanks? Or five
>> >> > seconds of no audio play? So if the suspend fails, then reset whatever
>> >> > userspace suspend policy timers.
>> >> >
>> >> >> If we fail to suspend, don't we have to resume all the drivers that
>> >> >> suspended before the one that failed?  (Maybe I'm mistaken here)
>> >> >
>> >> > Sure, but I guess that should be a rare event that only happens when
>> >> > you try to suspend and something interrupts the suspend.
>> >> >
>> >>
>> >> This is not a rare event. For example, the matrix keypad driver blocks
>> >> suspend when a key is down so it can scan the matrix.
>> >
>> > Sure, but how many times per day are you suspending?
>>
>> How many times we successfully suspend is irrelevant here. If the
>> driver blocks suspend the number of suspend attempts depend on your
>> poll frequency.
>
> Actually, this is quite interesting question I'd like answer here:
>
> "Sure, but how many times per day are you suspending?"
>
> I suspect it may be in 1000s, but it would be cool to get better
> answer -- so that people knew what we are talking about here.

This is highly variable. 1000s would mean that you wake about once
every minute which is not uncommon, but more often than what typically
happens on an idle device. The nexus one has to wake up every 10
minutes to check the battery status check means your at least in the
100s, but the G1 could stay suspended for much longer than that.

The original question was about a driver blocking suspend though, and
if we were to just retry suspend until it succeeds in that case you
could easily get to 100000s suspend attempts in a day.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07  0:10               ` Arve Hjønnevåg
  2010-05-07 15:54                 ` Tony Lindgren
@ 2010-05-28  6:43                 ` Pavel Machek
  2010-05-28  6:43                 ` [linux-pm] " Pavel Machek
  2 siblings, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-28  6:43 UTC (permalink / raw)
  To: Arve Hj?nnev?g
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

Hi!

> >> >> How often would we retry suspending?
> >> >
> >> > Well based on some timer, the same way the screen blanks? Or five
> >> > seconds of no audio play? So if the suspend fails, then reset whatever
> >> > userspace suspend policy timers.
> >> >
> >> >> If we fail to suspend, don't we have to resume all the drivers that
> >> >> suspended before the one that failed?  (Maybe I'm mistaken here)
> >> >
> >> > Sure, but I guess that should be a rare event that only happens when
> >> > you try to suspend and something interrupts the suspend.
> >> >
> >>
> >> This is not a rare event. For example, the matrix keypad driver blocks
> >> suspend when a key is down so it can scan the matrix.
> >
> > Sure, but how many times per day are you suspending?
> 
> How many times we successfully suspend is irrelevant here. If the
> driver blocks suspend the number of suspend attempts depend on your
> poll frequency.

Actually, this is quite interesting question I'd like answer here:

"Sure, but how many times per day are you suspending?"

I suspect it may be in 1000s, but it would be cool to get better
answer -- so that people knew what we are talking about here.
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20 22:18         ` Rafael J. Wysocki
  2010-05-21  6:04           ` Florian Mickler
  2010-05-21  6:04           ` Florian Mickler
@ 2010-05-27 15:41           ` Pavel Machek
  2010-05-27 15:41           ` Pavel Machek
  3 siblings, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-27 15:41 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Florian Mickler, Arve Hj??nnev??g, linux-pm, linux-kernel,
	Len Brown, Randy Dunlap, Andrew Morton, Andi Kleen,
	Cornelia Huck, Tejun Heo, Jesse Barnes, Magnus Damm,
	Nigel Cunningham, Alan Stern, Ming Lei, Wu Fengguang,
	Maxim Levitsky, linux-doc

Hi!

> > > > Yeah, one file selects behavior of another file, and to read available
> > > > states for opportunistic, you have to write to file first.
> > > > 
> > > > I still don't like the interface.
> > > > 
> > > 
> > > Actually, what would be a better interface? 
> > > 
> > > I wonder why it is not like this:
> 
> Because I think the "forced" and "opportunistic" suspend "modes" are mutually
> exclusive in practice and the interface as proposed reflects that quite well.

Why should they be? Forced disk while opportunistic mem is active
makes a lot of sense. If code can't support it now, just return
-EINVAL, but please don't cripple the interface just because of that.

> > > /sys/power/state 
> > > 	no change, works with and without opportunistic suspend the
> > > 	same. Ignores suspend blockers. Really no change. (From user
> > > 	perspective)
> > > 
> > > /sys/power/opportunistic
> > > 	On / Off
> > > 	While Off the opportunistic suspend is off.
> > > 	While On, the opportunistic suspend is on and if there are no
> > > 	suspend blockers the system goes to suspend. 
> > > 
> > 
> > I forgot, of course there needs to be another knob to implement the
> > "on" behaviour  in the opportunistic mode
> > 
> >  /sys/power/block_opportunistic_suspend
> > 
> > There you have it. One file, one purpose. 
> 
> That's getting messy IMHO.
> 
> In addition to that you get a nice race when the user writes "mem"
> to /sys/power/state and opportunistic suspend happens at the same
> time.

It should not opportunistically suspend when it has work to do (like
entering forced suspend).
									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20 22:18         ` Rafael J. Wysocki
                             ` (2 preceding siblings ...)
  2010-05-27 15:41           ` Pavel Machek
@ 2010-05-27 15:41           ` Pavel Machek
  3 siblings, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-27 15:41 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Len Brown, Andi Kleen, linux-doc, Jesse Barnes, linux-kernel,
	Florian Mickler, Magnus Damm, Tejun Heo, linux-pm, Wu Fengguang,
	Andrew Morton

Hi!

> > > > Yeah, one file selects behavior of another file, and to read available
> > > > states for opportunistic, you have to write to file first.
> > > > 
> > > > I still don't like the interface.
> > > > 
> > > 
> > > Actually, what would be a better interface? 
> > > 
> > > I wonder why it is not like this:
> 
> Because I think the "forced" and "opportunistic" suspend "modes" are mutually
> exclusive in practice and the interface as proposed reflects that quite well.

Why should they be? Forced disk while opportunistic mem is active
makes a lot of sense. If code can't support it now, just return
-EINVAL, but please don't cripple the interface just because of that.

> > > /sys/power/state 
> > > 	no change, works with and without opportunistic suspend the
> > > 	same. Ignores suspend blockers. Really no change. (From user
> > > 	perspective)
> > > 
> > > /sys/power/opportunistic
> > > 	On / Off
> > > 	While Off the opportunistic suspend is off.
> > > 	While On, the opportunistic suspend is on and if there are no
> > > 	suspend blockers the system goes to suspend. 
> > > 
> > 
> > I forgot, of course there needs to be another knob to implement the
> > "on" behaviour  in the opportunistic mode
> > 
> >  /sys/power/block_opportunistic_suspend
> > 
> > There you have it. One file, one purpose. 
> 
> That's getting messy IMHO.
> 
> In addition to that you get a nice race when the user writes "mem"
> to /sys/power/state and opportunistic suspend happens at the same
> time.

It should not opportunistically suspend when it has work to do (like
entering forced suspend).
									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20 22:27               ` Rafael J. Wysocki
@ 2010-05-21  6:35                   ` Tejun Heo
  0 siblings, 0 replies; 195+ messages in thread
From: Tejun Heo @ 2010-05-21  6:35 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Oleg Nesterov, Arve Hjønnevåg, linux-pm, linux-kernel,
	Alan Stern, Len Brown, Pavel Machek, Randy Dunlap, Jesse Barnes,
	Magnus Damm, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

Hello,

On 05/21/2010 12:27 AM, Rafael J. Wysocki wrote:
>> Oh, this isn't an issue w/ cmwq.  While frozen all new works are
>> collected into per-cpu delayed worklist and while frozen trustee in
>> charge of the cpu will keep waiting.  Once thawed, trustee will
>> execute all works including the delayed ones unbound to any cpu.
> 
> So, does it require any intrusive changes to make it possible to create
> multithread freezable workqueues?

cmwq itself is quite intrusive changes but w/ cmwq it just takes
flipping a flag when creating the queue.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
@ 2010-05-21  6:35                   ` Tejun Heo
  0 siblings, 0 replies; 195+ messages in thread
From: Tejun Heo @ 2010-05-21  6:35 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Len Brown, linux-doc, linux-kernel, Jesse Barnes, Oleg Nesterov,
	Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

Hello,

On 05/21/2010 12:27 AM, Rafael J. Wysocki wrote:
>> Oh, this isn't an issue w/ cmwq.  While frozen all new works are
>> collected into per-cpu delayed worklist and while frozen trustee in
>> charge of the cpu will keep waiting.  Once thawed, trustee will
>> execute all works including the delayed ones unbound to any cpu.
> 
> So, does it require any intrusive changes to make it possible to create
> multithread freezable workqueues?

cmwq itself is quite intrusive changes but w/ cmwq it just takes
flipping a flag when creating the queue.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20 22:18         ` Rafael J. Wysocki
@ 2010-05-21  6:04           ` Florian Mickler
  2010-05-21  6:04           ` Florian Mickler
                             ` (2 subsequent siblings)
  3 siblings, 0 replies; 195+ messages in thread
From: Florian Mickler @ 2010-05-21  6:04 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Pavel Machek, Arve Hj??nnev??g, linux-pm, linux-kernel,
	Len Brown, Randy Dunlap, Andrew Morton, Andi Kleen,
	Cornelia Huck, Tejun Heo, Jesse Barnes, Magnus Damm,
	Nigel Cunningham, Alan Stern, Ming Lei, Wu Fengguang,
	Maxim Levitsky, linux-doc

On Fri, 21 May 2010 00:18:43 +0200
"Rafael J. Wysocki" <rjw@sisk.pl> wrote:

> > > Actually, what would be a better interface? 
> > > 
> > > I wonder why it is not like this:
> 
> Because I think the "forced" and "opportunistic" suspend "modes" are mutually
> exclusive in practice and the interface as proposed reflects that quite well.
> 
> > > /sys/power/state 
> > > 	no change, works with and without opportunistic suspend the
> > > 	same. Ignores suspend blockers. Really no change. (From user
> > > 	perspective)
> > > 
> > > /sys/power/opportunistic
> > > 	On / Off
> > > 	While Off the opportunistic suspend is off.
> > > 	While On, the opportunistic suspend is on and if there are no
> > > 	suspend blockers the system goes to suspend. 
> > > 
> > 
> > I forgot, of course there needs to be another knob to implement the
> > "on" behaviour  in the opportunistic mode
> > 
> >  /sys/power/block_opportunistic_suspend
> > 
> > There you have it. One file, one purpose. 
> 
> That's getting messy IMHO.
> 
> In addition to that you get a nice race when the user writes "mem"
> to /sys/power/state and opportunistic suspend happens at the same time.
> If the latter wins the race, the system will suspend again immediately after
> being woken up, which probably is not the result you'd like to get.

But I don't think there is a problem with that. If the system is
'awake' (suspend blocked) and you hit it with forced 'mem', the system
_has_ to suspend. as that is what forced "mem"  means. And if
opportunistic won the race you would _expect_ the machine to suspend
again after the wakeup (and this time for good).

But perhaps this only makes sense if you can specify different
wake-events for opportunistic and forced suspend.

This is probably some kind of bikeshed by now. I'm alright with the
status quo. For what it's worth (not much): You can add my Reviewed-By.

Cheers,
Flo


^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20 22:18         ` Rafael J. Wysocki
  2010-05-21  6:04           ` Florian Mickler
@ 2010-05-21  6:04           ` Florian Mickler
  2010-05-27 15:41           ` Pavel Machek
  2010-05-27 15:41           ` Pavel Machek
  3 siblings, 0 replies; 195+ messages in thread
From: Florian Mickler @ 2010-05-21  6:04 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-doc, Barnes, Andi Kleen, Andrew, Len, linux-pm, Brown,
	Magnus Damm, Nigel, linux-kernel, Tejun Heo, Morton,
	Wu Fengguang, Jesse

On Fri, 21 May 2010 00:18:43 +0200
"Rafael J. Wysocki" <rjw@sisk.pl> wrote:

> > > Actually, what would be a better interface? 
> > > 
> > > I wonder why it is not like this:
> 
> Because I think the "forced" and "opportunistic" suspend "modes" are mutually
> exclusive in practice and the interface as proposed reflects that quite well.
> 
> > > /sys/power/state 
> > > 	no change, works with and without opportunistic suspend the
> > > 	same. Ignores suspend blockers. Really no change. (From user
> > > 	perspective)
> > > 
> > > /sys/power/opportunistic
> > > 	On / Off
> > > 	While Off the opportunistic suspend is off.
> > > 	While On, the opportunistic suspend is on and if there are no
> > > 	suspend blockers the system goes to suspend. 
> > > 
> > 
> > I forgot, of course there needs to be another knob to implement the
> > "on" behaviour  in the opportunistic mode
> > 
> >  /sys/power/block_opportunistic_suspend
> > 
> > There you have it. One file, one purpose. 
> 
> That's getting messy IMHO.
> 
> In addition to that you get a nice race when the user writes "mem"
> to /sys/power/state and opportunistic suspend happens at the same time.
> If the latter wins the race, the system will suspend again immediately after
> being woken up, which probably is not the result you'd like to get.

But I don't think there is a problem with that. If the system is
'awake' (suspend blocked) and you hit it with forced 'mem', the system
_has_ to suspend. as that is what forced "mem"  means. And if
opportunistic won the race you would _expect_ the machine to suspend
again after the wakeup (and this time for good).

But perhaps this only makes sense if you can specify different
wake-events for opportunistic and forced suspend.

This is probably some kind of bikeshed by now. I'm alright with the
status quo. For what it's worth (not much): You can add my Reviewed-By.

Cheers,
Flo

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20  8:30             ` Tejun Heo
@ 2010-05-20 22:27               ` Rafael J. Wysocki
  2010-05-21  6:35                   ` Tejun Heo
  2010-05-20 22:27               ` Rafael J. Wysocki
  1 sibling, 1 reply; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-20 22:27 UTC (permalink / raw)
  To: Tejun Heo
  Cc: Oleg Nesterov, Arve Hjønnevåg, linux-pm, linux-kernel,
	Alan Stern, Len Brown, Pavel Machek, Randy Dunlap, Jesse Barnes,
	Magnus Damm, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

On Thursday 20 May 2010, Tejun Heo wrote:
> Hello,
> 
> (sorry about late reply)
> 
> On 04/30/2010 07:26 PM, Oleg Nesterov wrote:
> > Currently _cpu_down() can't flush and/or stop the frozen cwq->thread.
> > 
> > IIRC this is fixable, but needs the nasty complications. We should
> > thaw + stop the frozen cwq->thread, then move the pending works to
> > another CPU.
> 
> Oh, this isn't an issue w/ cmwq.  While frozen all new works are
> collected into per-cpu delayed worklist and while frozen trustee in
> charge of the cpu will keep waiting.  Once thawed, trustee will
> execute all works including the delayed ones unbound to any cpu.

So, does it require any intrusive changes to make it possible to create
multithread freezable workqueues?

Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20  8:30             ` Tejun Heo
  2010-05-20 22:27               ` Rafael J. Wysocki
@ 2010-05-20 22:27               ` Rafael J. Wysocki
  1 sibling, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-20 22:27 UTC (permalink / raw)
  To: Tejun Heo
  Cc: Len Brown, linux-doc, linux-kernel, Jesse Barnes, Oleg Nesterov,
	Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Thursday 20 May 2010, Tejun Heo wrote:
> Hello,
> 
> (sorry about late reply)
> 
> On 04/30/2010 07:26 PM, Oleg Nesterov wrote:
> > Currently _cpu_down() can't flush and/or stop the frozen cwq->thread.
> > 
> > IIRC this is fixable, but needs the nasty complications. We should
> > thaw + stop the frozen cwq->thread, then move the pending works to
> > another CPU.
> 
> Oh, this isn't an issue w/ cmwq.  While frozen all new works are
> collected into per-cpu delayed worklist and while frozen trustee in
> charge of the cpu will keep waiting.  Once thawed, trustee will
> execute all works including the delayed ones unbound to any cpu.

So, does it require any intrusive changes to make it possible to create
multithread freezable workqueues?

Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20  9:26       ` Florian Mickler
  2010-05-20 22:18         ` Rafael J. Wysocki
@ 2010-05-20 22:18         ` Rafael J. Wysocki
  2010-05-21  6:04           ` Florian Mickler
                             ` (3 more replies)
  1 sibling, 4 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-20 22:18 UTC (permalink / raw)
  To: Florian Mickler
  Cc: Pavel Machek, Arve Hj??nnev??g, linux-pm, linux-kernel,
	Len Brown, Randy Dunlap, Andrew Morton, Andi Kleen,
	Cornelia Huck, Tejun Heo, Jesse Barnes, Magnus Damm,
	Nigel Cunningham, Alan Stern, Ming Lei, Wu Fengguang,
	Maxim Levitsky, linux-doc

On Thursday 20 May 2010, Florian Mickler wrote:
> On Thu, 20 May 2010 11:11:11 +0200
> Florian Mickler <florian@mickler.org> wrote:
> 
> > On Tue, 18 May 2010 15:11:11 +0200
> > Pavel Machek <pavel@ucw.cz> wrote:
> > 
> > > On Thu 2010-05-13 21:11:06, Arve Hj??nnev??g wrote:
> > > > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > > > After setting the policy to opportunistic, writes to /sys/power/state
> > > > become non-blocking requests that specify which suspend state to
> > > > enter
> > > 
> > > Yeah, one file selects behavior of another file, and to read available
> > > states for opportunistic, you have to write to file first.
> > > 
> > > I still don't like the interface.
> > > 
> > 
> > Actually, what would be a better interface? 
> > 
> > I wonder why it is not like this:

Because I think the "forced" and "opportunistic" suspend "modes" are mutually
exclusive in practice and the interface as proposed reflects that quite well.

> > /sys/power/state 
> > 	no change, works with and without opportunistic suspend the
> > 	same. Ignores suspend blockers. Really no change. (From user
> > 	perspective)
> > 
> > /sys/power/opportunistic
> > 	On / Off
> > 	While Off the opportunistic suspend is off.
> > 	While On, the opportunistic suspend is on and if there are no
> > 	suspend blockers the system goes to suspend. 
> > 
> 
> I forgot, of course there needs to be another knob to implement the
> "on" behaviour  in the opportunistic mode
> 
>  /sys/power/block_opportunistic_suspend
> 
> There you have it. One file, one purpose. 

That's getting messy IMHO.

In addition to that you get a nice race when the user writes "mem"
to /sys/power/state and opportunistic suspend happens at the same time.
If the latter wins the race, the system will suspend again immediately after
being woken up, which probably is not the result you'd like to get.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20  9:26       ` Florian Mickler
@ 2010-05-20 22:18         ` Rafael J. Wysocki
  2010-05-20 22:18         ` Rafael J. Wysocki
  1 sibling, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-20 22:18 UTC (permalink / raw)
  To: Florian Mickler
  Cc: Len Brown, Andi Kleen, linux-doc, Jesse Barnes, linux-kernel,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Thursday 20 May 2010, Florian Mickler wrote:
> On Thu, 20 May 2010 11:11:11 +0200
> Florian Mickler <florian@mickler.org> wrote:
> 
> > On Tue, 18 May 2010 15:11:11 +0200
> > Pavel Machek <pavel@ucw.cz> wrote:
> > 
> > > On Thu 2010-05-13 21:11:06, Arve Hj??nnev??g wrote:
> > > > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > > > After setting the policy to opportunistic, writes to /sys/power/state
> > > > become non-blocking requests that specify which suspend state to
> > > > enter
> > > 
> > > Yeah, one file selects behavior of another file, and to read available
> > > states for opportunistic, you have to write to file first.
> > > 
> > > I still don't like the interface.
> > > 
> > 
> > Actually, what would be a better interface? 
> > 
> > I wonder why it is not like this:

Because I think the "forced" and "opportunistic" suspend "modes" are mutually
exclusive in practice and the interface as proposed reflects that quite well.

> > /sys/power/state 
> > 	no change, works with and without opportunistic suspend the
> > 	same. Ignores suspend blockers. Really no change. (From user
> > 	perspective)
> > 
> > /sys/power/opportunistic
> > 	On / Off
> > 	While Off the opportunistic suspend is off.
> > 	While On, the opportunistic suspend is on and if there are no
> > 	suspend blockers the system goes to suspend. 
> > 
> 
> I forgot, of course there needs to be another knob to implement the
> "on" behaviour  in the opportunistic mode
> 
>  /sys/power/block_opportunistic_suspend
> 
> There you have it. One file, one purpose. 

That's getting messy IMHO.

In addition to that you get a nice race when the user writes "mem"
to /sys/power/state and opportunistic suspend happens at the same time.
If the latter wins the race, the system will suspend again immediately after
being woken up, which probably is not the result you'd like to get.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20  9:11     ` Florian Mickler
  2010-05-20  9:26       ` Florian Mickler
@ 2010-05-20  9:26       ` Florian Mickler
  2010-05-20 22:18         ` Rafael J. Wysocki
  2010-05-20 22:18         ` Rafael J. Wysocki
  1 sibling, 2 replies; 195+ messages in thread
From: Florian Mickler @ 2010-05-20  9:26 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Arve Hj??nnev??g, linux-pm, linux-kernel, Rafael J. Wysocki,
	Len Brown, Randy Dunlap, Andrew Morton, Andi Kleen,
	Cornelia Huck, Tejun Heo, Jesse Barnes, Magnus Damm,
	Nigel Cunningham, Alan Stern, Ming Lei, Wu Fengguang,
	Maxim Levitsky, linux-doc

On Thu, 20 May 2010 11:11:11 +0200
Florian Mickler <florian@mickler.org> wrote:

> On Tue, 18 May 2010 15:11:11 +0200
> Pavel Machek <pavel@ucw.cz> wrote:
> 
> > On Thu 2010-05-13 21:11:06, Arve Hj??nnev??g wrote:
> > > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > > After setting the policy to opportunistic, writes to /sys/power/state
> > > become non-blocking requests that specify which suspend state to
> > > enter
> > 
> > Yeah, one file selects behavior of another file, and to read available
> > states for opportunistic, you have to write to file first.
> > 
> > I still don't like the interface.
> > 
> 
> Actually, what would be a better interface? 
> 
> I wonder why it is not like this:
> 
> /sys/power/state 
> 	no change, works with and without opportunistic suspend the
> 	same. Ignores suspend blockers. Really no change. (From user
> 	perspective)
> 
> /sys/power/opportunistic
> 	On / Off
> 	While Off the opportunistic suspend is off.
> 	While On, the opportunistic suspend is on and if there are no
> 	suspend blockers the system goes to suspend. 
> 

I forgot, of course there needs to be another knob to implement the
"on" behaviour  in the opportunistic mode

 /sys/power/block_opportunistic_suspend

There you have it. One file, one purpose. 

> Cheers,
> Flo

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-20  9:11     ` Florian Mickler
@ 2010-05-20  9:26       ` Florian Mickler
  2010-05-20  9:26       ` Florian Mickler
  1 sibling, 0 replies; 195+ messages in thread
From: Florian Mickler @ 2010-05-20  9:26 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Len Brown, Kleen, Andi, linux-doc, Tejun, Jesse Barnes,
	linux-kernel, Heo, Magnus Damm, linux-pm, Wu Fengguang,
	Andrew Morton

On Thu, 20 May 2010 11:11:11 +0200
Florian Mickler <florian@mickler.org> wrote:

> On Tue, 18 May 2010 15:11:11 +0200
> Pavel Machek <pavel@ucw.cz> wrote:
> 
> > On Thu 2010-05-13 21:11:06, Arve Hj??nnev??g wrote:
> > > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > > After setting the policy to opportunistic, writes to /sys/power/state
> > > become non-blocking requests that specify which suspend state to
> > > enter
> > 
> > Yeah, one file selects behavior of another file, and to read available
> > states for opportunistic, you have to write to file first.
> > 
> > I still don't like the interface.
> > 
> 
> Actually, what would be a better interface? 
> 
> I wonder why it is not like this:
> 
> /sys/power/state 
> 	no change, works with and without opportunistic suspend the
> 	same. Ignores suspend blockers. Really no change. (From user
> 	perspective)
> 
> /sys/power/opportunistic
> 	On / Off
> 	While Off the opportunistic suspend is off.
> 	While On, the opportunistic suspend is on and if there are no
> 	suspend blockers the system goes to suspend. 
> 

I forgot, of course there needs to be another knob to implement the
"on" behaviour  in the opportunistic mode

 /sys/power/block_opportunistic_suspend

There you have it. One file, one purpose. 

> Cheers,
> Flo

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-18 13:11   ` Pavel Machek
@ 2010-05-20  9:11     ` Florian Mickler
  2010-05-20  9:26       ` Florian Mickler
  2010-05-20  9:26       ` Florian Mickler
  2010-05-20  9:11     ` Florian Mickler
  1 sibling, 2 replies; 195+ messages in thread
From: Florian Mickler @ 2010-05-20  9:11 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Arve Hj??nnev??g, linux-pm, linux-kernel, Rafael J. Wysocki,
	Len Brown, Randy Dunlap, Andrew Morton, Andi Kleen,
	Cornelia Huck, Tejun Heo, Jesse Barnes, Magnus Damm,
	Nigel Cunningham, Alan Stern, Ming Lei, Wu Fengguang,
	Maxim Levitsky, linux-doc

On Tue, 18 May 2010 15:11:11 +0200
Pavel Machek <pavel@ucw.cz> wrote:

> On Thu 2010-05-13 21:11:06, Arve Hj??nnev??g wrote:
> > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > After setting the policy to opportunistic, writes to /sys/power/state
> > become non-blocking requests that specify which suspend state to
> > enter
> 
> Yeah, one file selects behavior of another file, and to read available
> states for opportunistic, you have to write to file first.
> 
> I still don't like the interface.
> 

Actually, what would be a better interface? 

I wonder why it is not like this:

/sys/power/state 
	no change, works with and without opportunistic suspend the
	same. Ignores suspend blockers. Really no change. (From user
	perspective)

/sys/power/opportunistic
	On / Off
	While Off the opportunistic suspend is off.
	While On, the opportunistic suspend is on and if there are no
	suspend blockers the system goes to suspend. 

Cheers,
Flo




^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-18 13:11   ` Pavel Machek
  2010-05-20  9:11     ` Florian Mickler
@ 2010-05-20  9:11     ` Florian Mickler
  1 sibling, 0 replies; 195+ messages in thread
From: Florian Mickler @ 2010-05-20  9:11 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Len Brown, Kleen, Andi, linux-doc, Tejun, Jesse Barnes,
	linux-kernel, Heo, Magnus Damm, linux-pm, Wu Fengguang,
	Andrew Morton

On Tue, 18 May 2010 15:11:11 +0200
Pavel Machek <pavel@ucw.cz> wrote:

> On Thu 2010-05-13 21:11:06, Arve Hj??nnev??g wrote:
> > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > After setting the policy to opportunistic, writes to /sys/power/state
> > become non-blocking requests that specify which suspend state to
> > enter
> 
> Yeah, one file selects behavior of another file, and to read available
> states for opportunistic, you have to write to file first.
> 
> I still don't like the interface.
> 

Actually, what would be a better interface? 

I wonder why it is not like this:

/sys/power/state 
	no change, works with and without opportunistic suspend the
	same. Ignores suspend blockers. Really no change. (From user
	perspective)

/sys/power/opportunistic
	On / Off
	While Off the opportunistic suspend is off.
	While On, the opportunistic suspend is on and if there are no
	suspend blockers the system goes to suspend. 

Cheers,
Flo

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-30 17:26           ` Oleg Nesterov
@ 2010-05-20  8:30             ` Tejun Heo
  2010-05-20 22:27               ` Rafael J. Wysocki
  2010-05-20 22:27               ` Rafael J. Wysocki
  2010-05-20  8:30             ` Tejun Heo
  1 sibling, 2 replies; 195+ messages in thread
From: Tejun Heo @ 2010-05-20  8:30 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Rafael J. Wysocki, Arve Hjønnevåg, linux-pm,
	linux-kernel, Alan Stern, Len Brown, Pavel Machek, Randy Dunlap,
	Jesse Barnes, Magnus Damm, Nigel Cunningham, Cornelia Huck,
	Ming Lei, Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

Hello,

(sorry about late reply)

On 04/30/2010 07:26 PM, Oleg Nesterov wrote:
> Currently _cpu_down() can't flush and/or stop the frozen cwq->thread.
> 
> IIRC this is fixable, but needs the nasty complications. We should
> thaw + stop the frozen cwq->thread, then move the pending works to
> another CPU.

Oh, this isn't an issue w/ cmwq.  While frozen all new works are
collected into per-cpu delayed worklist and while frozen trustee in
charge of the cpu will keep waiting.  Once thawed, trustee will
execute all works including the delayed ones unbound to any cpu.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-30 17:26           ` Oleg Nesterov
  2010-05-20  8:30             ` Tejun Heo
@ 2010-05-20  8:30             ` Tejun Heo
  1 sibling, 0 replies; 195+ messages in thread
From: Tejun Heo @ 2010-05-20  8:30 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: =?ISO-8859-1?Q?Arve_Hj=F8nnev=E5?=,
	Len Brown, linux-doc, linux-kernel, Jesse Barnes, Magnus Damm,
	linux-pm, Wu Fengguang, Andrew Morton

Hello,

(sorry about late reply)

On 04/30/2010 07:26 PM, Oleg Nesterov wrote:
> Currently _cpu_down() can't flush and/or stop the frozen cwq->thread.
> 
> IIRC this is fixable, but needs the nasty complications. We should
> thaw + stop the frozen cwq->thread, then move the pending works to
> another CPU.

Oh, this isn't an issue w/ cmwq.  While frozen all new works are
collected into per-cpu delayed worklist and while frozen trustee in
charge of the cpu will keep waiting.  Once thawed, trustee will
execute all works including the delayed ones unbound to any cpu.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-19 15:55               ` Paul Walmsley
@ 2010-05-20  0:35                 ` Arve Hjønnevåg
  0 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-20  0:35 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: Jesse Barnes, linux-kernel, linux-pm, Liam Girdwood,
	Matthew Garrett, Len Brown, Jacob Pan, Oleg Nesterov, linux-omap,
	Linus Walleij, Daniel Walker, Theodore Ts'o, Brian Swetland,
	Mark Brown, Geoff Smith, Tejun Heo, Andrew Morton, Wu Fengguang,
	Arjan van de Ven

On Wed, May 19, 2010 at 8:55 AM, Paul Walmsley <paul@pwsan.com> wrote:
> Hi Arve,
>
> On Mon, 17 May 2010, Arve Hjønnevåg wrote:
>
>> On Mon, May 17, 2010 at 8:34 PM, Paul Walmsley <paul@pwsan.com> wrote:
>> > On Mon, 17 May 2010, Arve Hjønnevåg wrote:
>> >> On Mon, May 17, 2010 at 7:17 PM, Paul Walmsley <paul@pwsan.com> wrote:
>> >> > On Fri, 14 May 2010, Arve Hjønnevåg wrote:
>> >> >> On Thu, May 13, 2010 at 11:27 PM, Paul Walmsley <paul@pwsan.com> wrote:
>> >> >> > On Thu, 13 May 2010, Arve Hjønnevåg wrote:
>> >> >> >
>> >> >> >> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
>> >> >> >> After setting the policy to opportunistic, writes to /sys/power/state
>> >> >> >> become non-blocking requests that specify which suspend state to enter
>> >> >> >> when no suspend blockers are active. A special state, "on", stops the
>> >> >> >> process by activating the "main" suspend blocker.
>> >> >> >>
>> >> >> >> Signed-off-by: Arve Hjønnevåg <arve@android.com>
>> >> >> >
>> >> >> > Looking at the way that suspend-blocks are used in the current Android
>> >> >> > 'msm' kernel tree[1], they seem likely to cause layering violations,
>> >> >> > fragile kernel code with userspace dependencies, and code that consumes
>> >> >> > power unnecessarily.
>> >> >> >
>> >> >> > For example, in drivers/mmc/core/core.c:721, in mmc_rescan() [2], we find
>> >> >> > the following code:
>> >> >> >
>> >> >> >        /* give userspace some time to react */
>> >> >> >        wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
>> >> >> >
>> >> >> > This is a layering violation.  The MMC subsystem should have nothing to do
>> >> >> > with "giving userspace time to react."  That is the responsibility of the
>> >> >> > Linux scheduler.
>> >> >> >
>> >> >> > This code is also intrinsically fragile and use-case dependent.  What if
>> >> >> > userspace occasionally needs more than (HZ / 2) to react?  If the
>> >> >> > distributor is lucky enough to catch this before the product ships, then
>> >> >> > the distributor can patch in a new magic number.  But if the device makes
>> >> >> > it to the consumer, the result is an unstable device that unpredictably
>> >> >> > suspends.
>> >> >> >
>> >> >> > The above code will also waste power.  Even if userspace takes less than
>> >> >> > (HZ / 2) to react, the system will still be prevented from entering a
>> >> >> > system-wide low power state for the duration of the remaining time.  This
>> >> >> > is in contrast to an approach that uses the idle loop to enter a
>> >> >> > system-wide low power state.  The moment that the system has no further
>> >> >> > work to do, it can start saving power.
>> >> >> >
>> >> >>
>> >> >> Yes, preventing suspend for 1/2 second wastes some power, but are you
>> >> >> seriously suggesting that it wastes more power then never suspending?
>> >> >> Opportunitic suspend does not prevent entering low power modes from
>> >> >> idle.
>> >> >
>> >> > Sorry, my E-mail was unclear.  Here is my intended point:
>> >> >
>> >> > The current Android opportunistic suspend governor does not enter full
>> >> > system suspend until all suspend-blocks are released.  If the governor
>> >> > were modified to respect the scheduler, then it could enter suspend the
>> >> > moment that the system had no further work to do.
>> >> >
>> >>
>> >> On the hardware that shipped we enter the same power state from idle
>> >> and suspend, so the only power savings we get from suspend that we
>> >> don't get in idle is from not respecting the scheduler and timers.
>> >
>> > Other people
>> > say that their hardware XXX.  (This is probably likely for anything that
>> > supports ACPI.)
>
> Heh, looks like this part of my E-mail escaped a little too early.
>
>> >> > So during part of the (HZ / 2) time interval in the above code, the system
>> >> > is running some kernel or userspace code.  But after that work is done, an
>> >> > idle-loop-based opportunistic suspend governor could enter idle during
>> >> > the remaining portion of that (HZ / 2) time interval, while the current
>> >> > governor must keep the system out of suspend.
>> >> >
>> >> Triggering a full suspend (that disregards normal timers) from idle
>> >> does not work, since the system may temporarily enter idle while
>> >> processing the event.
>> >
>> > Could you provide an example?
>>
>> The code takes a pagefault. The system is idle while waiting for the
>> dma to finish.
>
> OK.
>
>> > In such a case, how can the kernel guarantee that the system will process
>> > the event within (HZ / 2) seconds?
>>
>> It can't, but in my opinion if the event did not start processing in
>> 1/2 second it was probably not time critical.
>
> Thanks for the correction regarding the unit of the time interval.
>
> To use the pagefault example, wouldn't that be partially dependent on
> factors outside the control of the task that handles the input event?
> (e.g., the time needed to page back in, or other code running on the
> system.)
>
Yes (we don't need any timeouts to handle input events though).

>> >> > Of course, to do this, a different method for freezing processes would be
>> >> > needed; one possible approach is described here[1]; others have proposed
>> >> > similar approaches.
>> >>
>> >> If you freeze processes you need something like a suspend blocker to
>> >> prevent processes from being frozen.
>> >
>> > (It occurs to me that "frozen" is the wrong terminology to use.
>> >  Perhaps "paused" would be a better term, along the lines of [1].)
>> >
>> > The full set of processes does not need to be paused.  For example, if
>> > "untrusted" processes are run in a cgroup, and only tasks in that cgroup
>> > are put into TASK_INTERRUPTIBLE, then "trusted" processes will not be
>> > paused.
>>
>> We want to freeze trusted processes.
>
> By "trusted," I mean processes that are run with a suspend block, as in
> your example to Tony[1].  Isn't it so that Android has processes that
> either run entirely under suspend-blocks, or which take suspend-blocks at
> some point in time during their execution?  Wouldn't those processes need
> to be prevented from a force-pause or force-freeze?
>
We don't freeze processes while any suspend blockers are active.

>> >> By having a process that never gets frozen you could implement this
>> >> entirely in user space, but you would have to funnel all wakeup events
>> >> though this process.
>> >
>> > The proposal[2] does require a kernel change.  No wakeup funnel process
>> > would be needed.  The proposal is to put processes into
>> > TASK_INTERRUPTIBLE, not TASK_STOPPED (unlike most "freezers").
>> >
>>
>> Why is the method used for pausing the processes relevant? If the
>> process is paused it cannot process the wakeup event.
>
> Tasks in TASK_INTERRUPTIBLE are woken back up when an event arrives,

So you are not actually preventing them from. A task that wakes up 60
times a second will still drain the battery, unless you happened to
pause it while it was active.

> unlike tasks in TASK_STOPPED, which are stopped until a SIGCONT arrives.
>
>> The only way to get the wake event to the process is to funnel it though
>> a process that never is paused.
>
>
> - Paul
>
> 1. http://lkml.org/lkml/2010/5/7/420
>
>



-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-18  3:51             ` Arve Hjønnevåg
@ 2010-05-19 15:55               ` Paul Walmsley
  2010-05-20  0:35                 ` Arve Hjønnevåg
  0 siblings, 1 reply; 195+ messages in thread
From: Paul Walmsley @ 2010-05-19 15:55 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Jesse Barnes, linux-kernel, linux-pm, Liam Girdwood,
	Matthew Garrett, Len Brown, Jacob Pan, Oleg Nesterov, linux-omap,
	Linus Walleij, Daniel Walker, Theodore Ts'o, Brian Swetland,
	Mark Brown, Geoff Smith, Tejun Heo, Andrew Morton, Wu Fengguang,
	Arjan van de Ven

[-- Attachment #1: Type: TEXT/PLAIN, Size: 6871 bytes --]

Hi Arve,

On Mon, 17 May 2010, Arve Hjønnevåg wrote:

> On Mon, May 17, 2010 at 8:34 PM, Paul Walmsley <paul@pwsan.com> wrote:
> > On Mon, 17 May 2010, Arve Hjønnevåg wrote:
> >> On Mon, May 17, 2010 at 7:17 PM, Paul Walmsley <paul@pwsan.com> wrote:
> >> > On Fri, 14 May 2010, Arve Hjønnevåg wrote:
> >> >> On Thu, May 13, 2010 at 11:27 PM, Paul Walmsley <paul@pwsan.com> wrote:
> >> >> > On Thu, 13 May 2010, Arve Hjønnevåg wrote:
> >> >> >
> >> >> >> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> >> >> >> After setting the policy to opportunistic, writes to /sys/power/state
> >> >> >> become non-blocking requests that specify which suspend state to enter
> >> >> >> when no suspend blockers are active. A special state, "on", stops the
> >> >> >> process by activating the "main" suspend blocker.
> >> >> >>
> >> >> >> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> >> >> >
> >> >> > Looking at the way that suspend-blocks are used in the current Android
> >> >> > 'msm' kernel tree[1], they seem likely to cause layering violations,
> >> >> > fragile kernel code with userspace dependencies, and code that consumes
> >> >> > power unnecessarily.
> >> >> >
> >> >> > For example, in drivers/mmc/core/core.c:721, in mmc_rescan() [2], we find
> >> >> > the following code:
> >> >> >
> >> >> >        /* give userspace some time to react */
> >> >> >        wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
> >> >> >
> >> >> > This is a layering violation.  The MMC subsystem should have nothing to do
> >> >> > with "giving userspace time to react."  That is the responsibility of the
> >> >> > Linux scheduler.
> >> >> >
> >> >> > This code is also intrinsically fragile and use-case dependent.  What if
> >> >> > userspace occasionally needs more than (HZ / 2) to react?  If the
> >> >> > distributor is lucky enough to catch this before the product ships, then
> >> >> > the distributor can patch in a new magic number.  But if the device makes
> >> >> > it to the consumer, the result is an unstable device that unpredictably
> >> >> > suspends.
> >> >> >
> >> >> > The above code will also waste power.  Even if userspace takes less than
> >> >> > (HZ / 2) to react, the system will still be prevented from entering a
> >> >> > system-wide low power state for the duration of the remaining time.  This
> >> >> > is in contrast to an approach that uses the idle loop to enter a
> >> >> > system-wide low power state.  The moment that the system has no further
> >> >> > work to do, it can start saving power.
> >> >> >
> >> >>
> >> >> Yes, preventing suspend for 1/2 second wastes some power, but are you
> >> >> seriously suggesting that it wastes more power then never suspending?
> >> >> Opportunitic suspend does not prevent entering low power modes from
> >> >> idle.
> >> >
> >> > Sorry, my E-mail was unclear.  Here is my intended point:
> >> >
> >> > The current Android opportunistic suspend governor does not enter full
> >> > system suspend until all suspend-blocks are released.  If the governor
> >> > were modified to respect the scheduler, then it could enter suspend the
> >> > moment that the system had no further work to do.
> >> >
> >>
> >> On the hardware that shipped we enter the same power state from idle
> >> and suspend, so the only power savings we get from suspend that we
> >> don't get in idle is from not respecting the scheduler and timers.
> >
> > Other people
> > say that their hardware XXX.  (This is probably likely for anything that
> > supports ACPI.)

Heh, looks like this part of my E-mail escaped a little too early.

> >> > So during part of the (HZ / 2) time interval in the above code, the system
> >> > is running some kernel or userspace code.  But after that work is done, an
> >> > idle-loop-based opportunistic suspend governor could enter idle during
> >> > the remaining portion of that (HZ / 2) time interval, while the current
> >> > governor must keep the system out of suspend.
> >> >
> >> Triggering a full suspend (that disregards normal timers) from idle
> >> does not work, since the system may temporarily enter idle while
> >> processing the event.
> >
> > Could you provide an example?
> 
> The code takes a pagefault. The system is idle while waiting for the
> dma to finish.

OK.

> > In such a case, how can the kernel guarantee that the system will process
> > the event within (HZ / 2) seconds?
>
> It can't, but in my opinion if the event did not start processing in
> 1/2 second it was probably not time critical.

Thanks for the correction regarding the unit of the time interval.

To use the pagefault example, wouldn't that be partially dependent on 
factors outside the control of the task that handles the input event?  
(e.g., the time needed to page back in, or other code running on the 
system.)

> >> > Of course, to do this, a different method for freezing processes would be
> >> > needed; one possible approach is described here[1]; others have proposed
> >> > similar approaches.
> >>
> >> If you freeze processes you need something like a suspend blocker to
> >> prevent processes from being frozen.
> >
> > (It occurs to me that "frozen" is the wrong terminology to use.
> >  Perhaps "paused" would be a better term, along the lines of [1].)
> >
> > The full set of processes does not need to be paused.  For example, if
> > "untrusted" processes are run in a cgroup, and only tasks in that cgroup
> > are put into TASK_INTERRUPTIBLE, then "trusted" processes will not be
> > paused.
> 
> We want to freeze trusted processes.

By "trusted," I mean processes that are run with a suspend block, as in 
your example to Tony[1].  Isn't it so that Android has processes that 
either run entirely under suspend-blocks, or which take suspend-blocks at 
some point in time during their execution?  Wouldn't those processes need 
to be prevented from a force-pause or force-freeze?

> >> By having a process that never gets frozen you could implement this
> >> entirely in user space, but you would have to funnel all wakeup events
> >> though this process.
> >
> > The proposal[2] does require a kernel change.  No wakeup funnel process
> > would be needed.  The proposal is to put processes into
> > TASK_INTERRUPTIBLE, not TASK_STOPPED (unlike most "freezers").
> >
> 
> Why is the method used for pausing the processes relevant? If the
> process is paused it cannot process the wakeup event.

Tasks in TASK_INTERRUPTIBLE are woken back up when an event arrives, 
unlike tasks in TASK_STOPPED, which are stopped until a SIGCONT arrives.

> The only way to get the wake event to the process is to funnel it though 
> a process that never is paused.


- Paul

1. http://lkml.org/lkml/2010/5/7/420


[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-14  4:11   ` Arve Hjønnevåg
                     ` (3 preceding siblings ...)
  (?)
@ 2010-05-18 13:11   ` Pavel Machek
  2010-05-20  9:11     ` Florian Mickler
  2010-05-20  9:11     ` Florian Mickler
  -1 siblings, 2 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-18 13:11 UTC (permalink / raw)
  To: Arve Hj??nnev??g
  Cc: linux-pm, linux-kernel, Rafael J. Wysocki, Len Brown,
	Randy Dunlap, Andrew Morton, Andi Kleen, Cornelia Huck,
	Tejun Heo, Jesse Barnes, Magnus Damm, Nigel Cunningham,
	Alan Stern, Ming Lei, Wu Fengguang, Maxim Levitsky, linux-doc

On Thu 2010-05-13 21:11:06, Arve Hj??nnev??g wrote:
> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to
> enter

Yeah, one file selects behavior of another file, and to read available
states for opportunistic, you have to write to file first.

I still don't like the interface.

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-14  4:11   ` Arve Hjønnevåg
                     ` (2 preceding siblings ...)
  (?)
@ 2010-05-18 13:11   ` Pavel Machek
  -1 siblings, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-18 13:11 UTC (permalink / raw)
  To: Arve Hj??nnev??g
  Cc: Len Brown, Andi Kleen, linux-doc, linux-kernel, Jesse Barnes,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Thu 2010-05-13 21:11:06, Arve Hj??nnev??g wrote:
> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to
> enter

Yeah, one file selects behavior of another file, and to read available
states for opportunistic, you have to write to file first.

I still don't like the interface.

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-18  3:34           ` Paul Walmsley
@ 2010-05-18  3:51             ` Arve Hjønnevåg
  2010-05-19 15:55               ` Paul Walmsley
  0 siblings, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-18  3:51 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: Jesse Barnes, linux-kernel, linux-pm, Liam Girdwood,
	Matthew Garrett, Len Brown, Jacob Pan, Oleg Nesterov, linux-omap,
	Linus Walleij, Daniel Walker, Theodore Ts'o, Brian Swetland,
	Mark Brown, Geoff Smith, Tejun Heo, Andrew Morton, Wu Fengguang,
	Arjan van de Ven

On Mon, May 17, 2010 at 8:34 PM, Paul Walmsley <paul@pwsan.com> wrote:
> Hello Arve,
>
> On Mon, 17 May 2010, Arve Hjønnevåg wrote:
>
>> On Mon, May 17, 2010 at 7:17 PM, Paul Walmsley <paul@pwsan.com> wrote:
>> > On Fri, 14 May 2010, Arve Hjønnevåg wrote:
>> >> On Thu, May 13, 2010 at 11:27 PM, Paul Walmsley <paul@pwsan.com> wrote:
>> >> > On Thu, 13 May 2010, Arve Hjønnevåg wrote:
>> >> >
>> >> >> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
>> >> >> After setting the policy to opportunistic, writes to /sys/power/state
>> >> >> become non-blocking requests that specify which suspend state to enter
>> >> >> when no suspend blockers are active. A special state, "on", stops the
>> >> >> process by activating the "main" suspend blocker.
>> >> >>
>> >> >> Signed-off-by: Arve Hjønnevåg <arve@android.com>
>> >> >
>> >> > Looking at the way that suspend-blocks are used in the current Android
>> >> > 'msm' kernel tree[1], they seem likely to cause layering violations,
>> >> > fragile kernel code with userspace dependencies, and code that consumes
>> >> > power unnecessarily.
>> >> >
>> >> > For example, in drivers/mmc/core/core.c:721, in mmc_rescan() [2], we find
>> >> > the following code:
>> >> >
>> >> >        /* give userspace some time to react */
>> >> >        wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
>> >> >
>> >> > This is a layering violation.  The MMC subsystem should have nothing to do
>> >> > with "giving userspace time to react."  That is the responsibility of the
>> >> > Linux scheduler.
>> >> >
>> >> > This code is also intrinsically fragile and use-case dependent.  What if
>> >> > userspace occasionally needs more than (HZ / 2) to react?  If the
>> >> > distributor is lucky enough to catch this before the product ships, then
>> >> > the distributor can patch in a new magic number.  But if the device makes
>> >> > it to the consumer, the result is an unstable device that unpredictably
>> >> > suspends.
>> >> >
>> >> > The above code will also waste power.  Even if userspace takes less than
>> >> > (HZ / 2) to react, the system will still be prevented from entering a
>> >> > system-wide low power state for the duration of the remaining time.  This
>> >> > is in contrast to an approach that uses the idle loop to enter a
>> >> > system-wide low power state.  The moment that the system has no further
>> >> > work to do, it can start saving power.
>> >> >
>> >>
>> >> Yes, preventing suspend for 1/2 second wastes some power, but are you
>> >> seriously suggesting that it wastes more power then never suspending?
>> >> Opportunitic suspend does not prevent entering low power modes from
>> >> idle.
>> >
>> > Sorry, my E-mail was unclear.  Here is my intended point:
>> >
>> > The current Android opportunistic suspend governor does not enter full
>> > system suspend until all suspend-blocks are released.  If the governor
>> > were modified to respect the scheduler, then it could enter suspend the
>> > moment that the system had no further work to do.
>> >
>>
>> On the hardware that shipped we enter the same power state from idle
>> and suspend, so the only power savings we get from suspend that we
>> don't get in idle is from not respecting the scheduler and timers.
>
> Other people
> say that their hardware XXX.  (This is probably likely for anything that
> supports ACPI.)
>
>> > So during part of the (HZ / 2) time interval in the above code, the system
>> > is running some kernel or userspace code.  But after that work is done, an
>> > idle-loop-based opportunistic suspend governor could enter idle during
>> > the remaining portion of that (HZ / 2) time interval, while the current
>> > governor must keep the system out of suspend.
>> >
>> Triggering a full suspend (that disregards normal timers) from idle
>> does not work, since the system may temporarily enter idle while
>> processing the event.
>
> Could you provide an example?

The code takes a pagefault. The system is idle while waiting for the
dma to finish.

>
> In such a case, how can the kernel guarantee that the system will process
> the event within (HZ / 2) seconds?
>

It can't, but in my opinion if the event did not start processing in
1/2 second it was probably not time critical.

>> > Of course, to do this, a different method for freezing processes would be
>> > needed; one possible approach is described here[1]; others have proposed
>> > similar approaches.
>>
>> If you freeze processes you need something like a suspend blocker to
>> prevent processes from being frozen.
>
> (It occurs to me that "frozen" is the wrong terminology to use.
>  Perhaps "paused" would be a better term, along the lines of [1].)
>
> The full set of processes does not need to be paused.  For example, if
> "untrusted" processes are run in a cgroup, and only tasks in that cgroup
> are put into TASK_INTERRUPTIBLE, then "trusted" processes will not be
> paused.
>

We want to freeze trusted processes.

>> By having a process that never gets frozen you could implement this
>> entirely in user space, but you would have to funnel all wakeup events
>> though this process.
>
> The proposal[2] does require a kernel change.  No wakeup funnel process
> would be needed.  The proposal is to put processes into
> TASK_INTERRUPTIBLE, not TASK_STOPPED (unlike most "freezers").
>

Why is the method used for pausing the processes relevant? If the
process is paused it cannot process the wakeup event. The only way to
get the wake event to the process is to funnel it though a process
that never is paused.

>
> - Paul
>
>
> 1. Paragraph B4B of Paul Walmsley E-mail to the linux-pm mailing
>   list, dated Mon May 17 18:39:44 PDT 2010:
>   https://lists.linux-foundation.org/pipermail/linux-pm/2010-May/025663.html
>
> 2. ibid.
>
>



-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-18  3:06         ` Arve Hjønnevåg
@ 2010-05-18  3:34           ` Paul Walmsley
  2010-05-18  3:51             ` Arve Hjønnevåg
  0 siblings, 1 reply; 195+ messages in thread
From: Paul Walmsley @ 2010-05-18  3:34 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Jesse Barnes, linux-kernel, linux-pm, Liam Girdwood,
	Matthew Garrett, Len Brown, Jacob Pan, Oleg Nesterov, linux-omap,
	Linus Walleij, Daniel Walker, Theodore Ts'o, Brian Swetland,
	Mark Brown, Geoff Smith, Tejun Heo, Andrew Morton, Wu Fengguang,
	Arjan van de Ven

[-- Attachment #1: Type: TEXT/PLAIN, Size: 5241 bytes --]

Hello Arve,

On Mon, 17 May 2010, Arve Hjønnevåg wrote:

> On Mon, May 17, 2010 at 7:17 PM, Paul Walmsley <paul@pwsan.com> wrote:
> > On Fri, 14 May 2010, Arve Hjønnevåg wrote:
> >> On Thu, May 13, 2010 at 11:27 PM, Paul Walmsley <paul@pwsan.com> wrote:
> >> > On Thu, 13 May 2010, Arve Hjønnevåg wrote:
> >> >
> >> >> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> >> >> After setting the policy to opportunistic, writes to /sys/power/state
> >> >> become non-blocking requests that specify which suspend state to enter
> >> >> when no suspend blockers are active. A special state, "on", stops the
> >> >> process by activating the "main" suspend blocker.
> >> >>
> >> >> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> >> >
> >> > Looking at the way that suspend-blocks are used in the current Android
> >> > 'msm' kernel tree[1], they seem likely to cause layering violations,
> >> > fragile kernel code with userspace dependencies, and code that consumes
> >> > power unnecessarily.
> >> >
> >> > For example, in drivers/mmc/core/core.c:721, in mmc_rescan() [2], we find
> >> > the following code:
> >> >
> >> >        /* give userspace some time to react */
> >> >        wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
> >> >
> >> > This is a layering violation.  The MMC subsystem should have nothing to do
> >> > with "giving userspace time to react."  That is the responsibility of the
> >> > Linux scheduler.
> >> >
> >> > This code is also intrinsically fragile and use-case dependent.  What if
> >> > userspace occasionally needs more than (HZ / 2) to react?  If the
> >> > distributor is lucky enough to catch this before the product ships, then
> >> > the distributor can patch in a new magic number.  But if the device makes
> >> > it to the consumer, the result is an unstable device that unpredictably
> >> > suspends.
> >> >
> >> > The above code will also waste power.  Even if userspace takes less than
> >> > (HZ / 2) to react, the system will still be prevented from entering a
> >> > system-wide low power state for the duration of the remaining time.  This
> >> > is in contrast to an approach that uses the idle loop to enter a
> >> > system-wide low power state.  The moment that the system has no further
> >> > work to do, it can start saving power.
> >> >
> >>
> >> Yes, preventing suspend for 1/2 second wastes some power, but are you
> >> seriously suggesting that it wastes more power then never suspending?
> >> Opportunitic suspend does not prevent entering low power modes from
> >> idle.
> >
> > Sorry, my E-mail was unclear.  Here is my intended point:
> >
> > The current Android opportunistic suspend governor does not enter full
> > system suspend until all suspend-blocks are released.  If the governor
> > were modified to respect the scheduler, then it could enter suspend the
> > moment that the system had no further work to do.
> >
> 
> On the hardware that shipped we enter the same power state from idle
> and suspend, so the only power savings we get from suspend that we
> don't get in idle is from not respecting the scheduler and timers.

Other people 
say that their hardware XXX.  (This is probably likely for anything that 
supports ACPI.)

> > So during part of the (HZ / 2) time interval in the above code, the system
> > is running some kernel or userspace code.  But after that work is done, an
> > idle-loop-based opportunistic suspend governor could enter idle during
> > the remaining portion of that (HZ / 2) time interval, while the current
> > governor must keep the system out of suspend.
> >
> Triggering a full suspend (that disregards normal timers) from idle
> does not work, since the system may temporarily enter idle while
> processing the event.

Could you provide an example?

In such a case, how can the kernel guarantee that the system will process 
the event within (HZ / 2) seconds?

> > Of course, to do this, a different method for freezing processes would be
> > needed; one possible approach is described here[1]; others have proposed
> > similar approaches.
> 
> If you freeze processes you need something like a suspend blocker to
> prevent processes from being frozen. 

(It occurs to me that "frozen" is the wrong terminology to use.
 Perhaps "paused" would be a better term, along the lines of [1].)

The full set of processes does not need to be paused.  For example, if 
"untrusted" processes are run in a cgroup, and only tasks in that cgroup 
are put into TASK_INTERRUPTIBLE, then "trusted" processes will not be 
paused.

> By having a process that never gets frozen you could implement this 
> entirely in user space, but you would have to funnel all wakeup events 
> though this process.

The proposal[2] does require a kernel change.  No wakeup funnel process 
would be needed.  The proposal is to put processes into 
TASK_INTERRUPTIBLE, not TASK_STOPPED (unlike most "freezers").


- Paul


1. Paragraph B4B of Paul Walmsley E-mail to the linux-pm mailing 
   list, dated Mon May 17 18:39:44 PDT 2010:
   https://lists.linux-foundation.org/pipermail/linux-pm/2010-May/025663.html

2. ibid.


[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-18  2:17       ` Paul Walmsley
@ 2010-05-18  3:06         ` Arve Hjønnevåg
  2010-05-18  3:34           ` Paul Walmsley
  0 siblings, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-18  3:06 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: Jesse Barnes, linux-kernel, linux-pm, Liam Girdwood,
	Matthew Garrett, Len Brown, Jacob Pan, Oleg Nesterov, linux-omap,
	Linus Walleij, Daniel Walker, Theodore Ts'o, Brian Swetland,
	Mark Brown, Geoff Smith, Tejun Heo, Andrew Morton, Wu Fengguang,
	Arjan van de Ven

On Mon, May 17, 2010 at 7:17 PM, Paul Walmsley <paul@pwsan.com> wrote:
> Hello Arve,
>
> On Fri, 14 May 2010, Arve Hjønnevåg wrote:
>
>> On Thu, May 13, 2010 at 11:27 PM, Paul Walmsley <paul@pwsan.com> wrote:
>> >
>> > On Thu, 13 May 2010, Arve Hjønnevåg wrote:
>> >
>> >> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
>> >> After setting the policy to opportunistic, writes to /sys/power/state
>> >> become non-blocking requests that specify which suspend state to enter
>> >> when no suspend blockers are active. A special state, "on", stops the
>> >> process by activating the "main" suspend blocker.
>> >>
>> >> Signed-off-by: Arve Hjønnevåg <arve@android.com>
>> >
>> > Looking at the way that suspend-blocks are used in the current Android
>> > 'msm' kernel tree[1], they seem likely to cause layering violations,
>> > fragile kernel code with userspace dependencies, and code that consumes
>> > power unnecessarily.
>> >
>> > For example, in drivers/mmc/core/core.c:721, in mmc_rescan() [2], we find
>> > the following code:
>> >
>> >        /* give userspace some time to react */
>> >        wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
>> >
>> > This is a layering violation.  The MMC subsystem should have nothing to do
>> > with "giving userspace time to react."  That is the responsibility of the
>> > Linux scheduler.
>> >
>> > This code is also intrinsically fragile and use-case dependent.  What if
>> > userspace occasionally needs more than (HZ / 2) to react?  If the
>> > distributor is lucky enough to catch this before the product ships, then
>> > the distributor can patch in a new magic number.  But if the device makes
>> > it to the consumer, the result is an unstable device that unpredictably
>> > suspends.
>> >
>> > The above code will also waste power.  Even if userspace takes less than
>> > (HZ / 2) to react, the system will still be prevented from entering a
>> > system-wide low power state for the duration of the remaining time.  This
>> > is in contrast to an approach that uses the idle loop to enter a
>> > system-wide low power state.  The moment that the system has no further
>> > work to do, it can start saving power.
>> >
>>
>> Yes, preventing suspend for 1/2 second wastes some power, but are you
>> seriously suggesting that it wastes more power then never suspending?
>> Opportunitic suspend does not prevent entering low power modes from
>> idle.
>
> Sorry, my E-mail was unclear.  Here is my intended point:
>
> The current Android opportunistic suspend governor does not enter full
> system suspend until all suspend-blocks are released.  If the governor
> were modified to respect the scheduler, then it could enter suspend the
> moment that the system had no further work to do.
>

On the hardware that shipped we enter the same power state from idle
and suspend, so the only power savings we get from suspend that we
don't get in idle is from not respecting the scheduler and timers.

> So during part of the (HZ / 2) time interval in the above code, the system
> is running some kernel or userspace code.  But after that work is done, an
> idle-loop-based opportunistic suspend governor could enter idle during
> the remaining portion of that (HZ / 2) time interval, while the current
> governor must keep the system out of suspend.
>
Triggering a full suspend (that disregards normal timers) from idle
does not work, since the system may temporarily enter idle while
processing the event.

> Of course, to do this, a different method for freezing processes would be
> needed; one possible approach is described here[1]; others have proposed
> similar approaches.
>

If you freeze processes you need something like a suspend blocker to
prevent processes from being frozen. By having a process that never
gets frozen you could implement this entirely in user space, but you
would have to funnel all wakeup events though this process.

>
> regards,
>
> - Paul
>
>
> 1. Paragraphs B4A and B4B of Paul Walmsley E-mail to the linux-pm mailing
>   list, dated Mon May 17 18:39:44 PDT 2010:
>   https://lists.linux-foundation.org/pipermail/linux-pm/2010-May/025663.html

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-14  7:14     ` Arve Hjønnevåg
@ 2010-05-18  2:17       ` Paul Walmsley
  2010-05-18  3:06         ` Arve Hjønnevåg
  0 siblings, 1 reply; 195+ messages in thread
From: Paul Walmsley @ 2010-05-18  2:17 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Jesse Barnes, linux-kernel, linux-pm, Liam Girdwood,
	Matthew Garrett, Len Brown, Jacob Pan, Oleg Nesterov, linux-omap,
	Linus Walleij, Daniel Walker, Theodore Ts'o, Brian Swetland,
	Mark Brown, Geoff Smith, Tejun Heo, Andrew Morton, Wu Fengguang,
	Arjan van de Ven

[-- Attachment #1: Type: TEXT/PLAIN, Size: 3446 bytes --]

Hello Arve,

On Fri, 14 May 2010, Arve Hjønnevåg wrote:

> On Thu, May 13, 2010 at 11:27 PM, Paul Walmsley <paul@pwsan.com> wrote:
> >
> > On Thu, 13 May 2010, Arve Hjønnevåg wrote:
> >
> >> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> >> After setting the policy to opportunistic, writes to /sys/power/state
> >> become non-blocking requests that specify which suspend state to enter
> >> when no suspend blockers are active. A special state, "on", stops the
> >> process by activating the "main" suspend blocker.
> >>
> >> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> >
> > Looking at the way that suspend-blocks are used in the current Android
> > 'msm' kernel tree[1], they seem likely to cause layering violations,
> > fragile kernel code with userspace dependencies, and code that consumes
> > power unnecessarily.
> >
> > For example, in drivers/mmc/core/core.c:721, in mmc_rescan() [2], we find
> > the following code:
> >
> >        /* give userspace some time to react */
> >        wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
> >
> > This is a layering violation.  The MMC subsystem should have nothing to do
> > with "giving userspace time to react."  That is the responsibility of the
> > Linux scheduler.
> >
> > This code is also intrinsically fragile and use-case dependent.  What if
> > userspace occasionally needs more than (HZ / 2) to react?  If the
> > distributor is lucky enough to catch this before the product ships, then
> > the distributor can patch in a new magic number.  But if the device makes
> > it to the consumer, the result is an unstable device that unpredictably
> > suspends.
> >
> > The above code will also waste power.  Even if userspace takes less than
> > (HZ / 2) to react, the system will still be prevented from entering a
> > system-wide low power state for the duration of the remaining time.  This
> > is in contrast to an approach that uses the idle loop to enter a
> > system-wide low power state.  The moment that the system has no further
> > work to do, it can start saving power.
> >
> 
> Yes, preventing suspend for 1/2 second wastes some power, but are you
> seriously suggesting that it wastes more power then never suspending?
> Opportunitic suspend does not prevent entering low power modes from
> idle.

Sorry, my E-mail was unclear.  Here is my intended point:

The current Android opportunistic suspend governor does not enter full 
system suspend until all suspend-blocks are released.  If the governor 
were modified to respect the scheduler, then it could enter suspend the 
moment that the system had no further work to do.

So during part of the (HZ / 2) time interval in the above code, the system 
is running some kernel or userspace code.  But after that work is done, an 
idle-loop-based opportunistic suspend governor could enter idle during 
the remaining portion of that (HZ / 2) time interval, while the current 
governor must keep the system out of suspend.

Of course, to do this, a different method for freezing processes would be 
needed; one possible approach is described here[1]; others have proposed 
similar approaches.


regards,

- Paul


1. Paragraphs B4A and B4B of Paul Walmsley E-mail to the linux-pm mailing 
   list, dated Mon May 17 18:39:44 PDT 2010:
   https://lists.linux-foundation.org/pipermail/linux-pm/2010-May/025663.html

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-13 19:01   ` Paul Walmsley
@ 2010-05-14 20:05     ` Paul Walmsley
  0 siblings, 0 replies; 195+ messages in thread
From: Paul Walmsley @ 2010-05-14 20:05 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Greg Kroah-Hartman, linux-doc, Jesse Barnes, linux-kernel,
	linux-pm, Liam Girdwood, Matthew Garrett, Len Brown, Jacob Pan,
	Linus Walleij, linux-omap, Arjan van de Ven, Daniel Walker,
	Theodore Ts'o, Geoff Smith, Brian Swetland, Mark Brown,
	Oleg Nesterov, Tejun Heo, Andrew Morton, Wu Fengguang

On Thu, 13 May 2010, Paul Walmsley wrote:

> Some comments on the opportunistic suspend portion of this patch.

...

> While reading the patch, it seemed that this opportunistic suspend
> mechanism is a degenerate case of a system-wide form of CPUIdle.  I'll
> call it "SystemIdle," for lack of a better term.

The footnotes for this E-mail were inadvertently omitted.  Sorry about 
that.  They are:


1. http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=arch/arm/mach-omap2/cpuidle34xx.c;h=3d3d035db9aff62ce522e0080e570cfdbf8e70cc;hb=4462dc02842698f173f518c1f5ce79c0fb89395a#l292

2. OMAP35x Technical Reference Manual Rev. F, section 4.7.5.2 "System
   Clock Oscillator Control": http://www.ti.com/litv/pdf/spruf98f

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-14  6:27   ` Paul Walmsley
@ 2010-05-14  7:14     ` Arve Hjønnevåg
  2010-05-18  2:17       ` Paul Walmsley
  0 siblings, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-14  7:14 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: Jesse Barnes, linux-kernel, linux-pm, Liam Girdwood,
	Matthew Garrett, Len Brown, Jacob Pan, Oleg Nesterov, linux-omap,
	Linus Walleij, Daniel Walker, Theodore Ts'o, Brian Swetland,
	Mark Brown, Geoff Smith, Tejun Heo, Andrew Morton, Wu Fengguang,
	Arjan van de Ven

On Thu, May 13, 2010 at 11:27 PM, Paul Walmsley <paul@pwsan.com> wrote:
>
> Hello,
>
> Another comment on the kernel suspend-blocker API.
>
> On Thu, 13 May 2010, Arve Hjønnevåg wrote:
>
>> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
>> After setting the policy to opportunistic, writes to /sys/power/state
>> become non-blocking requests that specify which suspend state to enter
>> when no suspend blockers are active. A special state, "on", stops the
>> process by activating the "main" suspend blocker.
>>
>> Signed-off-by: Arve Hjønnevåg <arve@android.com>
>
> Looking at the way that suspend-blocks are used in the current Android
> 'msm' kernel tree[1], they seem likely to cause layering violations,
> fragile kernel code with userspace dependencies, and code that consumes
> power unnecessarily.
>
> For example, in drivers/mmc/core/core.c:721, in mmc_rescan() [2], we find
> the following code:
>
>        /* give userspace some time to react */
>        wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
>
> This is a layering violation.  The MMC subsystem should have nothing to do
> with "giving userspace time to react."  That is the responsibility of the
> Linux scheduler.
>
> This code is also intrinsically fragile and use-case dependent.  What if
> userspace occasionally needs more than (HZ / 2) to react?  If the
> distributor is lucky enough to catch this before the product ships, then
> the distributor can patch in a new magic number.  But if the device makes
> it to the consumer, the result is an unstable device that unpredictably
> suspends.
>
> The above code will also waste power.  Even if userspace takes less than
> (HZ / 2) to react, the system will still be prevented from entering a
> system-wide low power state for the duration of the remaining time.  This
> is in contrast to an approach that uses the idle loop to enter a
> system-wide low power state.  The moment that the system has no further
> work to do, it can start saving power.
>

Yes, preventing suspend for 1/2 second wastes some power, but are you
seriously suggesting that it wastes more power then never suspending?
Opportunitic suspend does not prevent entering low power modes from
idle.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-14  4:11   ` Arve Hjønnevåg
  (?)
  (?)
@ 2010-05-14  6:27   ` Paul Walmsley
  2010-05-14  7:14     ` Arve Hjønnevåg
  -1 siblings, 1 reply; 195+ messages in thread
From: Paul Walmsley @ 2010-05-14  6:27 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Jesse Barnes, linux-kernel, linux-pm, Liam Girdwood,
	Matthew Garrett, Len Brown, Jacob Pan, Oleg Nesterov, linux-omap,
	Linus Walleij, Daniel Walker, Theodore Ts'o, Brian Swetland,
	Mark Brown, Geoff Smith, Tejun Heo, Andrew Morton, Wu Fengguang,
	Arjan van de Ven

[-- Attachment #1: Type: TEXT/PLAIN, Size: 3860 bytes --]


Hello,

Another comment on the kernel suspend-blocker API.

On Thu, 13 May 2010, Arve Hjønnevåg wrote:

> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.
> 
> Signed-off-by: Arve Hjønnevåg <arve@android.com>

Looking at the way that suspend-blocks are used in the current Android 
'msm' kernel tree[1], they seem likely to cause layering violations, 
fragile kernel code with userspace dependencies, and code that consumes 
power unnecessarily.

For example, in drivers/mmc/core/core.c:721, in mmc_rescan() [2], we find 
the following code:

	/* give userspace some time to react */
	wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);

This is a layering violation.  The MMC subsystem should have nothing to do 
with "giving userspace time to react."  That is the responsibility of the 
Linux scheduler.

This code is also intrinsically fragile and use-case dependent.  What if 
userspace occasionally needs more than (HZ / 2) to react?  If the 
distributor is lucky enough to catch this before the product ships, then 
the distributor can patch in a new magic number.  But if the device makes 
it to the consumer, the result is an unstable device that unpredictably 
suspends.

The above code will also waste power.  Even if userspace takes less than 
(HZ / 2) to react, the system will still be prevented from entering a 
system-wide low power state for the duration of the remaining time.  This 
is in contrast to an approach that uses the idle loop to enter a 
system-wide low power state.  The moment that the system has no further 
work to do, it can start saving power.

The problems with the above example are not isolated:

[paul@twilight msm]$ fgrep -r wake_lock_timeout . | egrep '(^./drivers|^./arch)'
./arch/arm/mach-msm/htc_battery.c:		wake_lock_timeout(&vbus_wake_lock, HZ / 2);
./arch/arm/mach-msm/smd_tty.c:		wake_lock_timeout(&info->wake_lock, HZ / 2);
./arch/arm/mach-msm/smd_qmi.c:		wake_lock_timeout(&ctxt->wake_lock, HZ / 2);
./drivers/rtc/alarm.c:		wake_lock_timeout(&alarm_wake_lock, 5 * HZ);
./drivers/rtc/alarm.c:	wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ);
./drivers/rtc/alarm.c:			wake_lock_timeout(&alarm_rtc_wake_lock, 2 * HZ);
./drivers/mmc/core/core.c:	wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
./drivers/net/msm_rmnet.c:	wake_lock_timeout(&p->wake_lock, HZ / 2);
./drivers/video/msm/msm_fb.c:	wake_lock_timeout(&msmfb->idle_lock, HZ/4);
./drivers/video/msm/msm_fb.c:		wake_lock_timeout(&msmfb->idle_lock, HZ/4);
./drivers/video/msm/msm_fb.c:		wake_lock_timeout(&msmfb->idle_lock, HZ);
./drivers/input/evdev.c:	wake_lock_timeout(&client->wake_lock, 5 * HZ);
./drivers/serial/msm_serial_hs.c:	wake_lock_timeout(&msm_uport->rx.wake_lock, HZ / 2);

It is true that the current patch series does not propose the *_timeout() 
interface.  But that raises the question of what it will be replaced with, 
in situations where the problem can't simply be pushed to userspace, as it 
is in Arve's evdev patch 7.  Some of these cases seem difficult to handle 
without some sort of timer-based approach.  Timer-based approaches are 
intrinsically problematic for the reasons mentioned above.


- Paul


1. git://android.git.kernel.org/kernel/msm.git - as of commit
   c7f8bcecb06b937c45dd0e342450a3218b286b8d, "[ARM] msm: defconfig:
   Set mmap_min_addr to 32k"

2. http://android.git.kernel.org/?p=kernel/msm.git;a=blob;f=drivers/mmc/core/core.c;h=22f6ddad85c92f34c55035982049bb03b5abfc74;hb=c7f8bcecb06b937c45dd0e342450a3218b286b8d#l721



[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-14  4:11   ` Arve Hjønnevåg
  (?)
@ 2010-05-14  6:13   ` Paul Walmsley
  -1 siblings, 0 replies; 195+ messages in thread
From: Paul Walmsley @ 2010-05-14  6:13 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Dmitry Torokhov, Sven Neumann, Jesse Barnes, linux-kernel,
	Tero Saarni, linux-input, linux-pm, Liam Girdwood,
	Alexey Dobriyan, Matthew Garrett, Len Brown, Jacob Pan,
	Daniel Mack, Oleg Nesterov, linux-omap, Linus Walleij,
	Daniel Walker, Theodore Ts'o, Márton Németh,
	Brian Swetland

[-- Attachment #1: Type: TEXT/PLAIN, Size: 5345 bytes --]


Hello,

some comments on the kernel-level suspend blocker API.

On Thu, 13 May 2010, Arve Hjønnevåg wrote:

> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.
> 
> Signed-off-by: Arve Hjønnevåg <arve@android.com>

Another problem with the suspend-block kernel API is that its primary 
use-case[1] is to work around the brokenness of patch 1's opportunistic 
suspend governor.  The governor is broken because it intentionally ignores 
timers and the scheduler.  To work around problems caused by this 
decision, suspend-blocks must be spread throughout the kernel (and 
userspace) to restore the previous behavior of the system.  But if patch 
1's opportunistic suspend governor were fixed, most of the need for a 
suspend blockers would disappear.

[ This E-mail expands on some earlier comments[2] and is also related
  to other opportunistic suspend comments[3]. ]

The suspend-block patches require already-working code to be patched to 
keep it working.  This is a backwards approach.  Instead, broken code 
should be patched to fix the brokenness.  But the former approach is 
exactly what the suspend block API is intended to do.  Patches 7 and 8 in 
this series are two clear cases.  Patch 7 adds an ioctl to the input 
subsystem that will cause the system to stay running as long as there is 
an event in the event queue.  Patch 8 adds a suspend-block call to the 
power_supply code that causes the system to stay running as long as 
power_supply's changed_work workqueue has work to do.  Neither of these 
patches should be needed, since the default expectation of the mainline 
Linux kernel is that the system should run when there is work to be done.

This longstanding expectation should be preserved, not broken and then 
patched around.  But since patch 1 violates that expectation, there is no 
choice but to add patches 7 and 8 -- and ultimately many more patches -- 
to get a working system.  Once the opportunistic suspend governor in patch 
1 is introduced and enabled, the kernel will suspend the system 
immediately, even if the system has work to do.  The only way to prevent 
that is to add suspend-blocks.

Many suspend blocks will be needed throughout the kernel to keep things 
working.  One might think that patches 7 and 8 are the only changes needed 
to get a working Android-style opportunistic suspend system.  It turns out 
that many more changes are needed.  This can be seen by looking at 
currently shipping Android kernels, such as the current current Android 
'msm' kernel tree[4]. Suspend-blocker usage is spread through many drivers 
and subsystems:

[paul@twilight msm]$ fgrep -r wake_lock . | egrep '(^./drivers|^./arch)' | 
cut -f1 -d: | sort -u
./arch/arm/mach-msm/htc_battery.c
./arch/arm/mach-msm/pm.c
./arch/arm/mach-msm/qdsp5/adsp.c
./arch/arm/mach-msm/qdsp5/audio_out.c
./arch/arm/mach-msm/smd_qmi.c
./arch/arm/mach-msm/smd_rpcrouter.c
./arch/arm/mach-msm/smd_rpcrouter.h
./arch/arm/mach-msm/smd_rpcrouter_servers.c
./arch/arm/mach-msm/smd_tty.c
./drivers/i2c/chips/mt9t013.c
./drivers/input/evdev.c
./drivers/input/misc/gpio_input.c
./drivers/input/misc/gpio_matrix.c
./drivers/mmc/core/core.c
./drivers/net/msm_rmnet.c
./drivers/rtc/alarm.c
./drivers/serial/msm_serial_hs.c
./drivers/usb/function/mass_storage.c
./drivers/usb/gadget/f_mass_storage.c
./drivers/video/msm/mddi.c
./drivers/video/msm/msm_fb.c

I guess this is something like what kernel developers should eventually 
expect to see if suspend blockers are merged.

There is a better way to make the system work as expected: these patch 1 
shouldn't break general timer and scheduler assumptions.  If patch 1's 
governor is fixed, most of these calls will be unnecessary.  The apparent 
motivation is to prevent "untrusted" userspace processes from preventing 
the system from entering a low-power state.  I agree that this is a 
problem.  But it should be fixed by modifying the specific Linux code that 
determines when the idle loop is entered and when the next timer wakeup 
should occur, on a per-process basis.  Such targeted changes would ensure 
that the rest of the kernel would continue to execute as expected.

regards,

- Paul


1. Arve Hjønnevåg E-mail to the linux-pm mailing list, dated Mon, 3
   May 2010 17:09:22 -0700:
   http://www.spinics.net/lists/linux-pm/msg18432.html

2. Paul Walmsley E-mail to the linux-pm mailing list, dated Wed, 12
   May 2010 21:35:30 -0600: http://lkml.org/lkml/2010/5/12/528

3. Paul Walmsley E-mail to the linux-pm mailing list, dated Thu, 13
   May 2010 13:01:33 -0600:
   http://permalink.gmane.org/gmane.linux.power-management.general/18592

4. git://android.git.kernel.org/kernel/msm.git - as of commit
   c7f8bcecb06b937c45dd0e342450a3218b286b8d, "[ARM] msm: defconfig:
   Set mmap_min_addr to 32k"

5. http://android.git.kernel.org/?p=kernel/msm.git;a=blob;f=drivers/mmc/core/core.c;h=22f6ddad85c92f34c55035982049bb03b5abfc74;hb=c7f8bcecb06b937c45dd0e342450a3218b286b8d#l721

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 195+ messages in thread

* [PATCH 1/8] PM: Add suspend block api.
  2010-05-14  4:11 [PATCH 0/8] Suspend block api (version 7) Arve Hjønnevåg
@ 2010-05-14  4:11   ` Arve Hjønnevåg
  0 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-14  4:11 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: Rafael J. Wysocki, Arve Hjønnevåg, Len Brown,
	Pavel Machek, Randy Dunlap, Andrew Morton, Andi Kleen,
	Cornelia Huck, Tejun Heo, Jesse Barnes, Magnus Damm,
	Nigel Cunningham, Alan Stern, Ming Lei, Wu Fengguang,
	Maxim Levitsky, linux-doc

Adds /sys/power/policy that selects the behaviour of /sys/power/state.
After setting the policy to opportunistic, writes to /sys/power/state
become non-blocking requests that specify which suspend state to enter
when no suspend blockers are active. A special state, "on", stops the
process by activating the "main" suspend blocker.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/opportunistic-suspend.txt |  129 +++++++++++
 include/linux/suspend.h                       |    1 +
 include/linux/suspend_blocker.h               |   74 +++++++
 kernel/power/Kconfig                          |   16 ++
 kernel/power/Makefile                         |    1 +
 kernel/power/main.c                           |  128 +++++++++++-
 kernel/power/opportunistic_suspend.c          |  284 +++++++++++++++++++++++++
 kernel/power/power.h                          |    9 +
 kernel/power/suspend.c                        |    3 +-
 9 files changed, 637 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/power/opportunistic-suspend.txt
 create mode 100755 include/linux/suspend_blocker.h
 create mode 100644 kernel/power/opportunistic_suspend.c

diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
new file mode 100644
index 0000000..4bee7bc
--- /dev/null
+++ b/Documentation/power/opportunistic-suspend.txt
@@ -0,0 +1,129 @@
+Opportunistic Suspend
+=====================
+
+Opportunistic suspend is a feature allowing the system to be suspended (ie. put
+into one of the available sleep states) automatically whenever it is regarded
+as idle.  The suspend blockers framework described below is used to determine
+when that happens.
+
+The /sys/power/policy sysfs attribute is used to switch the system between the
+opportunistic and "forced" suspend behavior, where in the latter case the
+system is only suspended if a specific value, corresponding to one of the
+available system sleep states, is written into /sys/power/state.  However, in
+the former, opportunistic, case the system is put into the sleep state
+corresponding to the value written to /sys/power/state whenever there are no
+active suspend blockers. The default policy is "forced". Also, suspend blockers
+do not affect sleep states entered from idle.
+
+When the policy is "opportunisic", there is a special value, "on", that can be
+written to /sys/power/state. This will block the automatic sleep request, as if
+a suspend blocker was used by a device driver. This way the opportunistic
+suspend may be blocked by user space whithout switching back to the "forced"
+mode.
+
+A suspend blocker is an object used to inform the PM subsystem when the system
+can or cannot be suspended in the "opportunistic" mode (the "forced" mode
+ignores suspend blockers).  To use it, a device driver creates a struct
+suspend_blocker that must be initialized with suspend_blocker_init(). Before
+freeing the suspend_blocker structure or its name, suspend_blocker_unregister()
+must be called on it.
+
+A suspend blocker is activated using suspend_block(), which prevents the PM
+subsystem from putting the system into the requested sleep state in the
+"opportunistic" mode until the suspend blocker is deactivated with
+suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
+the system will not suspend as long as at least one of them is active.
+
+If opportunistic suspend is already in progress when suspend_block() is called,
+it will abort the suspend, unless suspend_ops->enter has already been
+executed. If suspend is aborted this way, the system is usually not fully
+operational at that point. The suspend callbacks of some drivers may still be
+running and it usually takes time to restore the system to the fully operational
+state.
+
+Here's an example showing how a cell phone or other embedded system can handle
+keystrokes (or other input events) in the presence of suspend blockers. Use
+set_irq_wake or a platform specific API to make sure the keypad interrupt wakes
+up the cpu. Once the keypad driver has resumed, the sequence of events can look
+like this:
+
+- The Keypad driver gets an interrupt. It then calls suspend_block on the
+  keypad-scan suspend_blocker and starts scanning the keypad matrix.
+- The keypad-scan code detects a key change and reports it to the input-event
+  driver.
+- The input-event driver sees the key change, enqueues an event, and calls
+  suspend_block on the input-event-queue suspend_blocker.
+- The keypad-scan code detects that no keys are held and calls suspend_unblock
+  on the keypad-scan suspend_blocker.
+- The user-space input-event thread returns from select/poll, calls
+  suspend_block on the process-input-events suspend_blocker and then calls read
+  on the input-event device.
+- The input-event driver dequeues the key-event and, since the queue is now
+  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
+- The user-space input-event thread returns from read. If it determines that
+  the key should be ignored, it calls suspend_unblock on the
+  process_input_events suspend_blocker and then calls select or poll. The
+  system will automatically suspend again, since now no suspend blockers are
+  active.
+
+If the key that was pressed instead should preform a simple action (for example,
+adjusting the volume), this action can be performed right before calling
+suspend_unblock on the process_input_events suspend_blocker. However, if the key
+triggers a longer-running action, that action needs its own suspend_blocker and
+suspend_block must be called on that suspend blocker before calling
+suspend_unblock on the process_input_events suspend_blocker.
+
+                 Key pressed   Key released
+                     |             |
+keypad-scan          ++++++++++++++++++
+input-event-queue        +++          +++
+process-input-events       +++          +++
+
+
+Driver API
+==========
+
+A driver can use the suspend block API by adding a suspend_blocker variable to
+its state and calling suspend_blocker_init(). For instance:
+
+struct state {
+	struct suspend_blocker suspend_blocker;
+}
+
+init() {
+	suspend_blocker_init(&state->suspend_blocker, name);
+}
+
+If the suspend_blocker variable is allocated statically,
+DEFINE_SUSPEND_BLOCKER() should be used to initialize it, for example:
+
+static DEFINE_SUSPEND_BLOCKER(blocker, name);
+
+and suspend_blocker_register(&blocker) has to be called to make the suspend
+blocker usable.
+
+Before freeing the memory in which a suspend_blocker variable is located,
+suspend_blocker_unregister() must be called, for instance:
+
+uninit() {
+	suspend_blocker_unregister(&state->suspend_blocker);
+}
+
+When the driver determines that it needs to run (usually in an interrupt
+handler) it calls suspend_block():
+
+	suspend_block(&state->suspend_blocker);
+
+When it no longer needs to run it calls suspend_unblock():
+
+	suspend_unblock(&state->suspend_blocker);
+
+Calling suspend_block() when the suspend blocker is active or suspend_unblock()
+when it is not active has no effect (i.e., these functions don't nest). This
+allows drivers to update their state and call suspend suspend_block() or
+suspend_unblock() based on the result. For instance:
+
+if (list_empty(&state->pending_work))
+	suspend_unblock(&state->suspend_blocker);
+else
+	suspend_block(&state->suspend_blocker);
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 5e781d8..07023d3 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -6,6 +6,7 @@
 #include <linux/init.h>
 #include <linux/pm.h>
 #include <linux/mm.h>
+#include <linux/suspend_blocker.h>
 #include <asm/errno.h>
 
 #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_VT) && defined(CONFIG_VT_CONSOLE)
diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
new file mode 100755
index 0000000..8788302
--- /dev/null
+++ b/include/linux/suspend_blocker.h
@@ -0,0 +1,74 @@
+/* include/linux/suspend_blocker.h
+ *
+ * Copyright (C) 2007-2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SUSPEND_BLOCKER_H
+#define _LINUX_SUSPEND_BLOCKER_H
+
+#include <linux/list.h>
+
+/**
+ * struct suspend_blocker - the basic suspend_blocker structure
+ * @link: List entry for active or inactive list.
+ * @flags: Tracks initialized and active state.
+ * @name: Suspend blocker name used for debugging.
+ *
+ * When a suspend_blocker is active it prevents the system from entering
+ * opportunistic suspend.
+ *
+ * The suspend_blocker structure must be initialized by suspend_blocker_init()
+ */
+struct suspend_blocker {
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	struct list_head link;
+	int flags;
+	const char *name;
+#endif
+};
+
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+#define __SUSPEND_BLOCKER_INITIALIZER(blocker_name) \
+	{ .name = #blocker_name, }
+
+#define DEFINE_SUSPEND_BLOCKER(blocker, name) \
+	struct suspend_blocker blocker = __SUSPEND_BLOCKER_INITIALIZER(name)
+
+extern void suspend_blocker_register(struct suspend_blocker *blocker);
+extern void suspend_blocker_init(struct suspend_blocker *blocker,
+				 const char *name);
+extern void suspend_blocker_unregister(struct suspend_blocker *blocker);
+extern void suspend_block(struct suspend_blocker *blocker);
+extern void suspend_unblock(struct suspend_blocker *blocker);
+extern bool suspend_blocker_is_active(struct suspend_blocker *blocker);
+extern bool suspend_is_blocked(void);
+
+#else
+
+#define DEFINE_SUSPEND_BLOCKER(blocker, name) \
+	struct suspend_blocker blocker
+
+static inline void suspend_blocker_register(struct suspend_blocker *bl) {}
+static inline void suspend_blocker_init(struct suspend_blocker *bl,
+					const char *n) {}
+static inline void suspend_blocker_unregister(struct suspend_blocker *bl) {}
+static inline void suspend_block(struct suspend_blocker *bl) {}
+static inline void suspend_unblock(struct suspend_blocker *bl) {}
+static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
+{
+	return false;
+}
+static inline bool suspend_is_blocked(void) { return false; }
+#endif
+
+#endif
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 5c36ea9..6d11a45 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -130,6 +130,22 @@ config SUSPEND_FREEZER
 
 	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
 
+config OPPORTUNISTIC_SUSPEND
+	bool "Opportunistic suspend"
+	depends on SUSPEND
+	select RTC_LIB
+	default n
+	---help---
+	  Opportunistic sleep support.  Allows the system to be put into a sleep
+	  state opportunistically, if it doesn't do any useful work at the
+	  moment. The PM subsystem is switched into this mode of operation by
+	  writing "opportunistic" into /sys/power/policy, while writing
+	  "forced" to this file turns the opportunistic suspend feature off.
+	  In the "opportunistic" mode suspend blockers are used to determine
+	  when to suspend the system and the value written to /sys/power/state
+	  determines the sleep state the system will be put into when there are
+	  no active suspend blockers.
+
 config HIBERNATION_NVS
 	bool
 
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 4319181..95d8e6d 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
+obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= opportunistic_suspend.o
 obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
 obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
 obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
diff --git a/kernel/power/main.c b/kernel/power/main.c
index b58800b..afbb4dd 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -20,6 +20,58 @@ DEFINE_MUTEX(pm_mutex);
 unsigned int pm_flags;
 EXPORT_SYMBOL(pm_flags);
 
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+struct pm_policy {
+	const char *name;
+	bool (*valid_state)(suspend_state_t state);
+	int (*set_state)(suspend_state_t state);
+};
+
+static struct pm_policy policies[] = {
+	{
+		.name		= "forced",
+		.valid_state	= valid_state,
+		.set_state	= enter_state,
+	},
+	{
+		.name		= "opportunistic",
+		.valid_state	= opportunistic_suspend_valid_state,
+		.set_state	= opportunistic_suspend_state,
+	},
+};
+
+static int policy;
+
+static inline bool hibernation_supported(void)
+{
+	return !strncmp(policies[policy].name, "forced", 6);
+}
+
+static inline bool pm_state_valid(int state_idx)
+{
+	return pm_states[state_idx] && policies[policy].valid_state(state_idx);
+}
+
+static inline int pm_enter_state(int state_idx)
+{
+	return policies[policy].set_state(state_idx);
+}
+
+#else
+
+static inline bool hibernation_supported(void) { return true; }
+
+static inline bool pm_state_valid(int state_idx)
+{
+	return pm_states[state_idx] && valid_state(state_idx);
+}
+
+static inline int pm_enter_state(int state_idx)
+{
+	return enter_state(state_idx);
+}
+#endif /* CONFIG_OPPORTUNISTIC_SUSPEND */
+
 #ifdef CONFIG_PM_SLEEP
 
 /* Routines for PM-transition notifications */
@@ -146,6 +198,12 @@ struct kobject *power_kobj;
  *
  *	store() accepts one of those strings, translates it into the 
  *	proper enumerated value, and initiates a suspend transition.
+ *
+ *	If policy is set to opportunistic, store() does not block until the
+ *	system resumes, and it will try to re-enter the state until another
+ *	state is requested. Suspend blockers are respected and the requested
+ *	state will only be entered when no suspend blockers are active.
+ *	Write "on" to disable.
  */
 static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 			  char *buf)
@@ -155,12 +213,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 	int i;
 
 	for (i = 0; i < PM_SUSPEND_MAX; i++) {
-		if (pm_states[i] && valid_state(i))
+		if (pm_state_valid(i))
 			s += sprintf(s,"%s ", pm_states[i]);
 	}
 #endif
 #ifdef CONFIG_HIBERNATION
-	s += sprintf(s, "%s\n", "disk");
+	if (hibernation_supported())
+		s += sprintf(s, "%s\n", "disk");
 #else
 	if (s != buf)
 		/* convert the last space to a newline */
@@ -173,7 +232,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			   const char *buf, size_t n)
 {
 #ifdef CONFIG_SUSPEND
-	suspend_state_t state = PM_SUSPEND_STANDBY;
+	suspend_state_t state = PM_SUSPEND_ON;
 	const char * const *s;
 #endif
 	char *p;
@@ -185,8 +244,9 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 
 	/* First, check if we are requested to hibernate */
 	if (len == 4 && !strncmp(buf, "disk", len)) {
-		error = hibernate();
-  goto Exit;
+		if (hibernation_supported())
+			error = hibernate();
+		goto Exit;
 	}
 
 #ifdef CONFIG_SUSPEND
@@ -195,7 +255,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			break;
 	}
 	if (state < PM_SUSPEND_MAX && *s)
-		error = enter_state(state);
+		error = pm_enter_state(state);
 #endif
 
  Exit:
@@ -204,6 +264,56 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 
 power_attr(state);
 
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+/**
+ *	policy - set policy for state
+ */
+static ssize_t policy_show(struct kobject *kobj,
+			   struct kobj_attribute *attr, char *buf)
+{
+	char *s = buf;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		if (i == policy)
+			s += sprintf(s, "[%s] ", policies[i].name);
+		else
+			s += sprintf(s, "%s ", policies[i].name);
+	}
+	if (s != buf)
+		/* convert the last space to a newline */
+		*(s-1) = '\n';
+	return (s - buf);
+}
+
+static ssize_t policy_store(struct kobject *kobj,
+			    struct kobj_attribute *attr,
+			    const char *buf, size_t n)
+{
+	const char *s;
+	char *p;
+	int len;
+	int i;
+
+	p = memchr(buf, '\n', n);
+	len = p ? p - buf : n;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		s = policies[i].name;
+		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
+			mutex_lock(&pm_mutex);
+			policies[policy].set_state(PM_SUSPEND_ON);
+			policy = i;
+			mutex_unlock(&pm_mutex);
+			return n;
+		}
+	}
+	return -EINVAL;
+}
+
+power_attr(policy);
+#endif /* CONFIG_OPPORTUNISTIC_SUSPEND */
+
 #ifdef CONFIG_PM_TRACE
 int pm_trace_enabled;
 
@@ -236,6 +346,9 @@ static struct attribute * g[] = {
 #endif
 #ifdef CONFIG_PM_SLEEP
 	&pm_async_attr.attr,
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	&policy_attr.attr,
+#endif
 #ifdef CONFIG_PM_DEBUG
 	&pm_test_attr.attr,
 #endif
@@ -247,7 +360,7 @@ static struct attribute_group attr_group = {
 	.attrs = g,
 };
 
-#ifdef CONFIG_PM_RUNTIME
+#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_OPPORTUNISTIC_SUSPEND)
 struct workqueue_struct *pm_wq;
 EXPORT_SYMBOL_GPL(pm_wq);
 
@@ -266,6 +379,7 @@ static int __init pm_init(void)
 	int error = pm_start_workqueue();
 	if (error)
 		return error;
+	opportunistic_suspend_init();
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
diff --git a/kernel/power/opportunistic_suspend.c b/kernel/power/opportunistic_suspend.c
new file mode 100644
index 0000000..acc1651
--- /dev/null
+++ b/kernel/power/opportunistic_suspend.c
@@ -0,0 +1,284 @@
+/*
+ * kernel/power/opportunistic_suspend.c
+ *
+ * Copyright (C) 2005-2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/suspend.h>
+
+#include "power.h"
+
+extern struct workqueue_struct *pm_wq;
+
+enum {
+	DEBUG_EXIT_SUSPEND = 1U << 0,
+	DEBUG_WAKEUP = 1U << 1,
+	DEBUG_USER_STATE = 1U << 2,
+	DEBUG_SUSPEND = 1U << 3,
+	DEBUG_SUSPEND_BLOCKER = 1U << 4,
+};
+static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SB_INITIALIZED            (1U << 8)
+#define SB_ACTIVE                 (1U << 9)
+
+DEFINE_SUSPEND_BLOCKER(main_suspend_blocker, main);
+
+static DEFINE_SPINLOCK(list_lock);
+static DEFINE_SPINLOCK(state_lock);
+static LIST_HEAD(inactive_blockers);
+static LIST_HEAD(active_blockers);
+static int current_event_num;
+static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
+static bool enable_suspend_blockers;
+
+#define pr_info_time(fmt, args...) \
+	do { \
+		struct timespec ts; \
+		struct rtc_time tm; \
+		getnstimeofday(&ts); \
+		rtc_time_to_tm(ts.tv_sec, &tm); \
+		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
+			args, \
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
+			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
+	} while (0);
+
+static void print_active_suspend_blockers(void)
+{
+	struct suspend_blocker *blocker;
+
+	list_for_each_entry(blocker, &active_blockers, link)
+		pr_info("PM: Active suspend blocker %s\n", blocker->name);
+}
+
+/**
+ * suspend_is_blocked - Check if there are active suspend blockers.
+ *
+ * Return true if suspend blockers are enabled and there are active suspend
+ * blockers, in which case the system cannot be put to sleep opportunistically.
+ */
+bool suspend_is_blocked(void)
+{
+	return enable_suspend_blockers && !list_empty(&active_blockers);
+}
+
+static void suspend_worker(struct work_struct *work)
+{
+	int ret;
+	int entry_event_num;
+
+	enable_suspend_blockers = true;
+
+	if (suspend_is_blocked()) {
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("PM: Automatic suspend aborted\n");
+		goto abort;
+	}
+
+	entry_event_num = current_event_num;
+
+	if (debug_mask & DEBUG_SUSPEND)
+		pr_info("PM: Automatic suspend\n");
+
+	ret = pm_suspend(requested_suspend_state);
+
+	if (debug_mask & DEBUG_EXIT_SUSPEND)
+		pr_info_time("PM: Automatic suspend exit, ret = %d ", ret);
+
+	if (current_event_num == entry_event_num) {
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("PM: pm_suspend() returned with no event\n");
+		queue_work(pm_wq, work);
+	}
+
+abort:
+	enable_suspend_blockers = false;
+}
+static DECLARE_WORK(suspend_work, suspend_worker);
+
+/**
+ * suspend_blocker_register - Prepare a suspend blocker for being used.
+ * @blocker: Suspend blocker to handle.
+ *
+ * The suspend blocker struct and name must not be freed before calling
+ * suspend_blocker_unregister().
+ */
+void suspend_blocker_register(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags = 0;
+
+	WARN_ON(!blocker->name);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("%s: Registering %s\n", __func__, blocker->name);
+
+	blocker->flags = SB_INITIALIZED;
+	INIT_LIST_HEAD(&blocker->link);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	list_add(&blocker->link, &inactive_blockers);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_blocker_register);
+
+/**
+ * suspend_blocker_init - Initialize a suspend blocker's name and register it.
+ * @blocker: Suspend blocker to initialize.
+ * @name:    The name of the suspend blocker to show in debug messages.
+ *
+ * The suspend blocker struct and name must not be freed before calling
+ * suspend_blocker_unregister().
+ */
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
+{
+	blocker->name = name;
+	suspend_blocker_register(blocker);
+}
+EXPORT_SYMBOL(suspend_blocker_init);
+
+/**
+ * suspend_blocker_unregister - Unregister a suspend blocker.
+ * @blocker: Suspend blocker to handle.
+ */
+void suspend_blocker_unregister(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	blocker->flags &= ~SB_INITIALIZED;
+	list_del(&blocker->link);
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(pm_wq, &suspend_work);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("%s: Unregistered %s\n", __func__, blocker->name);
+}
+EXPORT_SYMBOL(suspend_blocker_unregister);
+
+/**
+ * suspend_block - Block system suspend.
+ * @blocker: Suspend blocker to use.
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_block(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("%s: %s\n", __func__, blocker->name);
+
+	blocker->flags |= SB_ACTIVE;
+	list_move(&blocker->link, &active_blockers);
+
+	current_event_num++;
+
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_block);
+
+/**
+ * suspend_unblock - Allow system suspend to happen.
+ * @blocker: Suspend blocker to unblock.
+ *
+ * If no other suspend blockers are active, schedule suspend of the system.
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_unblock(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("%s: %s\n", __func__, blocker->name);
+
+	list_move(&blocker->link, &inactive_blockers);
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(pm_wq, &suspend_work);
+	blocker->flags &= ~(SB_ACTIVE);
+
+	if ((debug_mask & DEBUG_SUSPEND) && blocker == &main_suspend_blocker)
+		print_active_suspend_blockers();
+
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_unblock);
+
+/**
+ * suspend_blocker_is_active - Test if a suspend blocker is blocking suspend
+ * @blocker: Suspend blocker to check.
+ *
+ * Returns true if the suspend_blocker is currently active.
+ */
+bool suspend_blocker_is_active(struct suspend_blocker *blocker)
+{
+	WARN_ON(!(blocker->flags & SB_INITIALIZED));
+
+	return !!(blocker->flags & SB_ACTIVE);
+}
+EXPORT_SYMBOL(suspend_blocker_is_active);
+
+bool opportunistic_suspend_valid_state(suspend_state_t state)
+{
+	return (state == PM_SUSPEND_ON) || valid_state(state);
+}
+
+int opportunistic_suspend_state(suspend_state_t state)
+{
+	unsigned long irqflags;
+
+	if (!opportunistic_suspend_valid_state(state))
+		return -ENODEV;
+
+	spin_lock_irqsave(&state_lock, irqflags);
+
+	if (debug_mask & DEBUG_USER_STATE)
+		pr_info_time("%s: %s (%d->%d) at %lld ", __func__,
+			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
+			     requested_suspend_state, state,
+			     ktime_to_ns(ktime_get()));
+
+	requested_suspend_state = state;
+	if (state == PM_SUSPEND_ON)
+		suspend_block(&main_suspend_blocker);
+	else
+		suspend_unblock(&main_suspend_blocker);
+
+	spin_unlock_irqrestore(&state_lock, irqflags);
+
+	return 0;
+}
+
+void __init opportunistic_suspend_init(void)
+{
+	suspend_blocker_register(&main_suspend_blocker);
+	suspend_block(&main_suspend_blocker);
+}
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 46c5a26..2e9cfd5 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -236,3 +236,12 @@ static inline void suspend_thaw_processes(void)
 {
 }
 #endif
+
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+/* kernel/power/opportunistic_suspend.c */
+extern int opportunistic_suspend_state(suspend_state_t state);
+extern bool opportunistic_suspend_valid_state(suspend_state_t state);
+extern void __init opportunistic_suspend_init(void);
+#else
+static inline void opportunistic_suspend_init(void) {}
+#endif
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 56e7dbb..9eb3876 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -20,6 +20,7 @@
 #include "power.h"
 
 const char *const pm_states[PM_SUSPEND_MAX] = {
+	[PM_SUSPEND_ON]		= "on",
 	[PM_SUSPEND_STANDBY]	= "standby",
 	[PM_SUSPEND_MEM]	= "mem",
 };
@@ -157,7 +158,7 @@ static int suspend_enter(suspend_state_t state)
 
 	error = sysdev_suspend(PMSG_SUSPEND);
 	if (!error) {
-		if (!suspend_test(TEST_CORE))
+		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
 			error = suspend_ops->enter(state);
 		sysdev_resume();
 	}
-- 
1.6.5.1


^ permalink raw reply related	[flat|nested] 195+ messages in thread

* [PATCH 1/8] PM: Add suspend block api.
@ 2010-05-14  4:11   ` Arve Hjønnevåg
  0 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-14  4:11 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: Len Brown, Andi Kleen, linux-doc, Jesse Barnes, Tejun Heo,
	Magnus Damm, Andrew Morton, Wu Fengguang

Adds /sys/power/policy that selects the behaviour of /sys/power/state.
After setting the policy to opportunistic, writes to /sys/power/state
become non-blocking requests that specify which suspend state to enter
when no suspend blockers are active. A special state, "on", stops the
process by activating the "main" suspend blocker.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/opportunistic-suspend.txt |  129 +++++++++++
 include/linux/suspend.h                       |    1 +
 include/linux/suspend_blocker.h               |   74 +++++++
 kernel/power/Kconfig                          |   16 ++
 kernel/power/Makefile                         |    1 +
 kernel/power/main.c                           |  128 +++++++++++-
 kernel/power/opportunistic_suspend.c          |  284 +++++++++++++++++++++++++
 kernel/power/power.h                          |    9 +
 kernel/power/suspend.c                        |    3 +-
 9 files changed, 637 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/power/opportunistic-suspend.txt
 create mode 100755 include/linux/suspend_blocker.h
 create mode 100644 kernel/power/opportunistic_suspend.c

diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
new file mode 100644
index 0000000..4bee7bc
--- /dev/null
+++ b/Documentation/power/opportunistic-suspend.txt
@@ -0,0 +1,129 @@
+Opportunistic Suspend
+=====================
+
+Opportunistic suspend is a feature allowing the system to be suspended (ie. put
+into one of the available sleep states) automatically whenever it is regarded
+as idle.  The suspend blockers framework described below is used to determine
+when that happens.
+
+The /sys/power/policy sysfs attribute is used to switch the system between the
+opportunistic and "forced" suspend behavior, where in the latter case the
+system is only suspended if a specific value, corresponding to one of the
+available system sleep states, is written into /sys/power/state.  However, in
+the former, opportunistic, case the system is put into the sleep state
+corresponding to the value written to /sys/power/state whenever there are no
+active suspend blockers. The default policy is "forced". Also, suspend blockers
+do not affect sleep states entered from idle.
+
+When the policy is "opportunisic", there is a special value, "on", that can be
+written to /sys/power/state. This will block the automatic sleep request, as if
+a suspend blocker was used by a device driver. This way the opportunistic
+suspend may be blocked by user space whithout switching back to the "forced"
+mode.
+
+A suspend blocker is an object used to inform the PM subsystem when the system
+can or cannot be suspended in the "opportunistic" mode (the "forced" mode
+ignores suspend blockers).  To use it, a device driver creates a struct
+suspend_blocker that must be initialized with suspend_blocker_init(). Before
+freeing the suspend_blocker structure or its name, suspend_blocker_unregister()
+must be called on it.
+
+A suspend blocker is activated using suspend_block(), which prevents the PM
+subsystem from putting the system into the requested sleep state in the
+"opportunistic" mode until the suspend blocker is deactivated with
+suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
+the system will not suspend as long as at least one of them is active.
+
+If opportunistic suspend is already in progress when suspend_block() is called,
+it will abort the suspend, unless suspend_ops->enter has already been
+executed. If suspend is aborted this way, the system is usually not fully
+operational at that point. The suspend callbacks of some drivers may still be
+running and it usually takes time to restore the system to the fully operational
+state.
+
+Here's an example showing how a cell phone or other embedded system can handle
+keystrokes (or other input events) in the presence of suspend blockers. Use
+set_irq_wake or a platform specific API to make sure the keypad interrupt wakes
+up the cpu. Once the keypad driver has resumed, the sequence of events can look
+like this:
+
+- The Keypad driver gets an interrupt. It then calls suspend_block on the
+  keypad-scan suspend_blocker and starts scanning the keypad matrix.
+- The keypad-scan code detects a key change and reports it to the input-event
+  driver.
+- The input-event driver sees the key change, enqueues an event, and calls
+  suspend_block on the input-event-queue suspend_blocker.
+- The keypad-scan code detects that no keys are held and calls suspend_unblock
+  on the keypad-scan suspend_blocker.
+- The user-space input-event thread returns from select/poll, calls
+  suspend_block on the process-input-events suspend_blocker and then calls read
+  on the input-event device.
+- The input-event driver dequeues the key-event and, since the queue is now
+  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
+- The user-space input-event thread returns from read. If it determines that
+  the key should be ignored, it calls suspend_unblock on the
+  process_input_events suspend_blocker and then calls select or poll. The
+  system will automatically suspend again, since now no suspend blockers are
+  active.
+
+If the key that was pressed instead should preform a simple action (for example,
+adjusting the volume), this action can be performed right before calling
+suspend_unblock on the process_input_events suspend_blocker. However, if the key
+triggers a longer-running action, that action needs its own suspend_blocker and
+suspend_block must be called on that suspend blocker before calling
+suspend_unblock on the process_input_events suspend_blocker.
+
+                 Key pressed   Key released
+                     |             |
+keypad-scan          ++++++++++++++++++
+input-event-queue        +++          +++
+process-input-events       +++          +++
+
+
+Driver API
+==========
+
+A driver can use the suspend block API by adding a suspend_blocker variable to
+its state and calling suspend_blocker_init(). For instance:
+
+struct state {
+	struct suspend_blocker suspend_blocker;
+}
+
+init() {
+	suspend_blocker_init(&state->suspend_blocker, name);
+}
+
+If the suspend_blocker variable is allocated statically,
+DEFINE_SUSPEND_BLOCKER() should be used to initialize it, for example:
+
+static DEFINE_SUSPEND_BLOCKER(blocker, name);
+
+and suspend_blocker_register(&blocker) has to be called to make the suspend
+blocker usable.
+
+Before freeing the memory in which a suspend_blocker variable is located,
+suspend_blocker_unregister() must be called, for instance:
+
+uninit() {
+	suspend_blocker_unregister(&state->suspend_blocker);
+}
+
+When the driver determines that it needs to run (usually in an interrupt
+handler) it calls suspend_block():
+
+	suspend_block(&state->suspend_blocker);
+
+When it no longer needs to run it calls suspend_unblock():
+
+	suspend_unblock(&state->suspend_blocker);
+
+Calling suspend_block() when the suspend blocker is active or suspend_unblock()
+when it is not active has no effect (i.e., these functions don't nest). This
+allows drivers to update their state and call suspend suspend_block() or
+suspend_unblock() based on the result. For instance:
+
+if (list_empty(&state->pending_work))
+	suspend_unblock(&state->suspend_blocker);
+else
+	suspend_block(&state->suspend_blocker);
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 5e781d8..07023d3 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -6,6 +6,7 @@
 #include <linux/init.h>
 #include <linux/pm.h>
 #include <linux/mm.h>
+#include <linux/suspend_blocker.h>
 #include <asm/errno.h>
 
 #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_VT) && defined(CONFIG_VT_CONSOLE)
diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
new file mode 100755
index 0000000..8788302
--- /dev/null
+++ b/include/linux/suspend_blocker.h
@@ -0,0 +1,74 @@
+/* include/linux/suspend_blocker.h
+ *
+ * Copyright (C) 2007-2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SUSPEND_BLOCKER_H
+#define _LINUX_SUSPEND_BLOCKER_H
+
+#include <linux/list.h>
+
+/**
+ * struct suspend_blocker - the basic suspend_blocker structure
+ * @link: List entry for active or inactive list.
+ * @flags: Tracks initialized and active state.
+ * @name: Suspend blocker name used for debugging.
+ *
+ * When a suspend_blocker is active it prevents the system from entering
+ * opportunistic suspend.
+ *
+ * The suspend_blocker structure must be initialized by suspend_blocker_init()
+ */
+struct suspend_blocker {
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	struct list_head link;
+	int flags;
+	const char *name;
+#endif
+};
+
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+#define __SUSPEND_BLOCKER_INITIALIZER(blocker_name) \
+	{ .name = #blocker_name, }
+
+#define DEFINE_SUSPEND_BLOCKER(blocker, name) \
+	struct suspend_blocker blocker = __SUSPEND_BLOCKER_INITIALIZER(name)
+
+extern void suspend_blocker_register(struct suspend_blocker *blocker);
+extern void suspend_blocker_init(struct suspend_blocker *blocker,
+				 const char *name);
+extern void suspend_blocker_unregister(struct suspend_blocker *blocker);
+extern void suspend_block(struct suspend_blocker *blocker);
+extern void suspend_unblock(struct suspend_blocker *blocker);
+extern bool suspend_blocker_is_active(struct suspend_blocker *blocker);
+extern bool suspend_is_blocked(void);
+
+#else
+
+#define DEFINE_SUSPEND_BLOCKER(blocker, name) \
+	struct suspend_blocker blocker
+
+static inline void suspend_blocker_register(struct suspend_blocker *bl) {}
+static inline void suspend_blocker_init(struct suspend_blocker *bl,
+					const char *n) {}
+static inline void suspend_blocker_unregister(struct suspend_blocker *bl) {}
+static inline void suspend_block(struct suspend_blocker *bl) {}
+static inline void suspend_unblock(struct suspend_blocker *bl) {}
+static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
+{
+	return false;
+}
+static inline bool suspend_is_blocked(void) { return false; }
+#endif
+
+#endif
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 5c36ea9..6d11a45 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -130,6 +130,22 @@ config SUSPEND_FREEZER
 
 	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
 
+config OPPORTUNISTIC_SUSPEND
+	bool "Opportunistic suspend"
+	depends on SUSPEND
+	select RTC_LIB
+	default n
+	---help---
+	  Opportunistic sleep support.  Allows the system to be put into a sleep
+	  state opportunistically, if it doesn't do any useful work at the
+	  moment. The PM subsystem is switched into this mode of operation by
+	  writing "opportunistic" into /sys/power/policy, while writing
+	  "forced" to this file turns the opportunistic suspend feature off.
+	  In the "opportunistic" mode suspend blockers are used to determine
+	  when to suspend the system and the value written to /sys/power/state
+	  determines the sleep state the system will be put into when there are
+	  no active suspend blockers.
+
 config HIBERNATION_NVS
 	bool
 
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 4319181..95d8e6d 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
+obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= opportunistic_suspend.o
 obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
 obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
 obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
diff --git a/kernel/power/main.c b/kernel/power/main.c
index b58800b..afbb4dd 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -20,6 +20,58 @@ DEFINE_MUTEX(pm_mutex);
 unsigned int pm_flags;
 EXPORT_SYMBOL(pm_flags);
 
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+struct pm_policy {
+	const char *name;
+	bool (*valid_state)(suspend_state_t state);
+	int (*set_state)(suspend_state_t state);
+};
+
+static struct pm_policy policies[] = {
+	{
+		.name		= "forced",
+		.valid_state	= valid_state,
+		.set_state	= enter_state,
+	},
+	{
+		.name		= "opportunistic",
+		.valid_state	= opportunistic_suspend_valid_state,
+		.set_state	= opportunistic_suspend_state,
+	},
+};
+
+static int policy;
+
+static inline bool hibernation_supported(void)
+{
+	return !strncmp(policies[policy].name, "forced", 6);
+}
+
+static inline bool pm_state_valid(int state_idx)
+{
+	return pm_states[state_idx] && policies[policy].valid_state(state_idx);
+}
+
+static inline int pm_enter_state(int state_idx)
+{
+	return policies[policy].set_state(state_idx);
+}
+
+#else
+
+static inline bool hibernation_supported(void) { return true; }
+
+static inline bool pm_state_valid(int state_idx)
+{
+	return pm_states[state_idx] && valid_state(state_idx);
+}
+
+static inline int pm_enter_state(int state_idx)
+{
+	return enter_state(state_idx);
+}
+#endif /* CONFIG_OPPORTUNISTIC_SUSPEND */
+
 #ifdef CONFIG_PM_SLEEP
 
 /* Routines for PM-transition notifications */
@@ -146,6 +198,12 @@ struct kobject *power_kobj;
  *
  *	store() accepts one of those strings, translates it into the 
  *	proper enumerated value, and initiates a suspend transition.
+ *
+ *	If policy is set to opportunistic, store() does not block until the
+ *	system resumes, and it will try to re-enter the state until another
+ *	state is requested. Suspend blockers are respected and the requested
+ *	state will only be entered when no suspend blockers are active.
+ *	Write "on" to disable.
  */
 static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 			  char *buf)
@@ -155,12 +213,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 	int i;
 
 	for (i = 0; i < PM_SUSPEND_MAX; i++) {
-		if (pm_states[i] && valid_state(i))
+		if (pm_state_valid(i))
 			s += sprintf(s,"%s ", pm_states[i]);
 	}
 #endif
 #ifdef CONFIG_HIBERNATION
-	s += sprintf(s, "%s\n", "disk");
+	if (hibernation_supported())
+		s += sprintf(s, "%s\n", "disk");
 #else
 	if (s != buf)
 		/* convert the last space to a newline */
@@ -173,7 +232,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			   const char *buf, size_t n)
 {
 #ifdef CONFIG_SUSPEND
-	suspend_state_t state = PM_SUSPEND_STANDBY;
+	suspend_state_t state = PM_SUSPEND_ON;
 	const char * const *s;
 #endif
 	char *p;
@@ -185,8 +244,9 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 
 	/* First, check if we are requested to hibernate */
 	if (len == 4 && !strncmp(buf, "disk", len)) {
-		error = hibernate();
-  goto Exit;
+		if (hibernation_supported())
+			error = hibernate();
+		goto Exit;
 	}
 
 #ifdef CONFIG_SUSPEND
@@ -195,7 +255,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			break;
 	}
 	if (state < PM_SUSPEND_MAX && *s)
-		error = enter_state(state);
+		error = pm_enter_state(state);
 #endif
 
  Exit:
@@ -204,6 +264,56 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 
 power_attr(state);
 
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+/**
+ *	policy - set policy for state
+ */
+static ssize_t policy_show(struct kobject *kobj,
+			   struct kobj_attribute *attr, char *buf)
+{
+	char *s = buf;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		if (i == policy)
+			s += sprintf(s, "[%s] ", policies[i].name);
+		else
+			s += sprintf(s, "%s ", policies[i].name);
+	}
+	if (s != buf)
+		/* convert the last space to a newline */
+		*(s-1) = '\n';
+	return (s - buf);
+}
+
+static ssize_t policy_store(struct kobject *kobj,
+			    struct kobj_attribute *attr,
+			    const char *buf, size_t n)
+{
+	const char *s;
+	char *p;
+	int len;
+	int i;
+
+	p = memchr(buf, '\n', n);
+	len = p ? p - buf : n;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		s = policies[i].name;
+		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
+			mutex_lock(&pm_mutex);
+			policies[policy].set_state(PM_SUSPEND_ON);
+			policy = i;
+			mutex_unlock(&pm_mutex);
+			return n;
+		}
+	}
+	return -EINVAL;
+}
+
+power_attr(policy);
+#endif /* CONFIG_OPPORTUNISTIC_SUSPEND */
+
 #ifdef CONFIG_PM_TRACE
 int pm_trace_enabled;
 
@@ -236,6 +346,9 @@ static struct attribute * g[] = {
 #endif
 #ifdef CONFIG_PM_SLEEP
 	&pm_async_attr.attr,
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	&policy_attr.attr,
+#endif
 #ifdef CONFIG_PM_DEBUG
 	&pm_test_attr.attr,
 #endif
@@ -247,7 +360,7 @@ static struct attribute_group attr_group = {
 	.attrs = g,
 };
 
-#ifdef CONFIG_PM_RUNTIME
+#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_OPPORTUNISTIC_SUSPEND)
 struct workqueue_struct *pm_wq;
 EXPORT_SYMBOL_GPL(pm_wq);
 
@@ -266,6 +379,7 @@ static int __init pm_init(void)
 	int error = pm_start_workqueue();
 	if (error)
 		return error;
+	opportunistic_suspend_init();
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
diff --git a/kernel/power/opportunistic_suspend.c b/kernel/power/opportunistic_suspend.c
new file mode 100644
index 0000000..acc1651
--- /dev/null
+++ b/kernel/power/opportunistic_suspend.c
@@ -0,0 +1,284 @@
+/*
+ * kernel/power/opportunistic_suspend.c
+ *
+ * Copyright (C) 2005-2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/suspend.h>
+
+#include "power.h"
+
+extern struct workqueue_struct *pm_wq;
+
+enum {
+	DEBUG_EXIT_SUSPEND = 1U << 0,
+	DEBUG_WAKEUP = 1U << 1,
+	DEBUG_USER_STATE = 1U << 2,
+	DEBUG_SUSPEND = 1U << 3,
+	DEBUG_SUSPEND_BLOCKER = 1U << 4,
+};
+static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SB_INITIALIZED            (1U << 8)
+#define SB_ACTIVE                 (1U << 9)
+
+DEFINE_SUSPEND_BLOCKER(main_suspend_blocker, main);
+
+static DEFINE_SPINLOCK(list_lock);
+static DEFINE_SPINLOCK(state_lock);
+static LIST_HEAD(inactive_blockers);
+static LIST_HEAD(active_blockers);
+static int current_event_num;
+static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
+static bool enable_suspend_blockers;
+
+#define pr_info_time(fmt, args...) \
+	do { \
+		struct timespec ts; \
+		struct rtc_time tm; \
+		getnstimeofday(&ts); \
+		rtc_time_to_tm(ts.tv_sec, &tm); \
+		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
+			args, \
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
+			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
+	} while (0);
+
+static void print_active_suspend_blockers(void)
+{
+	struct suspend_blocker *blocker;
+
+	list_for_each_entry(blocker, &active_blockers, link)
+		pr_info("PM: Active suspend blocker %s\n", blocker->name);
+}
+
+/**
+ * suspend_is_blocked - Check if there are active suspend blockers.
+ *
+ * Return true if suspend blockers are enabled and there are active suspend
+ * blockers, in which case the system cannot be put to sleep opportunistically.
+ */
+bool suspend_is_blocked(void)
+{
+	return enable_suspend_blockers && !list_empty(&active_blockers);
+}
+
+static void suspend_worker(struct work_struct *work)
+{
+	int ret;
+	int entry_event_num;
+
+	enable_suspend_blockers = true;
+
+	if (suspend_is_blocked()) {
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("PM: Automatic suspend aborted\n");
+		goto abort;
+	}
+
+	entry_event_num = current_event_num;
+
+	if (debug_mask & DEBUG_SUSPEND)
+		pr_info("PM: Automatic suspend\n");
+
+	ret = pm_suspend(requested_suspend_state);
+
+	if (debug_mask & DEBUG_EXIT_SUSPEND)
+		pr_info_time("PM: Automatic suspend exit, ret = %d ", ret);
+
+	if (current_event_num == entry_event_num) {
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("PM: pm_suspend() returned with no event\n");
+		queue_work(pm_wq, work);
+	}
+
+abort:
+	enable_suspend_blockers = false;
+}
+static DECLARE_WORK(suspend_work, suspend_worker);
+
+/**
+ * suspend_blocker_register - Prepare a suspend blocker for being used.
+ * @blocker: Suspend blocker to handle.
+ *
+ * The suspend blocker struct and name must not be freed before calling
+ * suspend_blocker_unregister().
+ */
+void suspend_blocker_register(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags = 0;
+
+	WARN_ON(!blocker->name);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("%s: Registering %s\n", __func__, blocker->name);
+
+	blocker->flags = SB_INITIALIZED;
+	INIT_LIST_HEAD(&blocker->link);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	list_add(&blocker->link, &inactive_blockers);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_blocker_register);
+
+/**
+ * suspend_blocker_init - Initialize a suspend blocker's name and register it.
+ * @blocker: Suspend blocker to initialize.
+ * @name:    The name of the suspend blocker to show in debug messages.
+ *
+ * The suspend blocker struct and name must not be freed before calling
+ * suspend_blocker_unregister().
+ */
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
+{
+	blocker->name = name;
+	suspend_blocker_register(blocker);
+}
+EXPORT_SYMBOL(suspend_blocker_init);
+
+/**
+ * suspend_blocker_unregister - Unregister a suspend blocker.
+ * @blocker: Suspend blocker to handle.
+ */
+void suspend_blocker_unregister(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	blocker->flags &= ~SB_INITIALIZED;
+	list_del(&blocker->link);
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(pm_wq, &suspend_work);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("%s: Unregistered %s\n", __func__, blocker->name);
+}
+EXPORT_SYMBOL(suspend_blocker_unregister);
+
+/**
+ * suspend_block - Block system suspend.
+ * @blocker: Suspend blocker to use.
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_block(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("%s: %s\n", __func__, blocker->name);
+
+	blocker->flags |= SB_ACTIVE;
+	list_move(&blocker->link, &active_blockers);
+
+	current_event_num++;
+
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_block);
+
+/**
+ * suspend_unblock - Allow system suspend to happen.
+ * @blocker: Suspend blocker to unblock.
+ *
+ * If no other suspend blockers are active, schedule suspend of the system.
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_unblock(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("%s: %s\n", __func__, blocker->name);
+
+	list_move(&blocker->link, &inactive_blockers);
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(pm_wq, &suspend_work);
+	blocker->flags &= ~(SB_ACTIVE);
+
+	if ((debug_mask & DEBUG_SUSPEND) && blocker == &main_suspend_blocker)
+		print_active_suspend_blockers();
+
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_unblock);
+
+/**
+ * suspend_blocker_is_active - Test if a suspend blocker is blocking suspend
+ * @blocker: Suspend blocker to check.
+ *
+ * Returns true if the suspend_blocker is currently active.
+ */
+bool suspend_blocker_is_active(struct suspend_blocker *blocker)
+{
+	WARN_ON(!(blocker->flags & SB_INITIALIZED));
+
+	return !!(blocker->flags & SB_ACTIVE);
+}
+EXPORT_SYMBOL(suspend_blocker_is_active);
+
+bool opportunistic_suspend_valid_state(suspend_state_t state)
+{
+	return (state == PM_SUSPEND_ON) || valid_state(state);
+}
+
+int opportunistic_suspend_state(suspend_state_t state)
+{
+	unsigned long irqflags;
+
+	if (!opportunistic_suspend_valid_state(state))
+		return -ENODEV;
+
+	spin_lock_irqsave(&state_lock, irqflags);
+
+	if (debug_mask & DEBUG_USER_STATE)
+		pr_info_time("%s: %s (%d->%d) at %lld ", __func__,
+			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
+			     requested_suspend_state, state,
+			     ktime_to_ns(ktime_get()));
+
+	requested_suspend_state = state;
+	if (state == PM_SUSPEND_ON)
+		suspend_block(&main_suspend_blocker);
+	else
+		suspend_unblock(&main_suspend_blocker);
+
+	spin_unlock_irqrestore(&state_lock, irqflags);
+
+	return 0;
+}
+
+void __init opportunistic_suspend_init(void)
+{
+	suspend_blocker_register(&main_suspend_blocker);
+	suspend_block(&main_suspend_blocker);
+}
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 46c5a26..2e9cfd5 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -236,3 +236,12 @@ static inline void suspend_thaw_processes(void)
 {
 }
 #endif
+
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+/* kernel/power/opportunistic_suspend.c */
+extern int opportunistic_suspend_state(suspend_state_t state);
+extern bool opportunistic_suspend_valid_state(suspend_state_t state);
+extern void __init opportunistic_suspend_init(void);
+#else
+static inline void opportunistic_suspend_init(void) {}
+#endif
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 56e7dbb..9eb3876 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -20,6 +20,7 @@
 #include "power.h"
 
 const char *const pm_states[PM_SUSPEND_MAX] = {
+	[PM_SUSPEND_ON]		= "on",
 	[PM_SUSPEND_STANDBY]	= "standby",
 	[PM_SUSPEND_MEM]	= "mem",
 };
@@ -157,7 +158,7 @@ static int suspend_enter(suspend_state_t state)
 
 	error = sysdev_suspend(PMSG_SUSPEND);
 	if (!error) {
-		if (!suspend_test(TEST_CORE))
+		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
 			error = suspend_ops->enter(state);
 		sysdev_resume();
 	}
-- 
1.6.5.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-30 22:36 ` Arve Hjønnevåg
                     ` (5 preceding siblings ...)
  2010-05-04  5:12   ` mark gross
@ 2010-05-13 19:01   ` Paul Walmsley
  2010-05-14 20:05     ` Paul Walmsley
  6 siblings, 1 reply; 195+ messages in thread
From: Paul Walmsley @ 2010-05-13 19:01 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: linux-doc, Jesse Barnes, linux-kernel, linux-pm, Liam Girdwood,
	Matthew Garrett, Len Brown, Jacob Pan, Linus Walleij,
	Magnus Damm, linux-omap, Arjan van de Ven, Daniel Walker,
	Theodore Ts'o, Geoff Smith, Brian Swetland, Mark Brown,
	Oleg Nesterov, Tejun Heo, Andrew Morton, Wu Fengguang

[-- Attachment #1: Type: TEXT/PLAIN, Size: 6285 bytes --]

Hi,

Some comments on the opportunistic suspend portion of this patch.

On Fri, 30 Apr 2010, Arve Hjønnevåg wrote:

> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.
> 
> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> ---
>  Documentation/power/opportunistic-suspend.txt |  119 +++++++++++
>  include/linux/suspend_blocker.h               |   64 ++++++
>  kernel/power/Kconfig                          |   16 ++
>  kernel/power/Makefile                         |    1 +
>  kernel/power/main.c                           |   92 ++++++++-
>  kernel/power/power.h                          |    9 +
>  kernel/power/suspend.c                        |    4 +-
>  kernel/power/suspend_blocker.c                |  261 +++++++++++++++++++++++++
>  8 files changed, 559 insertions(+), 7 deletions(-)
>  create mode 100644 Documentation/power/opportunistic-suspend.txt
>  create mode 100755 include/linux/suspend_blocker.h
>  create mode 100644 kernel/power/suspend_blocker.c


While reading the patch, it seemed that this opportunistic suspend
mechanism is a degenerate case of a system-wide form of CPUIdle.  I'll
call it "SystemIdle," for lack of a better term.  The main difference
between this and the CPUIdle design is that this patch's SystemIdle is
not integrated with any of the existing Linux mechanisms for limiting
idle duration or depth.  This is a major problem with the current
patch and should prevent it from being merged in its current state.
But if some SystemIdle-type code were to be written that did work with
the rest of the Linux kernel, it would be very useful.

To take an example from the current Linux-OMAP kernels: right now, the
Linux-OMAP code handles system-level idle through CPUIdle.[1] To
borrow ACPI terminology, some of the S-states are implemented as
C-states.  That isn't right, for several reasons:

- There are other constraints on system-level idle modes beyond those
  that apply to the CPU.  To take an OMAP example, on boards that
  support it, OMAP2+ chips can cut power to the external SoC
  high-frequency clock oscillator ("HF oscillator").[2] This is the
  clock that is later used to drive the CPU clock, bus clock, etc., on
  the SoC, but can also be used to drive other chips on the board,
  such as external audio codec chips, GPS receivers, etc.)  If power
  is cut to the HF oscillator, it can take several milliseconds to
  stabilize once power is reapplied.  Part of the decision as to
  whether to cut power to the HF oscillator is a classic idle
  balancing test between power economy and wakeup latency.  But this
  occurs on a system level - the CPU may not even be involved.

  Consider a low-power audio playback use-case.  The CPU may be only
  rarely involved, but other devices on the SoC may need to be active.
  For example, if a large number of audio samples are loaded into main
  memory, the CPU can go into a very deep sleep state while the DMA
  controller transfers the samples to the audio serial interface
  device.  But the DMA controller and audio serial interface need to
  run occasionally, so depending on FIFO depths in the system, the HF
  oscillator clock may need to be kept on, even if the CPU idle level
  would suggest that it could be disabled.

  Additionally, other external chips outside the SoC, may be clocked
  by that HF oscillator.  Continuing the low-power audio playback
  use-case, the external audio codec chip may also be clocked from the
  HF oscillator.  If the HF oscillator is cut when the SoC is idle,
  but the audio codec has samples in its buffer, audio playback will
  be disrupted.

- There should be only one system-level governor.  Since all of the
  production OMAPs so far have been single-CPU systems, we've been
  able to get away with this.  But on multi-CPU systems, there is one
  CPUIdle governor per CPU.  So reusing the CPUIdle governor to do this
  won't work the same way as it has for us in the past.

So some good SystemIdle-type code would be really useful for us.  But a 
significant part of what "good" means is that such a SystemIdle needs to 
be driven from the bottom up, rather than from the top down. In other 
words, both the 'policy' (i.e., the choice of metrics to use to determine 
the system idle state), and the 'implementation of that policy' (i.e., the 
way to enter those idle states), should be left up to the underlying chip 
architecture code that would implement SystemIdle client drivers.  (In 
this regard, it would be even more different than CPUIdle, which hardcodes 
the policy.)

This way, at least initially, there will be minimal top-down
restrictions on what architectures need to do to implement to their
chip's power management features.  As common features are identified,
by individual architectures implementing working code, then the
architecture developers can collaborate, and common components can be
proposed for merging into the top-level SystemIdle code.

Such a SystemIdle implementation should also work for Android's
opportunistic suspend code.  Google developers could choose the system
metrics that they wish to use to control system idle levels.  If they
want to ignore timers and the scheduler, that's fine - that design
decision can be confined to their policy driver, and the suspend-block
code can be activated the moment that some driver sets a PM
constraint, rather than doing anything more refined.  Similarly,
Google can change the way that the policy decisions are implemented.
Google could use a driver that does not take any advantage of
fine-grained power management aside from CPUIdle.  This driver could
simply enter full system suspend whenever the policy driver authorizes
it to do.

This approach - or some similar approach - should allow Android to do what 
it needs, while still allowing other, more finely-grained power management 
approaches to do something different.


regards,

- Paul

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 22:00                                                               ` [linux-pm] " Tony Lindgren
@ 2010-05-07 22:28                                                                 ` Matthew Garrett
  0 siblings, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 22:28 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 03:00:26PM -0700, Tony Lindgren wrote:

> Hmm, I'm thinking there would not be any need to turn the screen on
> for the broken apps until some other event such as a tap on the screen
> triggers the need to turn the screen on.
> 
> If it's a critical app, then it should be fixed so it's safe to keep
> running.
> 
> And yeah, I guess you could cgroups to categorize "timer certified"
> and "broken" apps.

This is a perfectly valid model, but it's not one that matches Android.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 21:48                                                             ` Matthew Garrett
@ 2010-05-07 22:00                                                               ` Tony Lindgren
  2010-05-07 22:00                                                               ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 22:00 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100507 14:44]:
> On Fri, May 07, 2010 at 02:42:11PM -0700, Tony Lindgren wrote:
> > * Matthew Garrett <mjg@redhat.com> [100507 14:34]:
> > > How do you know to wake the process up in response to the keypress?
> > 
> > Does it matter for processes that are not "certified"? Maybe you
> > could assume that you can keep it stopped until the screen is on
> > again, or some other policy.
> 
> Yes, it matters. You don't necessarily know whether to turn the screen 
> on until the app has had an opportunity to process the event. This is 
> exactly the kind of use case that suspend blocks are intended to allow, 
> so alternatives that don't permit that kind of use case aren't really 
> adequate alternatives.

Hmm, I'm thinking there would not be any need to turn the screen on
for the broken apps until some other event such as a tap on the screen
triggers the need to turn the screen on.

If it's a critical app, then it should be fixed so it's safe to keep
running.

And yeah, I guess you could cgroups to categorize "timer certified"
and "broken" apps.

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 21:42                                                           ` [linux-pm] " Tony Lindgren
  2010-05-07 21:48                                                             ` Matthew Garrett
@ 2010-05-07 21:48                                                             ` Matthew Garrett
  1 sibling, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 21:48 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 02:42:11PM -0700, Tony Lindgren wrote:
> * Matthew Garrett <mjg@redhat.com> [100507 14:34]:
> > How do you know to wake the process up in response to the keypress?
> 
> Does it matter for processes that are not "certified"? Maybe you
> could assume that you can keep it stopped until the screen is on
> again, or some other policy.

Yes, it matters. You don't necessarily know whether to turn the screen 
on until the app has had an opportunity to process the event. This is 
exactly the kind of use case that suspend blocks are intended to allow, 
so alternatives that don't permit that kind of use case aren't really 
adequate alternatives.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 21:35                                                         ` Arve Hjønnevåg
@ 2010-05-07 21:43                                                           ` Daniel Walker
  0 siblings, 0 replies; 195+ messages in thread
From: Daniel Walker @ 2010-05-07 21:43 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

On Fri, 2010-05-07 at 14:35 -0700, Arve Hjønnevåg wrote:
> On Fri, May 7, 2010 at 2:30 PM, Daniel Walker <dwalker@fifo99.com> wrote:
> > On Fri, 2010-05-07 at 22:03 +0100, Matthew Garrett wrote:
> >
> >> Here's a different example. A process is waiting for a keypress, but
> >> because it's badly written it's also drawing to the screen at 60 frames
> >> per second and preventing the system from every going to idle. How do
> >> you quiesce the system while still ensuring that the keypress will be
> >> delivered to the application?
> >
> > To me it's somewhat of a negative for suspend blockers. Since to solve
> > the problem you give above you would have to use a suspend blocker in an
> > asynchronous way (locked in an interrupt, released in a thread too)
> > assuming I understand your example. I've had my share of semaphore
> > nightmares, and I'm not too excited to see a protection scheme (i.e. a
> > lock) which allows asynchronous usage like suspend blockers.
> >
> 
> Why do you think this? The example in the documentation describe how
> we handle key events.

+- The Keypad driver gets an interrupt. It then calls suspend_block on the
+  keypad-scan suspend_blocker and starts scanning the keypad matrix.
+- The keypad-scan code detects a key change and reports it to the input-event
+  driver.
+- The input-event driver sees the key change, enqueues an event, and calls
+  suspend_block on the input-event-queue suspend_blocker.
+- The keypad-scan code detects that no keys are held and calls suspend_unblock
+  on the keypad-scan suspend_blocker.
+- The user-space input-event thread returns from select/poll, calls
+  suspend_block on the process-input-events suspend_blocker and then calls read
+  on the input-event device.
+- The input-event driver dequeues the key-event and, since the queue is now
+  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
+- The user-space input-event thread returns from read. If it determines that
+  the key should leave the screen off, it calls suspend_unblock on the
+  process_input_events suspend_blocker and then calls select or poll. The
+  system will automatically suspend again, since now no suspend blockers are
+  active.

This? Isn't this asynchronous on the input-event-queue since it's taken
in the interrupt , and release in the userspace thread?

Daniel

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 21:39                                                         ` [linux-pm] " Matthew Garrett
@ 2010-05-07 21:42                                                           ` Tony Lindgren
  2010-05-07 21:42                                                           ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 21:42 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100507 14:34]:
> On Fri, May 07, 2010 at 02:25:56PM -0700, Tony Lindgren wrote:
> > * Matthew Garrett <mjg@redhat.com> [100507 13:58]:
> > > Here's a different example. A process is waiting for a keypress, but 
> > > because it's badly written it's also drawing to the screen at 60 frames 
> > > per second and preventing the system from every going to idle. How do 
> > > you quiesce the system while still ensuring that the keypress will be 
> > > delivered to the application?
> > 
> > I guess it depends. If it's a game and I'm waiting to hit the fire
> > button, then I don't want the system to suspend!
> > 
> > It's starting to sound like you're really using suspend blocks
> > to "certify" that the app is safe to keep running.
> > 
> > Maybe it could be done with some kind of process flag instead that
> > would tell "this process is safe to keep running from timer point of view"
> > and if that flag is not set, then assume it's OK to stop the process
> > at any point?
> 
> How do you know to wake the process up in response to the keypress?

Does it matter for processes that are not "certified"? Maybe you
could assume that you can keep it stopped until the screen is on
again, or some other policy.

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 21:25                                                       ` [linux-pm] " Tony Lindgren
  2010-05-07 21:32                                                         ` Arve Hjønnevåg
@ 2010-05-07 21:39                                                         ` Matthew Garrett
  2010-05-07 21:39                                                         ` [linux-pm] " Matthew Garrett
  2 siblings, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 21:39 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 02:25:56PM -0700, Tony Lindgren wrote:
> * Matthew Garrett <mjg@redhat.com> [100507 13:58]:
> > Here's a different example. A process is waiting for a keypress, but 
> > because it's badly written it's also drawing to the screen at 60 frames 
> > per second and preventing the system from every going to idle. How do 
> > you quiesce the system while still ensuring that the keypress will be 
> > delivered to the application?
> 
> I guess it depends. If it's a game and I'm waiting to hit the fire
> button, then I don't want the system to suspend!
> 
> It's starting to sound like you're really using suspend blocks
> to "certify" that the app is safe to keep running.
> 
> Maybe it could be done with some kind of process flag instead that
> would tell "this process is safe to keep running from timer point of view"
> and if that flag is not set, then assume it's OK to stop the process
> at any point?

How do you know to wake the process up in response to the keypress?

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 21:30                                                       ` [linux-pm] " Daniel Walker
  2010-05-07 21:35                                                         ` Arve Hjønnevåg
  2010-05-07 21:35                                                         ` Arve Hjønnevåg
@ 2010-05-07 21:38                                                         ` Matthew Garrett
  2 siblings, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 21:38 UTC (permalink / raw)
  To: Daniel Walker
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 02:30:20PM -0700, Daniel Walker wrote:
> On Fri, 2010-05-07 at 22:03 +0100, Matthew Garrett wrote:
> 
> > Here's a different example. A process is waiting for a keypress, but 
> > because it's badly written it's also drawing to the screen at 60 frames 
> > per second and preventing the system from every going to idle. How do 
> > you quiesce the system while still ensuring that the keypress will be 
> > delivered to the application?
> 
> To me it's somewhat of a negative for suspend blockers. Since to solve
> the problem you give above you would have to use a suspend blocker in an
> asynchronous way (locked in an interrupt, released in a thread too)
> assuming I understand your example. I've had my share of semaphore
> nightmares, and I'm not too excited to see a protection scheme (i.e. a
> lock) which allows asynchronous usage like suspend blockers. 

Check the input patch for an example of this.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 21:30                                                       ` [linux-pm] " Daniel Walker
  2010-05-07 21:35                                                         ` Arve Hjønnevåg
@ 2010-05-07 21:35                                                         ` Arve Hjønnevåg
  2010-05-07 21:38                                                         ` Matthew Garrett
  2 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-07 21:35 UTC (permalink / raw)
  To: Daniel Walker
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

On Fri, May 7, 2010 at 2:30 PM, Daniel Walker <dwalker@fifo99.com> wrote:
> On Fri, 2010-05-07 at 22:03 +0100, Matthew Garrett wrote:
>
>> Here's a different example. A process is waiting for a keypress, but
>> because it's badly written it's also drawing to the screen at 60 frames
>> per second and preventing the system from every going to idle. How do
>> you quiesce the system while still ensuring that the keypress will be
>> delivered to the application?
>
> To me it's somewhat of a negative for suspend blockers. Since to solve
> the problem you give above you would have to use a suspend blocker in an
> asynchronous way (locked in an interrupt, released in a thread too)
> assuming I understand your example. I've had my share of semaphore
> nightmares, and I'm not too excited to see a protection scheme (i.e. a
> lock) which allows asynchronous usage like suspend blockers.
>

Why do you think this? The example in the documentation describe how
we handle key events.


-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 21:25                                                       ` [linux-pm] " Tony Lindgren
@ 2010-05-07 21:32                                                         ` Arve Hjønnevåg
  2010-05-07 21:39                                                         ` Matthew Garrett
  2010-05-07 21:39                                                         ` [linux-pm] " Matthew Garrett
  2 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-07 21:32 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

On Fri, May 7, 2010 at 2:25 PM, Tony Lindgren <tony@atomide.com> wrote:
> * Matthew Garrett <mjg@redhat.com> [100507 13:58]:
>> On Fri, May 07, 2010 at 01:53:29PM -0700, Tony Lindgren wrote:
>>
>> > So for example, if I leave ping running in a a terminal, do you
>> > have some way of preventing that from eating the battery?
>>
>> It depends on policy. If all network packets generate wakeup events then
>> no, that will carry on eating battery. If ICMP doesn't generate a wakeup
>> event then the process won't be run.
>>
>> > Do you just suspend the whole system anyways at some point,
>> > or do you have some other trick?
>>
>> If nothing's holding any suspend blocks then the system will enter
>> suspend. If the packet generates a wakeup then the kernel would block
>> suspend until userspace has had the opportunity to do so. Once userspace
>> has handled the packet then it could release the block and the system
>> will immediately transition back into suspend.
>
> OK, then what would I need to do to keep that ping running if I wanted to?
>

Use a suspend blocker. For instance: "runsuspendblock ping <addr>".

>> Here's a different example. A process is waiting for a keypress, but
>> because it's badly written it's also drawing to the screen at 60 frames
>> per second and preventing the system from every going to idle. How do
>> you quiesce the system while still ensuring that the keypress will be
>> delivered to the application?
>
> I guess it depends. If it's a game and I'm waiting to hit the fire
> button, then I don't want the system to suspend!
>

We don't suspend while the screen is on. If the user pressed the power
button to turn the screen off, then I would not want the game to
prevent suspend.

> It's starting to sound like you're really using suspend blocks
> to "certify" that the app is safe to keep running.
>
> Maybe it could be done with some kind of process flag instead that
> would tell "this process is safe to keep running from timer point of view"
> and if that flag is not set, then assume it's OK to stop the process
> at any point?
>
> Regards,
>
> Tony
> _______________________________________________
> linux-pm mailing list
> linux-pm@lists.linux-foundation.org
> https://lists.linux-foundation.org/mailman/listinfo/linux-pm
>



-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 21:03                                                     ` Matthew Garrett
  2010-05-07 21:25                                                       ` Tony Lindgren
  2010-05-07 21:25                                                       ` [linux-pm] " Tony Lindgren
@ 2010-05-07 21:30                                                       ` Daniel Walker
  2010-05-07 21:30                                                       ` [linux-pm] " Daniel Walker
  3 siblings, 0 replies; 195+ messages in thread
From: Daniel Walker @ 2010-05-07 21:30 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, 2010-05-07 at 22:03 +0100, Matthew Garrett wrote:

> Here's a different example. A process is waiting for a keypress, but 
> because it's badly written it's also drawing to the screen at 60 frames 
> per second and preventing the system from every going to idle. How do 
> you quiesce the system while still ensuring that the keypress will be 
> delivered to the application?

To me it's somewhat of a negative for suspend blockers. Since to solve
the problem you give above you would have to use a suspend blocker in an
asynchronous way (locked in an interrupt, released in a thread too)
assuming I understand your example. I've had my share of semaphore
nightmares, and I'm not too excited to see a protection scheme (i.e. a
lock) which allows asynchronous usage like suspend blockers. 

Daniel

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 21:03                                                     ` Matthew Garrett
@ 2010-05-07 21:25                                                       ` Tony Lindgren
  2010-05-07 21:25                                                       ` [linux-pm] " Tony Lindgren
                                                                         ` (2 subsequent siblings)
  3 siblings, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 21:25 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100507 13:58]:
> On Fri, May 07, 2010 at 01:53:29PM -0700, Tony Lindgren wrote:
> 
> > So for example, if I leave ping running in a a terminal, do you
> > have some way of preventing that from eating the battery?
> 
> It depends on policy. If all network packets generate wakeup events then 
> no, that will carry on eating battery. If ICMP doesn't generate a wakeup 
> event then the process won't be run.
> 
> > Do you just suspend the whole system anyways at some point,
> > or do you have some other trick?
> 
> If nothing's holding any suspend blocks then the system will enter 
> suspend. If the packet generates a wakeup then the kernel would block 
> suspend until userspace has had the opportunity to do so. Once userspace 
> has handled the packet then it could release the block and the system 
> will immediately transition back into suspend.

OK, then what would I need to do to keep that ping running if I wanted to?

> Here's a different example. A process is waiting for a keypress, but 
> because it's badly written it's also drawing to the screen at 60 frames 
> per second and preventing the system from every going to idle. How do 
> you quiesce the system while still ensuring that the keypress will be 
> delivered to the application?

I guess it depends. If it's a game and I'm waiting to hit the fire
button, then I don't want the system to suspend!

It's starting to sound like you're really using suspend blocks
to "certify" that the app is safe to keep running.

Maybe it could be done with some kind of process flag instead that
would tell "this process is safe to keep running from timer point of view"
and if that flag is not set, then assume it's OK to stop the process
at any point?

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 20:53                                                   ` [linux-pm] " Tony Lindgren
  2010-05-07 21:03                                                     ` Matthew Garrett
@ 2010-05-07 21:03                                                     ` Matthew Garrett
  1 sibling, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 21:03 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 01:53:29PM -0700, Tony Lindgren wrote:

> So for example, if I leave ping running in a a terminal, do you
> have some way of preventing that from eating the battery?

It depends on policy. If all network packets generate wakeup events then 
no, that will carry on eating battery. If ICMP doesn't generate a wakeup 
event then the process won't be run.

> Do you just suspend the whole system anyways at some point,
> or do you have some other trick?

If nothing's holding any suspend blocks then the system will enter 
suspend. If the packet generates a wakeup then the kernel would block 
suspend until userspace has had the opportunity to do so. Once userspace 
has handled the packet then it could release the block and the system 
will immediately transition back into suspend.

Here's a different example. A process is waiting for a keypress, but 
because it's badly written it's also drawing to the screen at 60 frames 
per second and preventing the system from every going to idle. How do 
you quiesce the system while still ensuring that the keypress will be 
delivered to the application?

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 20:28                                                 ` [linux-pm] " Matthew Garrett
@ 2010-05-07 20:53                                                   ` Tony Lindgren
  2010-05-07 20:53                                                   ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 20:53 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100507 13:24]:
> On Fri, May 07, 2010 at 12:55:48PM -0700, Tony Lindgren wrote:
> 
> > - Deal with broken apps whichever way you want in the userspace.
> 
> If we could do this then there would be no real need for suspend 
> blockers.

OK, I guess I don't understand all the details, need some kind
of common example I guess.

So for example, if I leave ping running in a a terminal, do you
have some way of preventing that from eating the battery?

In my scenario that program would keep on running until the
battery runs out, or something stops the program. But the system
keeps hitting retention mode in the idle loop.

How do you deal with programs like that?

Do you just suspend the whole system anyways at some point,
or do you have some other trick?

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 19:55                                               ` Tony Lindgren
@ 2010-05-07 20:28                                                 ` Matthew Garrett
  2010-05-07 20:28                                                 ` [linux-pm] " Matthew Garrett
  1 sibling, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 20:28 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 12:55:48PM -0700, Tony Lindgren wrote:

> - Deal with broken apps whichever way you want in the userspace.

If we could do this then there would be no real need for suspend 
blockers.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 19:33                                             ` [linux-pm] " Matthew Garrett
  2010-05-07 19:55                                               ` Tony Lindgren
@ 2010-05-07 19:55                                               ` Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 19:55 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100507 12:29]:
> On Fri, May 07, 2010 at 12:28:37PM -0700, Tony Lindgren wrote:
> > * Daniel Walker <dwalker@fifo99.com> [100507 12:01]:
> > > On Fri, 2010-05-07 at 19:46 +0100, Matthew Garrett wrote:
> > > > Freezer cgroups would work better, but it doesn't really change the 
> > > > point - if that application has an open network socket, how do you know 
> > > > to resume that application when a packet comes in?
> > 
> > No idea, but that still sounds a better situation to me than
> > trying to deal with that for a suspended system! :)
> 
> Suspend blocks deal with that problem. Nobody has yet demonstrated a 
> workable alternative solution.

Well there are obviously two paths to take, I think both of
them should work. The alternative to suspend blockers is:

- Implement a decent kernel idle function for the hardware and
  use cpuidle to change the c states. The dyntick stuff should
  already work for most hardware.

- Fix the core userspace apps to minimize timers.

- Deal with broken apps whichever way you want in the userspace.

- Optionally, do echo mem > /sys/power/state based on some
  product specific logic in the userspace.

The advantage of this is that no kernel changes are needed,
except for implementing the custom idle function for the
hardware. And this kind of setup has been in use for about
five years.

And, you can keep the system running constantly if you have
hardware that supports good idle modes, then you don't even
need to suspend at all.

The core problems I see with suspend blockers are following,
please correct me if I'm wrong:

- It is caching the value of echo mem > /sys/power/state and
  misusing it for runtime power management as the system still
  keeps running after trying to suspend. Instead, the kernel
  idle function and cpuidle should be used if the kernel keeps
  running.

- They require patching all over the drivers and userspace.

- Once the system is suspended, it does not run. And the apps
  don't behave in a standard way because the system does not
  wake to timer interrupts.

I agree that we need to be able to echo mem > /sys/power/state
in an atomic way. So if there are problems with that, those
issues should be fixed.

Cheers,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 19:28                                           ` Tony Lindgren
@ 2010-05-07 19:33                                             ` Matthew Garrett
  2010-05-07 19:33                                             ` [linux-pm] " Matthew Garrett
  1 sibling, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 19:33 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 12:28:37PM -0700, Tony Lindgren wrote:
> * Daniel Walker <dwalker@fifo99.com> [100507 12:01]:
> > On Fri, 2010-05-07 at 19:46 +0100, Matthew Garrett wrote:
> > > Freezer cgroups would work better, but it doesn't really change the 
> > > point - if that application has an open network socket, how do you know 
> > > to resume that application when a packet comes in?
> 
> No idea, but that still sounds a better situation to me than
> trying to deal with that for a suspended system! :)

Suspend blocks deal with that problem. Nobody has yet demonstrated a 
workable alternative solution.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 19:06                                         ` [linux-pm] " Daniel Walker
  2010-05-07 19:28                                           ` Tony Lindgren
@ 2010-05-07 19:28                                           ` Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 19:28 UTC (permalink / raw)
  To: Daniel Walker
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

* Daniel Walker <dwalker@fifo99.com> [100507 12:01]:
> On Fri, 2010-05-07 at 19:46 +0100, Matthew Garrett wrote:
> > On Fri, May 07, 2010 at 11:43:33AM -0700, Tony Lindgren wrote:
> > > * Matthew Garrett <mjg@redhat.com> [100507 11:23]:
> > > > On Fri, May 07, 2010 at 11:01:52AM -0700, Tony Lindgren wrote:
> > > > > * Matthew Garrett <mjg@redhat.com> [100507 10:46]:
> > > > > > Effective power management in the face of real-world applications is a 
> > > > > > reasonable usecase.
> > > > > 
> > > > > Sure there's no easy solution to misbehaving apps.
> > > > 
> > > > That's the point of the suspend blockers.
> > > 
> > > To me it sounds like suspending the whole system to deal with
> > > some misbehaving apps is an overkill. Sounds like kill -STOP
> > > the misbehaving apps should do the trick?
> > 
> > Freezer cgroups would work better, but it doesn't really change the 
> > point - if that application has an open network socket, how do you know 
> > to resume that application when a packet comes in?

No idea, but that still sounds a better situation to me than
trying to deal with that for a suspended system! :)
 
> suspend blockers can get abused also .. I had my phone in my pocket and
> accidentally ran "Google Talk" or something. It must have kept the
> screen on or kept the phone from suspending, so the battery drained
> completely over the course of an hour or so.

Yeah I guess there's nothing stopping that.

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 18:46                                       ` [linux-pm] " Matthew Garrett
@ 2010-05-07 19:06                                         ` Daniel Walker
  2010-05-07 19:06                                         ` [linux-pm] " Daniel Walker
  1 sibling, 0 replies; 195+ messages in thread
From: Daniel Walker @ 2010-05-07 19:06 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, 2010-05-07 at 19:46 +0100, Matthew Garrett wrote:
> On Fri, May 07, 2010 at 11:43:33AM -0700, Tony Lindgren wrote:
> > * Matthew Garrett <mjg@redhat.com> [100507 11:23]:
> > > On Fri, May 07, 2010 at 11:01:52AM -0700, Tony Lindgren wrote:
> > > > * Matthew Garrett <mjg@redhat.com> [100507 10:46]:
> > > > > Effective power management in the face of real-world applications is a 
> > > > > reasonable usecase.
> > > > 
> > > > Sure there's no easy solution to misbehaving apps.
> > > 
> > > That's the point of the suspend blockers.
> > 
> > To me it sounds like suspending the whole system to deal with
> > some misbehaving apps is an overkill. Sounds like kill -STOP
> > the misbehaving apps should do the trick?
> 
> Freezer cgroups would work better, but it doesn't really change the 
> point - if that application has an open network socket, how do you know 
> to resume that application when a packet comes in?

suspend blockers can get abused also .. I had my phone in my pocket and
accidentally ran "Google Talk" or something. It must have kept the
screen on or kept the phone from suspending, so the battery drained
completely over the course of an hour or so.

Daniel

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 18:43                                     ` Tony Lindgren
@ 2010-05-07 18:46                                       ` Matthew Garrett
  2010-05-07 18:46                                       ` [linux-pm] " Matthew Garrett
  1 sibling, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 18:46 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 11:43:33AM -0700, Tony Lindgren wrote:
> * Matthew Garrett <mjg@redhat.com> [100507 11:23]:
> > On Fri, May 07, 2010 at 11:01:52AM -0700, Tony Lindgren wrote:
> > > * Matthew Garrett <mjg@redhat.com> [100507 10:46]:
> > > > Effective power management in the face of real-world applications is a 
> > > > reasonable usecase.
> > > 
> > > Sure there's no easy solution to misbehaving apps.
> > 
> > That's the point of the suspend blockers.
> 
> To me it sounds like suspending the whole system to deal with
> some misbehaving apps is an overkill. Sounds like kill -STOP
> the misbehaving apps should do the trick?

Freezer cgroups would work better, but it doesn't really change the 
point - if that application has an open network socket, how do you know 
to resume that application when a packet comes in?

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 18:28                                   ` Matthew Garrett
@ 2010-05-07 18:43                                     ` Tony Lindgren
  2010-05-07 18:46                                       ` Matthew Garrett
  2010-05-07 18:46                                       ` [linux-pm] " Matthew Garrett
  0 siblings, 2 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 18:43 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100507 11:23]:
> On Fri, May 07, 2010 at 11:01:52AM -0700, Tony Lindgren wrote:
> > * Matthew Garrett <mjg@redhat.com> [100507 10:46]:
> > > Effective power management in the face of real-world applications is a 
> > > reasonable usecase.
> > 
> > Sure there's no easy solution to misbehaving apps.
> 
> That's the point of the suspend blockers.

To me it sounds like suspending the whole system to deal with
some misbehaving apps is an overkill. Sounds like kill -STOP
the misbehaving apps should do the trick?

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 18:01                                 ` [linux-pm] " Tony Lindgren
  2010-05-07 18:28                                   ` Matthew Garrett
@ 2010-05-07 18:28                                   ` Matthew Garrett
  1 sibling, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 18:28 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 11:01:52AM -0700, Tony Lindgren wrote:
> * Matthew Garrett <mjg@redhat.com> [100507 10:46]:
> > Effective power management in the face of real-world applications is a 
> > reasonable usecase.
> 
> Sure there's no easy solution to misbehaving apps.

That's the point of the suspend blockers.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 18:00                               ` Daniel Walker
@ 2010-05-07 18:17                                 ` Tony Lindgren
  0 siblings, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 18:17 UTC (permalink / raw)
  To: Daniel Walker
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

* Daniel Walker <dwalker@fifo99.com> [100507 10:56]:
> On Fri, 2010-05-07 at 18:51 +0100, Matthew Garrett wrote:
> > On Fri, May 07, 2010 at 10:40:43AM -0700, Daniel Walker wrote:
> > > On Fri, 2010-05-07 at 18:36 +0100, Matthew Garrett wrote:
> > > > If your wakeup latencies are sufficiently low and you have fine-grained 
> > > > enough control over your hardware then suspend in idle is a reasonable 
> > > > thing to do - but if you have a userspace app that's spinning then 
> > > > that doesn't solve the issue.
> > > 
> > > If there's a userspace app spinning then you don't go idle (or that's my
> > > assumption anyway). You mean like repeatedly blocking and unblocking
> > > right?
> > 
> > Right, that's the problem. idle-based suspend works fine if your 
> > applications let the system go idle, but if your applications are 
> > anything other than absolutely perfect in this respect then you consume 
> > significant power even if the device is sitting unused in someone's 
> > pocket.
> 
> True .. I'd wonder how an OMAP based devices deal with that issue, since
> they would have that exact problem. According to what Tony is telling
> us. Actually a bogus userspace can do a lot more than just consume power
> you could hang the system too.

There's nothing being done on omaps specifically, up to the device
user space to deal with that. From the kernel point of view the
omaps just run, and if idle enough, the device starts hitting the
retention and off modes in idle. But the system keeps on running
all the time, no need to suspend really. 

I don't think there's a generic solution to the misbehaving apps.
I know a lot of work has been done over past five years or so
to minimize the timer usage in various apps. But if I installed
some app that keeps the system busy, it would drain the battery.

I guess some apps could be just stopped when the screen blanks
unless somehow certified for the timer usage or something similar..

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 17:50                               ` Matthew Garrett
@ 2010-05-07 18:01                                 ` Tony Lindgren
  2010-05-07 18:01                                 ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 18:01 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100507 10:46]:
> On Fri, May 07, 2010 at 10:35:49AM -0700, Tony Lindgren wrote:
> > * Matthew Garrett <mjg@redhat.com> [100507 10:08]:
> > > The situation is this. You've frozen most of your userspace because you 
> > > don't trust the applications. One of those applications has an open 
> > > network socket, and policy indicates that receiving a network packet 
> > > should generate a wakeup, allow the userspace application to handle the 
> > > packet and then return to sleep. What mechanism do you use to do that?
> > 
> > I think the ideal way of doing this would be to have the system running
> > and hitting some deeper idle states using cpuidle. Then fix the apps
> > so timers don't wake up the system too often. Then everything would
> > just run in a normal way.
> 
> Effective power management in the face of real-world applications is a 
> reasonable usecase.

Sure there's no easy solution to misbehaving apps.

> > For the misbehaving stopped apps, maybe they could be woken
> > to deal with the incoming network data with sysfs_notify?
> 
> How would that work? Have the kernel send a sysfs_notify on every netwrk 
> packet and have a monitor app listen for it and unfreeze the rest of 
> userspace if it's frozen? That sounds expensive.

Yeah maybe there are better ways of dealing with this.

Maybe deferred timers would help some so all the apps could
be allowed to run until some power management policy decides
to suspend the whole device.

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 17:51                             ` [linux-pm] " Matthew Garrett
  2010-05-07 18:00                               ` Daniel Walker
@ 2010-05-07 18:00                               ` Daniel Walker
  1 sibling, 0 replies; 195+ messages in thread
From: Daniel Walker @ 2010-05-07 18:00 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, 2010-05-07 at 18:51 +0100, Matthew Garrett wrote:
> On Fri, May 07, 2010 at 10:40:43AM -0700, Daniel Walker wrote:
> > On Fri, 2010-05-07 at 18:36 +0100, Matthew Garrett wrote:
> > > If your wakeup latencies are sufficiently low and you have fine-grained 
> > > enough control over your hardware then suspend in idle is a reasonable 
> > > thing to do - but if you have a userspace app that's spinning then 
> > > that doesn't solve the issue.
> > 
> > If there's a userspace app spinning then you don't go idle (or that's my
> > assumption anyway). You mean like repeatedly blocking and unblocking
> > right?
> 
> Right, that's the problem. idle-based suspend works fine if your 
> applications let the system go idle, but if your applications are 
> anything other than absolutely perfect in this respect then you consume 
> significant power even if the device is sitting unused in someone's 
> pocket.

True .. I'd wonder how an OMAP based devices deal with that issue, since
they would have that exact problem. According to what Tony is telling
us. Actually a bogus userspace can do a lot more than just consume power
you could hang the system too.

Daniel

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 17:40                           ` Daniel Walker
@ 2010-05-07 17:51                             ` Matthew Garrett
  2010-05-07 17:51                             ` [linux-pm] " Matthew Garrett
  1 sibling, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 17:51 UTC (permalink / raw)
  To: Daniel Walker
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 10:40:43AM -0700, Daniel Walker wrote:
> On Fri, 2010-05-07 at 18:36 +0100, Matthew Garrett wrote:
> > If your wakeup latencies are sufficiently low and you have fine-grained 
> > enough control over your hardware then suspend in idle is a reasonable 
> > thing to do - but if you have a userspace app that's spinning then 
> > that doesn't solve the issue.
> 
> If there's a userspace app spinning then you don't go idle (or that's my
> assumption anyway). You mean like repeatedly blocking and unblocking
> right?

Right, that's the problem. idle-based suspend works fine if your 
applications let the system go idle, but if your applications are 
anything other than absolutely perfect in this respect then you consume 
significant power even if the device is sitting unused in someone's 
pocket.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 17:20                       ` Daniel Walker
  2010-05-07 17:36                         ` Matthew Garrett
  2010-05-07 17:36                         ` [linux-pm] " Matthew Garrett
@ 2010-05-07 17:50                         ` Tony Lindgren
  2 siblings, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 17:50 UTC (permalink / raw)
  To: Daniel Walker
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

* Daniel Walker <dwalker@fifo99.com> [100507 10:15]:
> On Thu, 2010-05-06 at 19:00 -0700, Tony Lindgren wrote:
> 
> > Oh some SoC devices like omap hit retention or off modes in the idle loop.
> > That brings down the idle consumption of a running system to minimal
> > levels. It's basically the same as suspending the device in every idle loop.
> > 
> > The system wakes up every few seconds or so, but that already provides
> > battery life of over ten days or so on an idle system. Of course the
> > wakeup latencies are in milliseconds then.
> 
> MSM doesn't have those power states unfortunately .. Your kind of
> suggesting what I was suggesting in that we should suspend in idle. Your
> hardware can do it easier tho since your have power states that are
> equal to suspend.

You might be able to implement suspend-while-idle with cpuidle and
a custom idle function on MSM. That is if MSM supports waking to
a timer event and some device interrupts. However, if it only wakes
to pressing some power button, then you will get missed timers
and the system won't behave in a normal way.

Maybe you could still kill -STOP the misbehaving apps, then keep the
idle system running until some timer expires, then when no activity,
echo mem > /sys/power/state?

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 17:35                             ` [linux-pm] " Tony Lindgren
  2010-05-07 17:50                               ` Matthew Garrett
@ 2010-05-07 17:50                               ` Matthew Garrett
  1 sibling, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 17:50 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 10:35:49AM -0700, Tony Lindgren wrote:
> * Matthew Garrett <mjg@redhat.com> [100507 10:08]:
> > The situation is this. You've frozen most of your userspace because you 
> > don't trust the applications. One of those applications has an open 
> > network socket, and policy indicates that receiving a network packet 
> > should generate a wakeup, allow the userspace application to handle the 
> > packet and then return to sleep. What mechanism do you use to do that?
> 
> I think the ideal way of doing this would be to have the system running
> and hitting some deeper idle states using cpuidle. Then fix the apps
> so timers don't wake up the system too often. Then everything would
> just run in a normal way.

Effective power management in the face of real-world applications is a 
reasonable usecase.

> For the misbehaving stopped apps, maybe they could be woken
> to deal with the incoming network data with sysfs_notify?

How would that work? Have the kernel send a sysfs_notify on every netwrk 
packet and have a monitor app listen for it and unfreeze the rest of 
userspace if it's frozen? That sounds expensive.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 17:36                         ` [linux-pm] " Matthew Garrett
  2010-05-07 17:40                           ` Daniel Walker
@ 2010-05-07 17:40                           ` Daniel Walker
  1 sibling, 0 replies; 195+ messages in thread
From: Daniel Walker @ 2010-05-07 17:40 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, 2010-05-07 at 18:36 +0100, Matthew Garrett wrote:
> On Fri, May 07, 2010 at 10:20:37AM -0700, Daniel Walker wrote:
> 
> > MSM doesn't have those power states unfortunately .. Your kind of
> > suggesting what I was suggesting in that we should suspend in idle. Your
> > hardware can do it easier tho since your have power states that are
> > equal to suspend.
> 
> If your wakeup latencies are sufficiently low and you have fine-grained 
> enough control over your hardware then suspend in idle is a reasonable 
> thing to do - but if you have a userspace app that's spinning then 
> that doesn't solve the issue.

If there's a userspace app spinning then you don't go idle (or that's my
assumption anyway). You mean like repeatedly blocking and unblocking
right?

Daniel

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 17:20                       ` Daniel Walker
@ 2010-05-07 17:36                         ` Matthew Garrett
  2010-05-07 17:36                         ` [linux-pm] " Matthew Garrett
  2010-05-07 17:50                         ` Tony Lindgren
  2 siblings, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 17:36 UTC (permalink / raw)
  To: Daniel Walker
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Fri, May 07, 2010 at 10:20:37AM -0700, Daniel Walker wrote:

> MSM doesn't have those power states unfortunately .. Your kind of
> suggesting what I was suggesting in that we should suspend in idle. Your
> hardware can do it easier tho since your have power states that are
> equal to suspend.

If your wakeup latencies are sufficiently low and you have fine-grained 
enough control over your hardware then suspend in idle is a reasonable 
thing to do - but if you have a userspace app that's spinning then 
that doesn't solve the issue.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07 17:12                           ` Matthew Garrett
@ 2010-05-07 17:35                             ` Tony Lindgren
  2010-05-07 17:35                             ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 17:35 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100507 10:08]:
> On Thu, May 06, 2010 at 07:05:41PM -0700, Tony Lindgren wrote:
> > * Matthew Garrett <mjg@redhat.com> [100506 11:39]:
> > > And the untrusted userspace code that's waiting for a network packet? 
> > > Adding a few seconds of latency isn't an option here.
> > 
> > Hmm well hitting retention and wake you can basically do between
> > jiffies. Hitting off mode in idle has way longer latencies,
> > but still in few hundred milliseconds or so, not seconds.
> 
> The situation is this. You've frozen most of your userspace because you 
> don't trust the applications. One of those applications has an open 
> network socket, and policy indicates that receiving a network packet 
> should generate a wakeup, allow the userspace application to handle the 
> packet and then return to sleep. What mechanism do you use to do that?

I think the ideal way of doing this would be to have the system running
and hitting some deeper idle states using cpuidle. Then fix the apps
so timers don't wake up the system too often. Then everything would
just run in a normal way.

For the misbehaving stopped apps, maybe they could be woken
to deal with the incoming network data with sysfs_notify?

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07  2:00                     ` [linux-pm] " Tony Lindgren
  2010-05-07 17:20                       ` Daniel Walker
@ 2010-05-07 17:20                       ` Daniel Walker
  1 sibling, 0 replies; 195+ messages in thread
From: Daniel Walker @ 2010-05-07 17:20 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

On Thu, 2010-05-06 at 19:00 -0700, Tony Lindgren wrote:

> Oh some SoC devices like omap hit retention or off modes in the idle loop.
> That brings down the idle consumption of a running system to minimal
> levels. It's basically the same as suspending the device in every idle loop.
> 
> The system wakes up every few seconds or so, but that already provides
> battery life of over ten days or so on an idle system. Of course the
> wakeup latencies are in milliseconds then.

MSM doesn't have those power states unfortunately .. Your kind of
suggesting what I was suggesting in that we should suspend in idle. Your
hardware can do it easier tho since your have power states that are
equal to suspend.

Daniel

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07  2:05                         ` [linux-pm] " Tony Lindgren
  2010-05-07 17:12                           ` Matthew Garrett
@ 2010-05-07 17:12                           ` Matthew Garrett
  1 sibling, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-07 17:12 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Thu, May 06, 2010 at 07:05:41PM -0700, Tony Lindgren wrote:
> * Matthew Garrett <mjg@redhat.com> [100506 11:39]:
> > And the untrusted userspace code that's waiting for a network packet? 
> > Adding a few seconds of latency isn't an option here.
> 
> Hmm well hitting retention and wake you can basically do between
> jiffies. Hitting off mode in idle has way longer latencies,
> but still in few hundred milliseconds or so, not seconds.

The situation is this. You've frozen most of your userspace because you 
don't trust the applications. One of those applications has an open 
network socket, and policy indicates that receiving a network packet 
should generate a wakeup, allow the userspace application to handle the 
packet and then return to sleep. What mechanism do you use to do that?

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07  0:10               ` Arve Hjønnevåg
@ 2010-05-07 15:54                 ` Tony Lindgren
  2010-05-28  6:43                 ` Pavel Machek
  2010-05-28  6:43                 ` [linux-pm] " Pavel Machek
  2 siblings, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07 15:54 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Arve Hjønnevåg <arve@android.com> [100506 17:05]:
> 2010/5/6 Tony Lindgren <tony@atomide.com>:
> > * Arve Hjønnevåg <arve@android.com> [100505 21:11]:

<snip>

> >> This is not a rare event. For example, the matrix keypad driver blocks
> >> suspend when a key is down so it can scan the matrix.
> >
> > Sure, but how many times per day are you suspending?
> >
> 
> How many times we successfully suspend is irrelevant here. If the
> driver blocks suspend the number of suspend attempts depend on your
> poll frequency.

I guess what I'm trying to say is that a failed suspend should be
a rare event.
 
> >> >> With the suspend block model we know the moment we're capable of
> >> >> suspending and then can suspend at that moment.  Continually trying to
> >> >> suspend seems like it'd be inefficient power-wise (we're going to be
> >> >> doing a lot more work as we try to suspend over and over, or we're
> >> >> going to retry after a timeout and spend extra time not suspended).
> >> >>
> >> >> We can often spend minutes (possibly many) at a time preventing
> >> >> suspend when the system is doing work that would be interrupted by a
> >> >> full suspend.
> >> >
> >> > Maybe you a userspace suspend policy manager would do the trick if
> >> > it knows when the screen is blanked and no audio has been played for
> >> > five seconds etc?
> >> >
> >>
> >> If user space has to initiate every suspend attempt, then you are
> >> forcing it to poll whenever a driver needs to block suspend.
> >
> > Hmm I don't follow you. If the userspace policy daemon timer times
> > out, the device suspends. If the device does not suspend because of
> > a blocking driver, then the timers get reset and you try again based
> > on some event such as when the screen blanks.
> >
> 
> This retry is what I call polling. You have to keep retrying until you
> succeed. Also, using the screen blank timeout for this polling is not
> a good idea. You do not want to toggle the screen off and on with with
> every suspend attempt.

The number of retries depends on the power management policy for your
device. And that is often device and use specific.

So having to retry suspend should be a rare event. The userspace should
mostly know when it's OK to suspend.

Regards,

Tony
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 23:48         ` Arve Hjønnevåg
  2010-05-07 14:22           ` Alan Stern
@ 2010-05-07 14:22           ` Alan Stern
  1 sibling, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-07 14:22 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Rafael J. Wysocki, Linux-pm mailing list,
	Kernel development list, Tejun Heo, Oleg Nesterov, Len Brown,
	Pavel Machek, Randy Dunlap, Jesse Barnes, Nigel Cunningham,
	Cornelia Huck, Ming Lei, Wu Fengguang, Andrew Morton,
	Maxim Levitsky, linux-doc

On Thu, 6 May 2010, Arve Hjønnevåg wrote:

> On Thu, May 6, 2010 at 12:40 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> > On Thu, 6 May 2010, Rafael J. Wysocki wrote:
> >
> >> > Here's a completely new issue.  When using opportunistic suspends on an
> >> > SMP system, it could happen that the system gets a wakeup event and
> >> > this routine starts running again before the event's IRQ handler has
> >> > finished (or has enabled a suspend blocker).  The system would
> >> > re-suspend too soon.
> >>
> >> This routine will be run from a freezable workqueue.
> >
> > But how do you know that processes won't get unfrozen until all the
> > pending IRQs have been handled?  Imagine something like this:
> >
> >        CPU 0                   CPU 1
> >        -----                   -----
> >        Wake up non-boot CPUs
> >        Resume devices          Invoke the IRQ handler
> >
> >        [ CPU 0 should wait here for the handler to finish,
> >          but it doesn't ]
> >
> >        Defrost threads         Handler running...
> >        Workqueue routine runs
> >        Start another suspend
> >                                Handler enables a suspend blocker,
> >                                but it's too late
> 
> It is not optimal, but it is not too late. We check if any suspend
> blockers block suspend after disabling non-boot cpus so as long as
> this is done in a way that does not lose interrupts the resuspend
> attempt will not succeed.

Is it possible for the resuspend to disable CPU 1 before the IRQ
handler can enable its suspend blocker?  (Probably not -- but I don't
know enough about how non-boot CPUs are enabled or disabled.)

Alan Stern


^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 23:48         ` Arve Hjønnevåg
@ 2010-05-07 14:22           ` Alan Stern
  2010-05-07 14:22           ` Alan Stern
  1 sibling, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-07 14:22 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, linux-doc, Kernel development list, Jesse Barnes,
	Oleg Nesterov, Tejun Heo, Linux-pm mailing list, Wu Fengguang,
	Andrew Morton

On Thu, 6 May 2010, Arve Hjønnevåg wrote:

> On Thu, May 6, 2010 at 12:40 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> > On Thu, 6 May 2010, Rafael J. Wysocki wrote:
> >
> >> > Here's a completely new issue.  When using opportunistic suspends on an
> >> > SMP system, it could happen that the system gets a wakeup event and
> >> > this routine starts running again before the event's IRQ handler has
> >> > finished (or has enabled a suspend blocker).  The system would
> >> > re-suspend too soon.
> >>
> >> This routine will be run from a freezable workqueue.
> >
> > But how do you know that processes won't get unfrozen until all the
> > pending IRQs have been handled?  Imagine something like this:
> >
> >        CPU 0                   CPU 1
> >        -----                   -----
> >        Wake up non-boot CPUs
> >        Resume devices          Invoke the IRQ handler
> >
> >        [ CPU 0 should wait here for the handler to finish,
> >          but it doesn't ]
> >
> >        Defrost threads         Handler running...
> >        Workqueue routine runs
> >        Start another suspend
> >                                Handler enables a suspend blocker,
> >                                but it's too late
> 
> It is not optimal, but it is not too late. We check if any suspend
> blockers block suspend after disabling non-boot cpus so as long as
> this is done in a way that does not lose interrupts the resuspend
> attempt will not succeed.

Is it possible for the resuspend to disable CPU 1 before the IRQ
handler can enable its suspend blocker?  (Probably not -- but I don't
know enough about how non-boot CPUs are enabled or disabled.)

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-07  3:45             ` [linux-pm] " mgross
@ 2010-05-07  4:10               ` Arve Hjønnevåg
  0 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-07  4:10 UTC (permalink / raw)
  To: markgross
  Cc: Len Brown, linux-doc, Brian Swetland, Kernel development list,
	Jesse Barnes, Oleg Nesterov, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton, Matthew Garrett

On Thu, May 6, 2010 at 8:45 PM, mgross <markgross@thegnar.org> wrote:
> On Thu, May 06, 2010 at 06:09:56PM +0100, Matthew Garrett wrote:
>> On Thu, May 06, 2010 at 10:01:51AM -0700, Tony Lindgren wrote:
>>
>> > Or are you suspending constantly, tens of times per minute even if
>> > there's no user activity?
>>
>> In this case you'd be repeatedly trying to suspend until the modem
>> driver stopped blocking it. It's pretty much a waste.
>
>
> lets not go off in the weeds for the wrong things now.  The answer to
> the retry is at most one time.  The first time would be blocked, then
> the suspend enable would need to re-enabled under user mode control that
> would, buy that time, know it has to ack to the modem to stop rejecting
> the suspend attempt.
>

This is incorrect in the general case. User-space has no way of
knowing which driver blocked suspend or when it will stop blocking
suspend.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:14             ` Tony Lindgren
                                 ` (3 preceding siblings ...)
  2010-05-06 17:35               ` Daniel Walker
@ 2010-05-07  3:45               ` mgross
  4 siblings, 0 replies; 195+ messages in thread
From: mgross @ 2010-05-07  3:45 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

On Thu, May 06, 2010 at 10:14:53AM -0700, Tony Lindgren wrote:
> * Matthew Garrett <mjg@redhat.com> [100506 10:05]:
> > On Thu, May 06, 2010 at 10:01:51AM -0700, Tony Lindgren wrote:
> > 
> > > Or are you suspending constantly, tens of times per minute even if
> > > there's no user activity?
> > 
> > In this case you'd be repeatedly trying to suspend until the modem 
> > driver stopped blocking it. It's pretty much a waste.
> 
> But then the userspace knows you're getting data from the modem, and
> it can kick some inactivity timer that determines when to try to
> suspend next.

Thank you for thinking.

--mgross

> 
> Why would you need to constantly try to suspend in that case?
> 
> Regards,
> 
> Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:09           ` [linux-pm] " Matthew Garrett
                               ` (2 preceding siblings ...)
  2010-05-07  3:45             ` [linux-pm] " mgross
@ 2010-05-07  3:45             ` mgross
  3 siblings, 0 replies; 195+ messages in thread
From: mgross @ 2010-05-07  3:45 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Thu, May 06, 2010 at 06:09:56PM +0100, Matthew Garrett wrote:
> On Thu, May 06, 2010 at 10:01:51AM -0700, Tony Lindgren wrote:
> 
> > Or are you suspending constantly, tens of times per minute even if
> > there's no user activity?
> 
> In this case you'd be repeatedly trying to suspend until the modem 
> driver stopped blocking it. It's pretty much a waste.


lets not go off in the weeds for the wrong things now.  The answer to
the retry is at most one time.  The first time would be blocked, then
the suspend enable would need to re-enabled under user mode control that
would, buy that time, know it has to ack to the modem to stop rejecting
the suspend attempt.

duh.

--mgross

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 18:47                       ` [linux-pm] " Alan Stern
@ 2010-05-07  2:20                         ` Tony Lindgren
  0 siblings, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07  2:20 UTC (permalink / raw)
  To: Alan Stern
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

* Alan Stern <stern@rowland.harvard.edu> [100506 11:42]:
> On Thu, 6 May 2010, Tony Lindgren wrote:
> 
> > Well if your hardware runs off-while-idle or even just
> > retention-while-idle, then the basic shell works just fine waking up
> > every few seconds or so.
> > 
> > Then you could keep init/shell/suspend policy deamon running until
> > it's time to suspend the whole device. To cut down runaway timers,
> > you could already freeze the desktop/GUI/whatever earlier.
> 
> This comes down mostly to efficiency.  Although the suspend blocker
> patch does the actual suspending in a workqueue thread, AFAIK there's
> no reason it couldn't use a user thread instead.
> 
> The important difference lies in what happens when a suspend fails
> because a driver is busy.  Without suspend blockers, the kernel has to
> go through the whole procedure of freezing userspace and kernel threads
> and then suspending a bunch of drivers before hitting the one that's
> busy.  Then all that work has to be undone.  By contrast, with suspend
> blockers the suspend attempt can fail right away with minimal overhead.

But does that really matter if you're only few tens of times times per
day or so? I don't understand why you would want to try to suspend except
after some timeout of no user or network activity.
 
> There's also a question of reliability.  With suspends controlled by 
> userspace there is a possibility of races, which could lead the system 
> to suspend when it shouldn't.  With control in the kernel, these races 
> can be eliminated.

I agree the suspend needs to happen without races. But I think the
logic for when to suspend should be done in the userspace as it
can be device or user specific.

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 18:44                       ` Matthew Garrett
@ 2010-05-07  2:05                         ` Tony Lindgren
  2010-05-07  2:05                         ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07  2:05 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100506 11:39]:
> On Thu, May 06, 2010 at 11:33:35AM -0700, Tony Lindgren wrote:
> > * Matthew Garrett <mjg@redhat.com> [100506 10:39]:
> > > On Thu, May 06, 2010 at 10:38:08AM -0700, Tony Lindgren wrote:
> > > 
> > > > If your userspace keeps polling and has runaway timers, then you
> > > > could suspend it's parent process to idle the system?
> > > 
> > > If your userspace is suspended, how does it process the events that 
> > > generated a system wakeup? If we had a good answer to that then suspend 
> > > blockers would be much less necessary.
> > 
> > Well if your hardware runs off-while-idle or even just
> > retention-while-idle, then the basic shell works just fine waking up
> > every few seconds or so.
> 
> And the untrusted userspace code that's waiting for a network packet? 
> Adding a few seconds of latency isn't an option here.

Hmm well hitting retention and wake you can basically do between
jiffies. Hitting off mode in idle has way longer latencies,
but still in few hundred milliseconds or so, not seconds.

And cpuidle pretty much takes care of hitting the desired C state
for you. This setup is totally working on Nokia N900 for example,
it's hitting off modes in idle and running all the time, it never
suspends.

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 19:11                   ` Daniel Walker
@ 2010-05-07  2:00                     ` Tony Lindgren
  2010-05-07  2:00                     ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-07  2:00 UTC (permalink / raw)
  To: Daniel Walker
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

* Daniel Walker <dwalker@fifo99.com> [100506 12:07]:
> On Thu, 2010-05-06 at 11:36 -0700, Tony Lindgren wrote:
> > * Daniel Walker <dwalker@fifo99.com> [100506 10:30]:
> > > On Thu, 2010-05-06 at 10:14 -0700, Tony Lindgren wrote:
> > > > * Matthew Garrett <mjg@redhat.com> [100506 10:05]:
> > > > > On Thu, May 06, 2010 at 10:01:51AM -0700, Tony Lindgren wrote:
> > > > > 
> > > > > > Or are you suspending constantly, tens of times per minute even if
> > > > > > there's no user activity?
> > > > > 
> > > > > In this case you'd be repeatedly trying to suspend until the modem 
> > > > > driver stopped blocking it. It's pretty much a waste.
> > > > 
> > > > But then the userspace knows you're getting data from the modem, and
> > > > it can kick some inactivity timer that determines when to try to
> > > > suspend next.
> > > 
> > > If the idle thread was doing the suspending then the inactivity timer by
> > > it's self could block suspend. As long as the idle thread was setup to
> > > check for timers. I'm sure that _isn't_ the point your trying to make.
> > > It just makes gobs more sense to me that the idle thread does the
> > > suspending .. Your idle, so depending on how long your idle then you
> > > suspend.
> > 
> > The alternative logic I'm suggesting is get the GUI into idle mode as
> > soon as possible, then limp along with off-while-idle or
> > retention-while-idle until some timer expires, then suspend the whole
> > device.
> 
> Could you elaborate on "off-while-idle" and "retention-while-idle" ? I'm
> not sure I follow what you mean.

Oh some SoC devices like omap hit retention or off modes in the idle loop.
That brings down the idle consumption of a running system to minimal
levels. It's basically the same as suspending the device in every idle loop.

The system wakes up every few seconds or so, but that already provides
battery life of over ten days or so on an idle system. Of course the
wakeup latencies are in milliseconds then.

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:04             ` Tony Lindgren
  2010-05-07  0:10               ` Arve Hjønnevåg
@ 2010-05-07  0:10               ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-07  0:10 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

2010/5/6 Tony Lindgren <tony@atomide.com>:
> * Arve Hjønnevåg <arve@android.com> [100505 21:11]:
>> On Wed, May 5, 2010 at 5:05 PM, Tony Lindgren <tony@atomide.com> wrote:
>> > * Brian Swetland <swetland@google.com> [100505 16:51]:
>> >> On Wed, May 5, 2010 at 4:47 PM, Tony Lindgren <tony@atomide.com> wrote:
>> >> > * Brian Swetland <swetland@google.com> [100505 14:34]:
>> >> >> On Wed, May 5, 2010 at 2:12 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
>> >> >> >>
>> >> >> >> Oh, like tell the modem that user mode has handled the ring event and
>> >> >> >> its ok to un-block?
>> >> >> >
>> >> >> > No, that's not how it works.  It would go like this:
>> >> >> >
>> >> >> >        The modem IRQ handler queues its event to the input subsystem.
>> >> >> >        As it does so the input subsystem enables a suspend blocker,
>> >> >> >        causing the system to stay awake after the IRQ is done.
>> >> >
>> >> > How about instead the modem driver fails to suspend until it's done?
>> >> >
>> >> > Each driver could have a suspend_policy sysfs entry with options such
>> >> > as [ forced | safe ]. The default would be forced. Forced would
>> >> > be the current behaviour, while safe would refuse suspend until the
>> >> > driver is done processing.
>> >> >
>> >> >> >        The user program enables its own suspend blocker before reading
>> >> >> >        the input queue.  When the queue is empty, the input subsystem
>> >> >> >        releases its suspend blocker.
>> >> >
>> >> > And also the input layer could refuse to suspend until it's done.
>> >> >
>> >> >> >        When the user program finishes processing the event, it
>> >> >> >        releases its suspend blocker.  Now the system can go back to
>> >> >> >        sleep.
>> >> >
>> >> > And here the user space just tries to suspend again when it's done?
>> >> > It's not like you're trying to suspend all the time, so it should be
>> >> > OK to retry a few times.
>> >>
>> >> We actually are trying to suspend all the time -- that's our basic
>> >> model -- suspend whenever we can when something doesn't prevent it.
>> >
>> > Maybe that state could be kept in some userspace suspend policy manager?
>> >
>> >> >> > At no point does the user program have to communicate anything to the
>> >> >> > modem driver, and at no point does it have to do anything out of the
>> >> >> > ordinary except to enable and disable a suspend blocker.
>> >> >>
>> >> >> Exactly -- and you can use the same style of overlapping suspend
>> >> >> blockers with other drivers than input, if the input interface is not
>> >> >> suitable for the particular interaction.
>> >> >
>> >> > Would the suspend blockers still be needed somewhere in the example
>> >> > above?
>> >>
>> >> How often would we retry suspending?
>> >
>> > Well based on some timer, the same way the screen blanks? Or five
>> > seconds of no audio play? So if the suspend fails, then reset whatever
>> > userspace suspend policy timers.
>> >
>> >> If we fail to suspend, don't we have to resume all the drivers that
>> >> suspended before the one that failed?  (Maybe I'm mistaken here)
>> >
>> > Sure, but I guess that should be a rare event that only happens when
>> > you try to suspend and something interrupts the suspend.
>> >
>>
>> This is not a rare event. For example, the matrix keypad driver blocks
>> suspend when a key is down so it can scan the matrix.
>
> Sure, but how many times per day are you suspending?
>

How many times we successfully suspend is irrelevant here. If the
driver blocks suspend the number of suspend attempts depend on your
poll frequency.

>> >> With the suspend block model we know the moment we're capable of
>> >> suspending and then can suspend at that moment.  Continually trying to
>> >> suspend seems like it'd be inefficient power-wise (we're going to be
>> >> doing a lot more work as we try to suspend over and over, or we're
>> >> going to retry after a timeout and spend extra time not suspended).
>> >>
>> >> We can often spend minutes (possibly many) at a time preventing
>> >> suspend when the system is doing work that would be interrupted by a
>> >> full suspend.
>> >
>> > Maybe you a userspace suspend policy manager would do the trick if
>> > it knows when the screen is blanked and no audio has been played for
>> > five seconds etc?
>> >
>>
>> If user space has to initiate every suspend attempt, then you are
>> forcing it to poll whenever a driver needs to block suspend.
>
> Hmm I don't follow you. If the userspace policy daemon timer times
> out, the device suspends. If the device does not suspend because of
> a blocking driver, then the timers get reset and you try again based
> on some event such as when the screen blanks.
>

This retry is what I call polling. You have to keep retrying until you
succeed. Also, using the screen blank timeout for this polling is not
a good idea. You do not want to toggle the screen off and on with with
every suspend attempt.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 19:40       ` Alan Stern
  2010-05-06 23:48         ` Arve Hjønnevåg
@ 2010-05-06 23:48         ` Arve Hjønnevåg
  2010-05-07 14:22           ` Alan Stern
  2010-05-07 14:22           ` Alan Stern
  1 sibling, 2 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-06 23:48 UTC (permalink / raw)
  To: Alan Stern
  Cc: Rafael J. Wysocki, Linux-pm mailing list,
	Kernel development list, Tejun Heo, Oleg Nesterov, Len Brown,
	Pavel Machek, Randy Dunlap, Jesse Barnes, Nigel Cunningham,
	Cornelia Huck, Ming Lei, Wu Fengguang, Andrew Morton,
	Maxim Levitsky, linux-doc

On Thu, May 6, 2010 at 12:40 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> On Thu, 6 May 2010, Rafael J. Wysocki wrote:
>
>> > Here's a completely new issue.  When using opportunistic suspends on an
>> > SMP system, it could happen that the system gets a wakeup event and
>> > this routine starts running again before the event's IRQ handler has
>> > finished (or has enabled a suspend blocker).  The system would
>> > re-suspend too soon.
>>
>> This routine will be run from a freezable workqueue.
>
> But how do you know that processes won't get unfrozen until all the
> pending IRQs have been handled?  Imagine something like this:
>
>        CPU 0                   CPU 1
>        -----                   -----
>        Wake up non-boot CPUs
>        Resume devices          Invoke the IRQ handler
>
>        [ CPU 0 should wait here for the handler to finish,
>          but it doesn't ]
>
>        Defrost threads         Handler running...
>        Workqueue routine runs
>        Start another suspend
>                                Handler enables a suspend blocker,
>                                but it's too late

It is not optimal, but it is not too late. We check if any suspend
blockers block suspend after disabling non-boot cpus so as long as
this is done in a way that does not lose interrupts the resuspend
attempt will not succeed.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 19:40       ` Alan Stern
@ 2010-05-06 23:48         ` Arve Hjønnevåg
  2010-05-06 23:48         ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-06 23:48 UTC (permalink / raw)
  To: Alan Stern
  Cc: Len Brown, linux-doc, Kernel development list, Jesse Barnes,
	Oleg Nesterov, Tejun Heo, Linux-pm mailing list, Wu Fengguang,
	Andrew Morton

On Thu, May 6, 2010 at 12:40 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> On Thu, 6 May 2010, Rafael J. Wysocki wrote:
>
>> > Here's a completely new issue.  When using opportunistic suspends on an
>> > SMP system, it could happen that the system gets a wakeup event and
>> > this routine starts running again before the event's IRQ handler has
>> > finished (or has enabled a suspend blocker).  The system would
>> > re-suspend too soon.
>>
>> This routine will be run from a freezable workqueue.
>
> But how do you know that processes won't get unfrozen until all the
> pending IRQs have been handled?  Imagine something like this:
>
>        CPU 0                   CPU 1
>        -----                   -----
>        Wake up non-boot CPUs
>        Resume devices          Invoke the IRQ handler
>
>        [ CPU 0 should wait here for the handler to finish,
>          but it doesn't ]
>
>        Defrost threads         Handler running...
>        Workqueue routine runs
>        Start another suspend
>                                Handler enables a suspend blocker,
>                                but it's too late

It is not optimal, but it is not too late. We check if any suspend
blockers block suspend after disabling non-boot cpus so as long as
this is done in a way that does not lose interrupts the resuspend
attempt will not succeed.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 19:28     ` Rafael J. Wysocki
  2010-05-06 19:40       ` Alan Stern
@ 2010-05-06 19:40       ` Alan Stern
  2010-05-06 23:48         ` Arve Hjønnevåg
  2010-05-06 23:48         ` Arve Hjønnevåg
  1 sibling, 2 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-06 19:40 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Arve Hjønnevåg, Linux-pm mailing list,
	Kernel development list, Tejun Heo, Oleg Nesterov, Len Brown,
	Pavel Machek, Randy Dunlap, Jesse Barnes, Nigel Cunningham,
	Cornelia Huck, Ming Lei, Wu Fengguang, Andrew Morton,
	Maxim Levitsky, linux-doc

On Thu, 6 May 2010, Rafael J. Wysocki wrote:

> > Here's a completely new issue.  When using opportunistic suspends on an
> > SMP system, it could happen that the system gets a wakeup event and
> > this routine starts running again before the event's IRQ handler has
> > finished (or has enabled a suspend blocker).  The system would 
> > re-suspend too soon.
> 
> This routine will be run from a freezable workqueue.

But how do you know that processes won't get unfrozen until all the 
pending IRQs have been handled?  Imagine something like this:

	CPU 0			CPU 1
	-----			-----
	Wake up non-boot CPUs
	Resume devices		Invoke the IRQ handler

	[ CPU 0 should wait here for the handler to finish, 
	  but it doesn't ]

	Defrost threads		Handler running...
	Workqueue routine runs
	Start another suspend
				Handler enables a suspend blocker,
				but it's too late

Alan Stern


^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 19:28     ` Rafael J. Wysocki
@ 2010-05-06 19:40       ` Alan Stern
  2010-05-06 19:40       ` Alan Stern
  1 sibling, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-06 19:40 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes,
	Kernel development list, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Thu, 6 May 2010, Rafael J. Wysocki wrote:

> > Here's a completely new issue.  When using opportunistic suspends on an
> > SMP system, it could happen that the system gets a wakeup event and
> > this routine starts running again before the event's IRQ handler has
> > finished (or has enabled a suspend blocker).  The system would 
> > re-suspend too soon.
> 
> This routine will be run from a freezable workqueue.

But how do you know that processes won't get unfrozen until all the 
pending IRQs have been handled?  Imagine something like this:

	CPU 0			CPU 1
	-----			-----
	Wake up non-boot CPUs
	Resume devices		Invoke the IRQ handler

	[ CPU 0 should wait here for the handler to finish, 
	  but it doesn't ]

	Defrost threads		Handler running...
	Workqueue routine runs
	Start another suspend
				Handler enables a suspend blocker,
				but it's too late

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 15:18   ` Alan Stern
  2010-05-06 19:28     ` Rafael J. Wysocki
@ 2010-05-06 19:28     ` Rafael J. Wysocki
  2010-05-06 19:40       ` Alan Stern
  2010-05-06 19:40       ` Alan Stern
  1 sibling, 2 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-06 19:28 UTC (permalink / raw)
  To: Alan Stern
  Cc: Arve Hjønnevåg, Linux-pm mailing list,
	Kernel development list, Tejun Heo, Oleg Nesterov, Len Brown,
	Pavel Machek, Randy Dunlap, Jesse Barnes, Nigel Cunningham,
	Cornelia Huck, Ming Lei, Wu Fengguang, Andrew Morton,
	Maxim Levitsky, linux-doc

On Thursday 06 May 2010, Alan Stern wrote:
> On Tue, 27 Apr 2010, [UTF-8] Arve Hjønnevåg wrote:
> 
> > +static void suspend_worker(struct work_struct *work)
> > +{
> > +	int ret;
> > +	int entry_event_num;
> > +
> > +	enable_suspend_blockers = true;
> > +	while (!suspend_is_blocked()) {
> > +		entry_event_num = current_event_num;
> > +
> > +		if (debug_mask & DEBUG_SUSPEND)
> > +			pr_info("suspend: enter suspend\n");
> > +
> > +		ret = pm_suspend(requested_suspend_state);
> > +
> > +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> > +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> > +
> > +		if (current_event_num == entry_event_num)
> > +			pr_info("suspend: pm_suspend returned with no event\n");
> > +	}
> > +	enable_suspend_blockers = false;
> > +}
> 
> Here's a completely new issue.  When using opportunistic suspends on an
> SMP system, it could happen that the system gets a wakeup event and
> this routine starts running again before the event's IRQ handler has
> finished (or has enabled a suspend blocker).  The system would 
> re-suspend too soon.

This routine will be run from a freezable workqueue.

Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 15:18   ` Alan Stern
@ 2010-05-06 19:28     ` Rafael J. Wysocki
  2010-05-06 19:28     ` Rafael J. Wysocki
  1 sibling, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-06 19:28 UTC (permalink / raw)
  To: Alan Stern
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes,
	Kernel development list, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Thursday 06 May 2010, Alan Stern wrote:
> On Tue, 27 Apr 2010, [UTF-8] Arve Hjønnevåg wrote:
> 
> > +static void suspend_worker(struct work_struct *work)
> > +{
> > +	int ret;
> > +	int entry_event_num;
> > +
> > +	enable_suspend_blockers = true;
> > +	while (!suspend_is_blocked()) {
> > +		entry_event_num = current_event_num;
> > +
> > +		if (debug_mask & DEBUG_SUSPEND)
> > +			pr_info("suspend: enter suspend\n");
> > +
> > +		ret = pm_suspend(requested_suspend_state);
> > +
> > +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> > +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> > +
> > +		if (current_event_num == entry_event_num)
> > +			pr_info("suspend: pm_suspend returned with no event\n");
> > +	}
> > +	enable_suspend_blockers = false;
> > +}
> 
> Here's a completely new issue.  When using opportunistic suspends on an
> SMP system, it could happen that the system gets a wakeup event and
> this routine starts running again before the event's IRQ handler has
> finished (or has enabled a suspend blocker).  The system would 
> re-suspend too soon.

This routine will be run from a freezable workqueue.

Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 18:36                 ` Tony Lindgren
  2010-05-06 19:11                   ` Daniel Walker
@ 2010-05-06 19:11                   ` Daniel Walker
  1 sibling, 0 replies; 195+ messages in thread
From: Daniel Walker @ 2010-05-06 19:11 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

On Thu, 2010-05-06 at 11:36 -0700, Tony Lindgren wrote:
> * Daniel Walker <dwalker@fifo99.com> [100506 10:30]:
> > On Thu, 2010-05-06 at 10:14 -0700, Tony Lindgren wrote:
> > > * Matthew Garrett <mjg@redhat.com> [100506 10:05]:
> > > > On Thu, May 06, 2010 at 10:01:51AM -0700, Tony Lindgren wrote:
> > > > 
> > > > > Or are you suspending constantly, tens of times per minute even if
> > > > > there's no user activity?
> > > > 
> > > > In this case you'd be repeatedly trying to suspend until the modem 
> > > > driver stopped blocking it. It's pretty much a waste.
> > > 
> > > But then the userspace knows you're getting data from the modem, and
> > > it can kick some inactivity timer that determines when to try to
> > > suspend next.
> > 
> > If the idle thread was doing the suspending then the inactivity timer by
> > it's self could block suspend. As long as the idle thread was setup to
> > check for timers. I'm sure that _isn't_ the point your trying to make.
> > It just makes gobs more sense to me that the idle thread does the
> > suspending .. Your idle, so depending on how long your idle then you
> > suspend.
> 
> The alternative logic I'm suggesting is get the GUI into idle mode as
> soon as possible, then limp along with off-while-idle or
> retention-while-idle until some timer expires, then suspend the whole
> device.

Could you elaborate on "off-while-idle" and "retention-while-idle" ? I'm
not sure I follow what you mean.

Daniel

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 18:33                     ` [linux-pm] " Tony Lindgren
  2010-05-06 18:44                       ` Matthew Garrett
  2010-05-06 18:44                       ` Matthew Garrett
@ 2010-05-06 18:47                       ` Alan Stern
  2010-05-06 18:47                       ` [linux-pm] " Alan Stern
  3 siblings, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-06 18:47 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

On Thu, 6 May 2010, Tony Lindgren wrote:

> Well if your hardware runs off-while-idle or even just
> retention-while-idle, then the basic shell works just fine waking up
> every few seconds or so.
> 
> Then you could keep init/shell/suspend policy deamon running until
> it's time to suspend the whole device. To cut down runaway timers,
> you could already freeze the desktop/GUI/whatever earlier.

This comes down mostly to efficiency.  Although the suspend blocker
patch does the actual suspending in a workqueue thread, AFAIK there's
no reason it couldn't use a user thread instead.

The important difference lies in what happens when a suspend fails
because a driver is busy.  Without suspend blockers, the kernel has to
go through the whole procedure of freezing userspace and kernel threads
and then suspending a bunch of drivers before hitting the one that's
busy.  Then all that work has to be undone.  By contrast, with suspend
blockers the suspend attempt can fail right away with minimal overhead.

There's also a question of reliability.  With suspends controlled by 
userspace there is a possibility of races, which could lead the system 
to suspend when it shouldn't.  With control in the kernel, these races 
can be eliminated.

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 18:33                     ` [linux-pm] " Tony Lindgren
  2010-05-06 18:44                       ` Matthew Garrett
@ 2010-05-06 18:44                       ` Matthew Garrett
  2010-05-06 18:47                       ` Alan Stern
  2010-05-06 18:47                       ` [linux-pm] " Alan Stern
  3 siblings, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-06 18:44 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Thu, May 06, 2010 at 11:33:35AM -0700, Tony Lindgren wrote:
> * Matthew Garrett <mjg@redhat.com> [100506 10:39]:
> > On Thu, May 06, 2010 at 10:38:08AM -0700, Tony Lindgren wrote:
> > 
> > > If your userspace keeps polling and has runaway timers, then you
> > > could suspend it's parent process to idle the system?
> > 
> > If your userspace is suspended, how does it process the events that 
> > generated a system wakeup? If we had a good answer to that then suspend 
> > blockers would be much less necessary.
> 
> Well if your hardware runs off-while-idle or even just
> retention-while-idle, then the basic shell works just fine waking up
> every few seconds or so.

And the untrusted userspace code that's waiting for a network packet? 
Adding a few seconds of latency isn't an option here.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:35               ` [linux-pm] " Daniel Walker
  2010-05-06 18:36                 ` Tony Lindgren
@ 2010-05-06 18:36                 ` Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-06 18:36 UTC (permalink / raw)
  To: Daniel Walker
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

* Daniel Walker <dwalker@fifo99.com> [100506 10:30]:
> On Thu, 2010-05-06 at 10:14 -0700, Tony Lindgren wrote:
> > * Matthew Garrett <mjg@redhat.com> [100506 10:05]:
> > > On Thu, May 06, 2010 at 10:01:51AM -0700, Tony Lindgren wrote:
> > > 
> > > > Or are you suspending constantly, tens of times per minute even if
> > > > there's no user activity?
> > > 
> > > In this case you'd be repeatedly trying to suspend until the modem 
> > > driver stopped blocking it. It's pretty much a waste.
> > 
> > But then the userspace knows you're getting data from the modem, and
> > it can kick some inactivity timer that determines when to try to
> > suspend next.
> 
> If the idle thread was doing the suspending then the inactivity timer by
> it's self could block suspend. As long as the idle thread was setup to
> check for timers. I'm sure that _isn't_ the point your trying to make.
> It just makes gobs more sense to me that the idle thread does the
> suspending .. Your idle, so depending on how long your idle then you
> suspend.

The alternative logic I'm suggesting is get the GUI into idle mode as
soon as possible, then limp along with off-while-idle or
retention-while-idle until some timer expires, then suspend the whole
device.

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:43                   ` Matthew Garrett
@ 2010-05-06 18:33                     ` Tony Lindgren
  2010-05-06 18:33                     ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-06 18:33 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100506 10:39]:
> On Thu, May 06, 2010 at 10:38:08AM -0700, Tony Lindgren wrote:
> 
> > If your userspace keeps polling and has runaway timers, then you
> > could suspend it's parent process to idle the system?
> 
> If your userspace is suspended, how does it process the events that 
> generated a system wakeup? If we had a good answer to that then suspend 
> blockers would be much less necessary.

Well if your hardware runs off-while-idle or even just
retention-while-idle, then the basic shell works just fine waking up
every few seconds or so.

Then you could keep init/shell/suspend policy deamon running until
it's time to suspend the whole device. To cut down runaway timers,
you could already freeze the desktop/GUI/whatever earlier.

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:38                 ` [linux-pm] " Tony Lindgren
  2010-05-06 17:43                   ` Matthew Garrett
@ 2010-05-06 17:43                   ` Matthew Garrett
  2010-05-28 13:29                   ` [linux-pm] " Pavel Machek
  2010-05-28 13:29                   ` Pavel Machek
  3 siblings, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-06 17:43 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Thu, May 06, 2010 at 10:38:08AM -0700, Tony Lindgren wrote:

> If your userspace keeps polling and has runaway timers, then you
> could suspend it's parent process to idle the system?

If your userspace is suspended, how does it process the events that 
generated a system wakeup? If we had a good answer to that then suspend 
blockers would be much less necessary.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:22               ` Matthew Garrett
@ 2010-05-06 17:38                 ` Tony Lindgren
  2010-05-06 17:38                 ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-06 17:38 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100506 10:17]:
> On Thu, May 06, 2010 at 10:14:53AM -0700, Tony Lindgren wrote:
> 
> > Why would you need to constantly try to suspend in that case?
> 
> Because otherwise you're awake for longer than you need to be.

If your system is idle and your hardware supports off-while-idle,
then that really does not matter. There's not much of a difference
in power savings, we're already talking over 10 days on batteries
with just off-while-idle on omaps.

If your userspace keeps polling and has runaway timers, then you
could suspend it's parent process to idle the system?

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:14             ` Tony Lindgren
                                 ` (2 preceding siblings ...)
  2010-05-06 17:35               ` [linux-pm] " Daniel Walker
@ 2010-05-06 17:35               ` Daniel Walker
  2010-05-07  3:45               ` mgross
  4 siblings, 0 replies; 195+ messages in thread
From: Daniel Walker @ 2010-05-06 17:35 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton,
	Matthew Garrett

On Thu, 2010-05-06 at 10:14 -0700, Tony Lindgren wrote:
> * Matthew Garrett <mjg@redhat.com> [100506 10:05]:
> > On Thu, May 06, 2010 at 10:01:51AM -0700, Tony Lindgren wrote:
> > 
> > > Or are you suspending constantly, tens of times per minute even if
> > > there's no user activity?
> > 
> > In this case you'd be repeatedly trying to suspend until the modem 
> > driver stopped blocking it. It's pretty much a waste.
> 
> But then the userspace knows you're getting data from the modem, and
> it can kick some inactivity timer that determines when to try to
> suspend next.

If the idle thread was doing the suspending then the inactivity timer by
it's self could block suspend. As long as the idle thread was setup to
check for timers. I'm sure that _isn't_ the point your trying to make.
It just makes gobs more sense to me that the idle thread does the
suspending .. Your idle, so depending on how long your idle then you
suspend.

Daniel

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:14             ` Tony Lindgren
  2010-05-06 17:22               ` Matthew Garrett
@ 2010-05-06 17:22               ` Matthew Garrett
  2010-05-06 17:35               ` [linux-pm] " Daniel Walker
                                 ` (2 subsequent siblings)
  4 siblings, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-06 17:22 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Thu, May 06, 2010 at 10:14:53AM -0700, Tony Lindgren wrote:

> Why would you need to constantly try to suspend in that case?

Because otherwise you're awake for longer than you need to be.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:09           ` [linux-pm] " Matthew Garrett
  2010-05-06 17:14             ` Tony Lindgren
@ 2010-05-06 17:14             ` Tony Lindgren
  2010-05-07  3:45             ` [linux-pm] " mgross
  2010-05-07  3:45             ` mgross
  3 siblings, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-06 17:14 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100506 10:05]:
> On Thu, May 06, 2010 at 10:01:51AM -0700, Tony Lindgren wrote:
> 
> > Or are you suspending constantly, tens of times per minute even if
> > there's no user activity?
> 
> In this case you'd be repeatedly trying to suspend until the modem 
> driver stopped blocking it. It's pretty much a waste.

But then the userspace knows you're getting data from the modem, and
it can kick some inactivity timer that determines when to try to
suspend next.

Why would you need to constantly try to suspend in that case?

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 17:01         ` [linux-pm] " Tony Lindgren
@ 2010-05-06 17:09           ` Matthew Garrett
  2010-05-06 17:09           ` [linux-pm] " Matthew Garrett
  1 sibling, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-06 17:09 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Thu, May 06, 2010 at 10:01:51AM -0700, Tony Lindgren wrote:

> Or are you suspending constantly, tens of times per minute even if
> there's no user activity?

In this case you'd be repeatedly trying to suspend until the modem 
driver stopped blocking it. It's pretty much a waste.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06  4:16           ` Arve Hjønnevåg
  2010-05-06 17:04             ` Tony Lindgren
@ 2010-05-06 17:04             ` Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-06 17:04 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Arve Hjønnevåg <arve@android.com> [100505 21:11]:
> On Wed, May 5, 2010 at 5:05 PM, Tony Lindgren <tony@atomide.com> wrote:
> > * Brian Swetland <swetland@google.com> [100505 16:51]:
> >> On Wed, May 5, 2010 at 4:47 PM, Tony Lindgren <tony@atomide.com> wrote:
> >> > * Brian Swetland <swetland@google.com> [100505 14:34]:
> >> >> On Wed, May 5, 2010 at 2:12 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> >> >> >>
> >> >> >> Oh, like tell the modem that user mode has handled the ring event and
> >> >> >> its ok to un-block?
> >> >> >
> >> >> > No, that's not how it works.  It would go like this:
> >> >> >
> >> >> >        The modem IRQ handler queues its event to the input subsystem.
> >> >> >        As it does so the input subsystem enables a suspend blocker,
> >> >> >        causing the system to stay awake after the IRQ is done.
> >> >
> >> > How about instead the modem driver fails to suspend until it's done?
> >> >
> >> > Each driver could have a suspend_policy sysfs entry with options such
> >> > as [ forced | safe ]. The default would be forced. Forced would
> >> > be the current behaviour, while safe would refuse suspend until the
> >> > driver is done processing.
> >> >
> >> >> >        The user program enables its own suspend blocker before reading
> >> >> >        the input queue.  When the queue is empty, the input subsystem
> >> >> >        releases its suspend blocker.
> >> >
> >> > And also the input layer could refuse to suspend until it's done.
> >> >
> >> >> >        When the user program finishes processing the event, it
> >> >> >        releases its suspend blocker.  Now the system can go back to
> >> >> >        sleep.
> >> >
> >> > And here the user space just tries to suspend again when it's done?
> >> > It's not like you're trying to suspend all the time, so it should be
> >> > OK to retry a few times.
> >>
> >> We actually are trying to suspend all the time -- that's our basic
> >> model -- suspend whenever we can when something doesn't prevent it.
> >
> > Maybe that state could be kept in some userspace suspend policy manager?
> >
> >> >> > At no point does the user program have to communicate anything to the
> >> >> > modem driver, and at no point does it have to do anything out of the
> >> >> > ordinary except to enable and disable a suspend blocker.
> >> >>
> >> >> Exactly -- and you can use the same style of overlapping suspend
> >> >> blockers with other drivers than input, if the input interface is not
> >> >> suitable for the particular interaction.
> >> >
> >> > Would the suspend blockers still be needed somewhere in the example
> >> > above?
> >>
> >> How often would we retry suspending?
> >
> > Well based on some timer, the same way the screen blanks? Or five
> > seconds of no audio play? So if the suspend fails, then reset whatever
> > userspace suspend policy timers.
> >
> >> If we fail to suspend, don't we have to resume all the drivers that
> >> suspended before the one that failed?  (Maybe I'm mistaken here)
> >
> > Sure, but I guess that should be a rare event that only happens when
> > you try to suspend and something interrupts the suspend.
> >
> 
> This is not a rare event. For example, the matrix keypad driver blocks
> suspend when a key is down so it can scan the matrix.

Sure, but how many times per day are you suspending?
 
> >> With the suspend block model we know the moment we're capable of
> >> suspending and then can suspend at that moment.  Continually trying to
> >> suspend seems like it'd be inefficient power-wise (we're going to be
> >> doing a lot more work as we try to suspend over and over, or we're
> >> going to retry after a timeout and spend extra time not suspended).
> >>
> >> We can often spend minutes (possibly many) at a time preventing
> >> suspend when the system is doing work that would be interrupted by a
> >> full suspend.
> >
> > Maybe you a userspace suspend policy manager would do the trick if
> > it knows when the screen is blanked and no audio has been played for
> > five seconds etc?
> >
> 
> If user space has to initiate every suspend attempt, then you are
> forcing it to poll whenever a driver needs to block suspend.

Hmm I don't follow you. If the userspace policy daemon timer times
out, the device suspends. If the device does not suspend because of
a blocking driver, then the timers get reset and you try again based
on some event such as when the screen blanks.

Regards,

Tony
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06 13:40       ` [linux-pm] " Matthew Garrett
@ 2010-05-06 17:01         ` Tony Lindgren
  2010-05-06 17:01         ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-06 17:01 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

* Matthew Garrett <mjg@redhat.com> [100506 06:35]:
> On Wed, May 05, 2010 at 04:47:55PM -0700, Tony Lindgren wrote:
> 
> > How about instead the modem driver fails to suspend until it's done?
> 
> Because every attempted suspend requires freezing userspace, suspending 
> devices until you hit one that refuses to suspend, resuming the devies 
> that did suspend and then unfreezing userspace. That's not an attractive 
> option.

But how many times per day are you really suspending? Maybe few tens
of times at most if it's based on some user activity?

Or are you suspending constantly, tens of times per minute even if
there's no user activity?

Regards,

Tony

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28  4:31 ` Arve Hjønnevåg
                     ` (5 preceding siblings ...)
  2010-05-06 15:18   ` Alan Stern
@ 2010-05-06 15:18   ` Alan Stern
  2010-05-06 19:28     ` Rafael J. Wysocki
  2010-05-06 19:28     ` Rafael J. Wysocki
  6 siblings, 2 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-06 15:18 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Linux-pm mailing list, Kernel development list,
	Rafael J. Wysocki, Tejun Heo, Oleg Nesterov, Len Brown,
	Pavel Machek, Randy Dunlap, Jesse Barnes, Nigel Cunningham,
	Cornelia Huck, Ming Lei, Wu Fengguang, Andrew Morton,
	Maxim Levitsky, linux-doc

On Tue, 27 Apr 2010, [UTF-8] Arve Hjønnevåg wrote:

> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = true;
> +	while (!suspend_is_blocked()) {
> +		entry_event_num = current_event_num;
> +
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: enter suspend\n");
> +
> +		ret = pm_suspend(requested_suspend_state);
> +
> +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> +
> +		if (current_event_num == entry_event_num)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +	}
> +	enable_suspend_blockers = false;
> +}

Here's a completely new issue.  When using opportunistic suspends on an
SMP system, it could happen that the system gets a wakeup event and
this routine starts running again before the event's IRQ handler has
finished (or has enabled a suspend blocker).  The system would 
re-suspend too soon.

Is there anything in the PM core that waits until all outstanding IRQs 
have been handled before unfreezing processes?  If there is, I can't 
find it.  Should such a thing be added?

Alan Stern


^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28  4:31 ` Arve Hjønnevåg
                     ` (4 preceding siblings ...)
  2010-04-28 20:50   ` Rafael J. Wysocki
@ 2010-05-06 15:18   ` Alan Stern
  2010-05-06 15:18   ` Alan Stern
  6 siblings, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-06 15:18 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, linux-doc, Kernel development list, Jesse Barnes,
	Oleg Nesterov, Tejun Heo, Linux-pm mailing list, Wu Fengguang,
	Andrew Morton

On Tue, 27 Apr 2010, [UTF-8] Arve Hjønnevåg wrote:

> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = true;
> +	while (!suspend_is_blocked()) {
> +		entry_event_num = current_event_num;
> +
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: enter suspend\n");
> +
> +		ret = pm_suspend(requested_suspend_state);
> +
> +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> +
> +		if (current_event_num == entry_event_num)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +	}
> +	enable_suspend_blockers = false;
> +}

Here's a completely new issue.  When using opportunistic suspends on an
SMP system, it could happen that the system gets a wakeup event and
this routine starts running again before the event's IRQ handler has
finished (or has enabled a suspend blocker).  The system would 
re-suspend too soon.

Is there anything in the PM core that waits until all outstanding IRQs 
have been handled before unfreezing processes?  If there is, I can't 
find it.  Should such a thing be added?

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05 23:47     ` [linux-pm] " Tony Lindgren
  2010-05-05 23:56       ` Brian Swetland
  2010-05-05 23:56       ` Brian Swetland
@ 2010-05-06 13:40       ` Matthew Garrett
  2010-05-06 13:40       ` [linux-pm] " Matthew Garrett
  3 siblings, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-06 13:40 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Wed, May 05, 2010 at 04:47:55PM -0700, Tony Lindgren wrote:

> How about instead the modem driver fails to suspend until it's done?

Because every attempted suspend requires freezing userspace, suspending 
devices until you hit one that refuses to suspend, resuming the devies 
that did suspend and then unfreezing userspace. That's not an attractive 
option.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-06  0:05         ` Tony Lindgren
  2010-05-06  4:16           ` Arve Hjønnevåg
@ 2010-05-06  4:16           ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-06  4:16 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, markgross, Brian Swetland, linux-doc,
	Kernel development list, Jesse Barnes, Oleg Nesterov, Tejun Heo,
	Linux-pm mailing list, Wu Fengguang, Andrew Morton

On Wed, May 5, 2010 at 5:05 PM, Tony Lindgren <tony@atomide.com> wrote:
> * Brian Swetland <swetland@google.com> [100505 16:51]:
>> On Wed, May 5, 2010 at 4:47 PM, Tony Lindgren <tony@atomide.com> wrote:
>> > * Brian Swetland <swetland@google.com> [100505 14:34]:
>> >> On Wed, May 5, 2010 at 2:12 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
>> >> >>
>> >> >> Oh, like tell the modem that user mode has handled the ring event and
>> >> >> its ok to un-block?
>> >> >
>> >> > No, that's not how it works.  It would go like this:
>> >> >
>> >> >        The modem IRQ handler queues its event to the input subsystem.
>> >> >        As it does so the input subsystem enables a suspend blocker,
>> >> >        causing the system to stay awake after the IRQ is done.
>> >
>> > How about instead the modem driver fails to suspend until it's done?
>> >
>> > Each driver could have a suspend_policy sysfs entry with options such
>> > as [ forced | safe ]. The default would be forced. Forced would
>> > be the current behaviour, while safe would refuse suspend until the
>> > driver is done processing.
>> >
>> >> >        The user program enables its own suspend blocker before reading
>> >> >        the input queue.  When the queue is empty, the input subsystem
>> >> >        releases its suspend blocker.
>> >
>> > And also the input layer could refuse to suspend until it's done.
>> >
>> >> >        When the user program finishes processing the event, it
>> >> >        releases its suspend blocker.  Now the system can go back to
>> >> >        sleep.
>> >
>> > And here the user space just tries to suspend again when it's done?
>> > It's not like you're trying to suspend all the time, so it should be
>> > OK to retry a few times.
>>
>> We actually are trying to suspend all the time -- that's our basic
>> model -- suspend whenever we can when something doesn't prevent it.
>
> Maybe that state could be kept in some userspace suspend policy manager?
>
>> >> > At no point does the user program have to communicate anything to the
>> >> > modem driver, and at no point does it have to do anything out of the
>> >> > ordinary except to enable and disable a suspend blocker.
>> >>
>> >> Exactly -- and you can use the same style of overlapping suspend
>> >> blockers with other drivers than input, if the input interface is not
>> >> suitable for the particular interaction.
>> >
>> > Would the suspend blockers still be needed somewhere in the example
>> > above?
>>
>> How often would we retry suspending?
>
> Well based on some timer, the same way the screen blanks? Or five
> seconds of no audio play? So if the suspend fails, then reset whatever
> userspace suspend policy timers.
>
>> If we fail to suspend, don't we have to resume all the drivers that
>> suspended before the one that failed?  (Maybe I'm mistaken here)
>
> Sure, but I guess that should be a rare event that only happens when
> you try to suspend and something interrupts the suspend.
>

This is not a rare event. For example, the matrix keypad driver blocks
suspend when a key is down so it can scan the matrix.

>> With the suspend block model we know the moment we're capable of
>> suspending and then can suspend at that moment.  Continually trying to
>> suspend seems like it'd be inefficient power-wise (we're going to be
>> doing a lot more work as we try to suspend over and over, or we're
>> going to retry after a timeout and spend extra time not suspended).
>>
>> We can often spend minutes (possibly many) at a time preventing
>> suspend when the system is doing work that would be interrupted by a
>> full suspend.
>
> Maybe you a userspace suspend policy manager would do the trick if
> it knows when the screen is blanked and no audio has been played for
> five seconds etc?
>

If user space has to initiate every suspend attempt, then you are
forcing it to poll whenever a driver needs to block suspend.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05 23:56       ` Brian Swetland
  2010-05-06  0:05         ` Tony Lindgren
@ 2010-05-06  0:05         ` Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-06  0:05 UTC (permalink / raw)
  To: Brian Swetland
  Cc: Len Brown, linux-doc, markgross, Kernel development list,
	Jesse Barnes, Oleg Nesterov, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

* Brian Swetland <swetland@google.com> [100505 16:51]:
> On Wed, May 5, 2010 at 4:47 PM, Tony Lindgren <tony@atomide.com> wrote:
> > * Brian Swetland <swetland@google.com> [100505 14:34]:
> >> On Wed, May 5, 2010 at 2:12 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> >> >>
> >> >> Oh, like tell the modem that user mode has handled the ring event and
> >> >> its ok to un-block?
> >> >
> >> > No, that's not how it works.  It would go like this:
> >> >
> >> >        The modem IRQ handler queues its event to the input subsystem.
> >> >        As it does so the input subsystem enables a suspend blocker,
> >> >        causing the system to stay awake after the IRQ is done.
> >
> > How about instead the modem driver fails to suspend until it's done?
> >
> > Each driver could have a suspend_policy sysfs entry with options such
> > as [ forced | safe ]. The default would be forced. Forced would
> > be the current behaviour, while safe would refuse suspend until the
> > driver is done processing.
> >
> >> >        The user program enables its own suspend blocker before reading
> >> >        the input queue.  When the queue is empty, the input subsystem
> >> >        releases its suspend blocker.
> >
> > And also the input layer could refuse to suspend until it's done.
> >
> >> >        When the user program finishes processing the event, it
> >> >        releases its suspend blocker.  Now the system can go back to
> >> >        sleep.
> >
> > And here the user space just tries to suspend again when it's done?
> > It's not like you're trying to suspend all the time, so it should be
> > OK to retry a few times.
> 
> We actually are trying to suspend all the time -- that's our basic
> model -- suspend whenever we can when something doesn't prevent it.

Maybe that state could be kept in some userspace suspend policy manager?
 
> >> > At no point does the user program have to communicate anything to the
> >> > modem driver, and at no point does it have to do anything out of the
> >> > ordinary except to enable and disable a suspend blocker.
> >>
> >> Exactly -- and you can use the same style of overlapping suspend
> >> blockers with other drivers than input, if the input interface is not
> >> suitable for the particular interaction.
> >
> > Would the suspend blockers still be needed somewhere in the example
> > above?
> 
> How often would we retry suspending?

Well based on some timer, the same way the screen blanks? Or five
seconds of no audio play? So if the suspend fails, then reset whatever
userspace suspend policy timers.
 
> If we fail to suspend, don't we have to resume all the drivers that
> suspended before the one that failed?  (Maybe I'm mistaken here)

Sure, but I guess that should be a rare event that only happens when
you try to suspend and something interrupts the suspend.
 
> With the suspend block model we know the moment we're capable of
> suspending and then can suspend at that moment.  Continually trying to
> suspend seems like it'd be inefficient power-wise (we're going to be
> doing a lot more work as we try to suspend over and over, or we're
> going to retry after a timeout and spend extra time not suspended).
> 
> We can often spend minutes (possibly many) at a time preventing
> suspend when the system is doing work that would be interrupted by a
> full suspend.

Maybe you a userspace suspend policy manager would do the trick if
it knows when the screen is blanked and no audio has been played for
five seconds etc?

Regards,

Tony
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05 23:47     ` [linux-pm] " Tony Lindgren
  2010-05-05 23:56       ` Brian Swetland
@ 2010-05-05 23:56       ` Brian Swetland
  2010-05-06 13:40       ` Matthew Garrett
  2010-05-06 13:40       ` [linux-pm] " Matthew Garrett
  3 siblings, 0 replies; 195+ messages in thread
From: Brian Swetland @ 2010-05-05 23:56 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Len Brown, linux-doc, markgross, Kernel development list,
	Jesse Barnes, Oleg Nesterov, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Wed, May 5, 2010 at 4:47 PM, Tony Lindgren <tony@atomide.com> wrote:
> * Brian Swetland <swetland@google.com> [100505 14:34]:
>> On Wed, May 5, 2010 at 2:12 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
>> >>
>> >> Oh, like tell the modem that user mode has handled the ring event and
>> >> its ok to un-block?
>> >
>> > No, that's not how it works.  It would go like this:
>> >
>> >        The modem IRQ handler queues its event to the input subsystem.
>> >        As it does so the input subsystem enables a suspend blocker,
>> >        causing the system to stay awake after the IRQ is done.
>
> How about instead the modem driver fails to suspend until it's done?
>
> Each driver could have a suspend_policy sysfs entry with options such
> as [ forced | safe ]. The default would be forced. Forced would
> be the current behaviour, while safe would refuse suspend until the
> driver is done processing.
>
>> >        The user program enables its own suspend blocker before reading
>> >        the input queue.  When the queue is empty, the input subsystem
>> >        releases its suspend blocker.
>
> And also the input layer could refuse to suspend until it's done.
>
>> >        When the user program finishes processing the event, it
>> >        releases its suspend blocker.  Now the system can go back to
>> >        sleep.
>
> And here the user space just tries to suspend again when it's done?
> It's not like you're trying to suspend all the time, so it should be
> OK to retry a few times.

We actually are trying to suspend all the time -- that's our basic
model -- suspend whenever we can when something doesn't prevent it.

>> > At no point does the user program have to communicate anything to the
>> > modem driver, and at no point does it have to do anything out of the
>> > ordinary except to enable and disable a suspend blocker.
>>
>> Exactly -- and you can use the same style of overlapping suspend
>> blockers with other drivers than input, if the input interface is not
>> suitable for the particular interaction.
>
> Would the suspend blockers still be needed somewhere in the example
> above?

How often would we retry suspending?

If we fail to suspend, don't we have to resume all the drivers that
suspended before the one that failed?  (Maybe I'm mistaken here)

With the suspend block model we know the moment we're capable of
suspending and then can suspend at that moment.  Continually trying to
suspend seems like it'd be inefficient power-wise (we're going to be
doing a lot more work as we try to suspend over and over, or we're
going to retry after a timeout and spend extra time not suspended).

We can often spend minutes (possibly many) at a time preventing
suspend when the system is doing work that would be interrupted by a
full suspend.

Brian
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05 21:37   ` Brian Swetland
@ 2010-05-05 23:47     ` Tony Lindgren
  2010-05-05 23:47     ` [linux-pm] " Tony Lindgren
  1 sibling, 0 replies; 195+ messages in thread
From: Tony Lindgren @ 2010-05-05 23:47 UTC (permalink / raw)
  To: Brian Swetland
  Cc: Len Brown, linux-doc, markgross, Kernel development list,
	Jesse Barnes, Oleg Nesterov, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

* Brian Swetland <swetland@google.com> [100505 14:34]:
> On Wed, May 5, 2010 at 2:12 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> >>
> >> Oh, like tell the modem that user mode has handled the ring event and
> >> its ok to un-block?
> >
> > No, that's not how it works.  It would go like this:
> >
> >        The modem IRQ handler queues its event to the input subsystem.
> >        As it does so the input subsystem enables a suspend blocker,
> >        causing the system to stay awake after the IRQ is done.

How about instead the modem driver fails to suspend until it's done?

Each driver could have a suspend_policy sysfs entry with options such
as [ forced | safe ]. The default would be forced. Forced would
be the current behaviour, while safe would refuse suspend until the
driver is done processing.

> >        The user program enables its own suspend blocker before reading
> >        the input queue.  When the queue is empty, the input subsystem
> >        releases its suspend blocker.

And also the input layer could refuse to suspend until it's done.

> >        When the user program finishes processing the event, it
> >        releases its suspend blocker.  Now the system can go back to
> >        sleep.

And here the user space just tries to suspend again when it's done?
It's not like you're trying to suspend all the time, so it should be
OK to retry a few times.

> > At no point does the user program have to communicate anything to the
> > modem driver, and at no point does it have to do anything out of the
> > ordinary except to enable and disable a suspend blocker.
> 
> Exactly -- and you can use the same style of overlapping suspend
> blockers with other drivers than input, if the input interface is not
> suitable for the particular interaction.

Would the suspend blockers still be needed somewhere in the example
above?

Regards,

Tony
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05 21:12 ` Alan Stern
  2010-05-05 21:37   ` Brian Swetland
@ 2010-05-05 21:37   ` Brian Swetland
  1 sibling, 0 replies; 195+ messages in thread
From: Brian Swetland @ 2010-05-05 21:37 UTC (permalink / raw)
  To: Alan Stern
  Cc: Len Brown, linux-doc, markgross, Kernel development list,
	Jesse Barnes, Oleg Nesterov, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Wed, May 5, 2010 at 2:12 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
>>
>> Oh, like tell the modem that user mode has handled the ring event and
>> its ok to un-block?
>
> No, that's not how it works.  It would go like this:
>
>        The modem IRQ handler queues its event to the input subsystem.
>        As it does so the input subsystem enables a suspend blocker,
>        causing the system to stay awake after the IRQ is done.
>
>        The user program enables its own suspend blocker before reading
>        the input queue.  When the queue is empty, the input subsystem
>        releases its suspend blocker.
>
>        When the user program finishes processing the event, it
>        releases its suspend blocker.  Now the system can go back to
>        sleep.
>
> At no point does the user program have to communicate anything to the
> modem driver, and at no point does it have to do anything out of the
> ordinary except to enable and disable a suspend blocker.

Exactly -- and you can use the same style of overlapping suspend
blockers with other drivers than input, if the input interface is not
suitable for the particular interaction.

Brian
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05 20:28 [linux-pm] " mark gross
  2010-05-05 21:12 ` Alan Stern
@ 2010-05-05 21:12 ` Alan Stern
  1 sibling, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-05 21:12 UTC (permalink / raw)
  To: mark gross
  Cc: Len Brown, linux-doc, markgross, Oleg Nesterov, Jesse Barnes,
	Kernel development list, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Wed, 5 May 2010, mark gross wrote:

> > > True, you need an ack back from user mode for when its ok to allow
> > > suspend to happen.  This ack is device specific and needs to be custom
> > > built per product to its wake up sources.
> > 
> > No and no.  Nothing special is needed.  All userspace needs to do is 
> > remove the condition that led to the blocker being enabled initially -- 
> > which is exactly what userspace would do normally anyway.
> 
> Oh, like tell the modem that user mode has handled the ring event and
> its ok to un-block?

No, that's not how it works.  It would go like this:

	The modem IRQ handler queues its event to the input subsystem.  
	As it does so the input subsystem enables a suspend blocker, 
	causing the system to stay awake after the IRQ is done.

	The user program enables its own suspend blocker before reading
	the input queue.  When the queue is empty, the input subsystem
	releases its suspend blocker.

	When the user program finishes processing the event, it
	releases its suspend blocker.  Now the system can go back to
	sleep.

At no point does the user program have to communicate anything to the 
modem driver, and at no point does it have to do anything out of the 
ordinary except to enable and disable a suspend blocker.

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05 15:44     ` [linux-pm] " Alan Stern
@ 2010-05-05 20:28       ` mark gross
  0 siblings, 0 replies; 195+ messages in thread
From: mark gross @ 2010-05-05 20:28 UTC (permalink / raw)
  To: Alan Stern
  Cc: Len Brown, linux-doc, markgross, Oleg Nesterov, Jesse Barnes,
	Kernel development list, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Wed, May 05, 2010 at 11:44:39AM -0400, Alan Stern wrote:
> On Tue, 4 May 2010, mark gross wrote:
> 
> > Thanks, I think I'm starting to get it.  From this it seems that the
> > system integrator needs to identify those wake up sources that need to
> > be able to block a suspend, and figure out a way of acknowledging from
> > user mode, that its now ok to allow a suspend to happen.
> 
> The second part is easy.  Userspace doesn't need to do anything special 
> to acknowledge that a suspend is now okay; it just has to remove the 
> conditions that led the driver to block suspends in the first place.
> 
> For example, if suspends are blocked because some input event has been
> queued, emptying the input event queue should unblock suspends.
> 
> > The rev-6 proposed way is for the integrator to implement overlapping
> > blocker sections from ISR up to user mode for selected wake up devices
> > (i.e. the modem)
> > 
> > There *has* to be a better way.
> 
> Why?  What's wrong with overlapping blockers?  It's a very common
> idiom.  For example, the same sort of thing is used when locking
> subtrees of a tree: You lock the root node, and then use overlapping
> locks on the nodes leading down to the subtree you're interested in.

Because in the kenel there is only a partial ordering of calling
sequences from IRQ to usermode.  I see a lot of custom out of tree code
being developed to deal with getting the overlapping blocker sections
right, per device.

 
> > Can't we have some notification based thing that supports user mode
> > acks through a misc device or sysfs thing?   Anything to avoid the
> > overlapping blocker sections. 
> 
> Userspace acks aren't the issue; the issue is how (and when) kernel 
> drivers should initiate a blocker.  Switching to notifications, misc 
> devices, or sysfs won't help solve this issue.

communicating non-local knowledge back down to the blocking object to
tell it that it can unblock is the issue
 
> > True, you need an ack back from user mode for when its ok to allow
> > suspend to happen.  This ack is device specific and needs to be custom
> > built per product to its wake up sources.
> 
> No and no.  Nothing special is needed.  All userspace needs to do is 
> remove the condition that led to the blocker being enabled initially -- 
> which is exactly what userspace would do normally anyway.

Oh, like tell the modem that user mode has handled the ring event and
its ok to un-block?

--mgross

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05 20:09       ` mark gross
@ 2010-05-05 20:21         ` Matthew Garrett
  0 siblings, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-05 20:21 UTC (permalink / raw)
  To: mark gross
  Cc: Len Brown, linux-doc, markgross, Kernel development list,
	Jesse Barnes, Oleg Nesterov, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Wed, May 05, 2010 at 01:09:06PM -0700, mark gross wrote:
> On Wed, May 05, 2010 at 02:31:31PM +0100, Matthew Garrett wrote:
> > But nobody has reasonably proposed one and demonstrated that it works. 
> > We've had over a year to do so and failed, and I think it's pretty 
> > unreasonable to ask Google to attempt to rearchitect based on a 
> > hypothetical.
> >
> 
> These are not new issues being raised. They've had over a year to
> address them, and all thats really happened was some sed script changes
> from wake_lock to suspend_blocker.  Nothing is really different
> here.

Our issues haven't been addressed because we've given no indication as 
to how they can be addressed. For better or worse, our runtime 
powermanagement story isn't sufficient to satisfy Google's usecases. 
That would be fine, if we could tell them what changes needed to be made 
to satisfy their usecases. The Android people have said that they don't 
see a cleaner way of doing this. Are we seriously saying that they 
should prove themselves wrong, and if they can't they don't get their 
code in the kernel? This seems... problematic.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05 13:31     ` Matthew Garrett
  2010-05-05 20:09       ` mark gross
@ 2010-05-05 20:09       ` mark gross
  1 sibling, 0 replies; 195+ messages in thread
From: mark gross @ 2010-05-05 20:09 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Len Brown, linux-doc, markgross, Kernel development list,
	Jesse Barnes, Oleg Nesterov, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Wed, May 05, 2010 at 02:31:31PM +0100, Matthew Garrett wrote:
> On Tue, May 04, 2010 at 06:50:50PM -0700, mark gross wrote:
> 
> > In my sequence above I had the modem driver "magically" knowing to fail
> > this suspend attempt.  (that "magic" wasn't fully thought out though.)
> 
> If the modem driver knows to "magically" fail a suspend attempt until it 
> knows that userspace has consumed the event, you have something that 
> looks awfully like suspend blockers.
> 
> > There *has* to be a better way.
> 
> But nobody has reasonably proposed one and demonstrated that it works. 
> We've had over a year to do so and failed, and I think it's pretty 
> unreasonable to ask Google to attempt to rearchitect based on a 
> hypothetical.
>

These are not new issues being raised. They've had over a year to
address them, and all thats really happened was some sed script changes
from wake_lock to suspend_blocker.  Nothing is really different
here.

Rearchitecting out of tree code is as silly thing for you to expect from
a community member.  

sigh, lets stop wasting time and just merge it then.

I'm finished with this thread until I do some rearchecting and post
something that looks better to me.  I'll look for this stuff in 2.6.34
or 35.

--mgross 
ps It think the name suspend blocker is worse than wake-lock.  I'd
change it back.

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05  1:50   ` mark gross
  2010-05-05 13:31     ` Matthew Garrett
  2010-05-05 13:31     ` Matthew Garrett
@ 2010-05-05 15:44     ` Alan Stern
  2010-05-05 15:44     ` [linux-pm] " Alan Stern
  3 siblings, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-05 15:44 UTC (permalink / raw)
  To: mark gross
  Cc: Len Brown, linux-doc, markgross, Oleg Nesterov, Jesse Barnes,
	Kernel development list, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Tue, 4 May 2010, mark gross wrote:

> Thanks, I think I'm starting to get it.  From this it seems that the
> system integrator needs to identify those wake up sources that need to
> be able to block a suspend, and figure out a way of acknowledging from
> user mode, that its now ok to allow a suspend to happen.

The second part is easy.  Userspace doesn't need to do anything special 
to acknowledge that a suspend is now okay; it just has to remove the 
conditions that led the driver to block suspends in the first place.

For example, if suspends are blocked because some input event has been
queued, emptying the input event queue should unblock suspends.

> The rev-6 proposed way is for the integrator to implement overlapping
> blocker sections from ISR up to user mode for selected wake up devices
> (i.e. the modem)
> 
> There *has* to be a better way.

Why?  What's wrong with overlapping blockers?  It's a very common
idiom.  For example, the same sort of thing is used when locking
subtrees of a tree: You lock the root node, and then use overlapping
locks on the nodes leading down to the subtree you're interested in.

> Can't we have some notification based thing that supports user mode
> acks through a misc device or sysfs thing?   Anything to avoid the
> overlapping blocker sections. 

Userspace acks aren't the issue; the issue is how (and when) kernel 
drivers should initiate a blocker.  Switching to notifications, misc 
devices, or sysfs won't help solve this issue.

> True, you need an ack back from user mode for when its ok to allow
> suspend to happen.  This ack is device specific and needs to be custom
> built per product to its wake up sources.

No and no.  Nothing special is needed.  All userspace needs to do is 
remove the condition that led to the blocker being enabled initially -- 
which is exactly what userspace would do normally anyway.

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-05  1:50   ` mark gross
  2010-05-05 13:31     ` Matthew Garrett
@ 2010-05-05 13:31     ` Matthew Garrett
  2010-05-05 15:44     ` Alan Stern
  2010-05-05 15:44     ` [linux-pm] " Alan Stern
  3 siblings, 0 replies; 195+ messages in thread
From: Matthew Garrett @ 2010-05-05 13:31 UTC (permalink / raw)
  To: mark gross
  Cc: Len Brown, linux-doc, markgross, Kernel development list,
	Jesse Barnes, Oleg Nesterov, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Tue, May 04, 2010 at 06:50:50PM -0700, mark gross wrote:

> In my sequence above I had the modem driver "magically" knowing to fail
> this suspend attempt.  (that "magic" wasn't fully thought out though.)

If the modem driver knows to "magically" fail a suspend attempt until it 
knows that userspace has consumed the event, you have something that 
looks awfully like suspend blockers.

> There *has* to be a better way.

But nobody has reasonably proposed one and demonstrated that it works. 
We've had over a year to do so and failed, and I think it's pretty 
unreasonable to ask Google to attempt to rearchitect based on a 
hypothetical.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-04 17:16 ` Alan Stern
  2010-05-05  1:50   ` mark gross
@ 2010-05-05  1:50   ` mark gross
  1 sibling, 0 replies; 195+ messages in thread
From: mark gross @ 2010-05-05  1:50 UTC (permalink / raw)
  To: Alan Stern
  Cc: Len Brown, linux-doc, markgross, Oleg Nesterov, Jesse Barnes,
	Kernel development list, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Tue, May 04, 2010 at 01:16:25PM -0400, Alan Stern wrote:
> On Tue, 4 May 2010, mark gross wrote:
> 
> > On Tue, May 04, 2010 at 09:59:59AM -0400, Alan Stern wrote:
> > > On Mon, 3 May 2010, mark gross wrote:
> > > 
> > > > You know things would be so much easier if the policy was a one-shot
> > > > styled thing.  i.e. when enabled it does what it does, but upon resume
> > > > the policy must be re-enabled by user mode to get the opportunistic
> > > > behavior.  That way we don't need to grab the suspend blocker from the
> > > > wake up irq handler all the way up to user mode as the example below
> > > > calls out.  I suppose doing this would put a burden on the user mode code
> > > > to keep track of if it has no pending blockers registered after a wake
> > > > up from the suspend.  but that seems nicer to me than sprinkling
> > > > overlapping blocker critical sections from the mettle up to user mode.
> > > > 
> > > > Please consider making the policy a one shot API that needs to be
> > > > re-enabled after resume by user mode.  That would remove my issue with
> > > > the design.
> > > 
> > > This won't work right if a second IRQ arrives while the first is being
> > > processed.  Suppose the kernel driver for the second IRQ doesn't
> > > activate a suspend blocker, and suppose all the userspace handlers for
> > > the first IRQ end (and the opportunistic policy is re-enabled) before
> > > the userspace handler for the second IRQ can start.  Then the system
> > > will go back to sleep before userspace can handle the second IRQ.
> > >
> > 
> > What?  If the suspend blocker API was a one shot styled api, after the
> > suspend, the one shot is over and the behavior is that you do not fall
> > back into suspend again until user mode re-enables the one-shot
> > behavior.  
> 
> I was referring to your sentence: "That way we don't need to grab the 
> suspend blocker from the wake up irq handler all the way up to user 
> mode..."  The problem arises when kernel handlers don't do _something_ 
> to prevent another suspend from occurring too soon.
> 
> > With what I am proposing the next suspend would not happen until the
> > user mode code re-enables the suspend blocking behavior.  It would much
> > cleaner for everyone and I see zero down side WRT the example you just
> > posted.  
> > 
> > If user mode triggers a suspend by releasing the last blocker, you have
> > until pm_suspend('mem') turns off interrupts to cancel it.  That race
> > will never go away. 
> 
> (Actually the kernel can cancel the suspend even after interrupts are
> turned off, if it has a reason to do so.)
> 
> In theory the race between interrupts and suspend don't need to be a
> problem.  In practice it still is -- the PM core needs a few changes to
> allow wakeup interrupts to cancel a suspend in progress.  Regardless, 
> that's not what I was talking about.
> 
> > Let me think this through, please check that I'm understanding the issue
> > please:
> > 1) one-shot policy enable blocker PM suspend behavior;
> > 2) release last blocker and call pm_suspend('mem') and suspend all the
> > way down.
> > 3) get wake up event, one-shot policy over, no-blockers in list
> > 4) go all the way to user mode, 
> > 5) user mode decides to not grab a blocker, and re-enables one-shot
> > policy
> > 6) pm_suspend('mem') called
> > 7) interrupt comes in (say the modem rings)
> > 8) modem driver handler needs to returns fail from pm_suspend call back,
> > device resumes (I am proposing changing the posted api for doing this.)
> > 9) user mode figures out one-shot needs to be re-enabled and it grabs a
> > blocker to handle the call and re-enables the one-shot.
> 
> This is not the scenario I was describing.  Here's what I had in mind:
> 
> 1) The system is asleep
> 2) Wakeup event occurs, one-shot policy over
> 3) Go all the way to user mode
> 4) A second wakeup interrupt occurs (say the modem rings)
> 5) The modem driver does not enable any suspend blockers
> 6) The modem driver queues an input event for userspace
> 7) The userspace handler invoked during 3) finishes and re-enables
> 	the one-shot policy
> 8) No suspend blockers are enabled, so the system goes to sleep
In my sequence above I had the modem driver "magically" knowing to fail
this suspend attempt.  (that "magic" wasn't fully thought out though.)
> 9) The userspace handler for the input event in 6) doesn't get to run
> 
> > So the real problem grabbing blockers from ISR's trying to solve is to
> > reduce the window of time where a pm_suspend('mem') can be canceled.
> 
> No.  The real problem is how ISRs should prevent the system from
> suspending before the events they generate can be handled by userspace.

Thanks, I think I'm starting to get it.  From this it seems that the
system integrator needs to identify those wake up sources that need to
be able to block a suspend, and figure out a way of acknowledging from
user mode, that its now ok to allow a suspend to happen.

The rev-6 proposed way is for the integrator to implement overlapping
blocker sections from ISR up to user mode for selected wake up devices
(i.e. the modem)

There *has* to be a better way.

Can't we have some notification based thing that supports user mode
acks through a misc device or sysfs thing?   Anything to avoid the
overlapping blocker sections. 


> > My proposal is to;
> > 1) change the kernel blocker api to be
> > "cancel-one-shot-policy-enable" that can be called from the ISR's for
> > the wake up devices before the IRQ's are disabled to cancel an in-flight
> > suspend.  I would make it 2 macros one goes in the pm_suspend callback
> > to return fail, and the other in the ISR to disable the one-shot-policy.
> >  
> > 2) make the policy a one shot that user mode must re-enable after each
> > suspend attempted.
> 
> Neither of these solves the race I described.

True, you need an ack back from user mode for when its ok to allow
suspend to happen.  This ack is device specific and needs to be custom
built per product to its wake up sources.

> 
> > Would it help if I coded up the above proposal as patch to the current
> > (rev-6) of the blocker patchset?  I'm offering.
> > 
> > Because this part of the current design is broken, in that it demands
> > the sprinkling of blocker critical sections from the hardware up to the
> > user mode.  Its ugly, hard to get right and if more folks would take a
> > look at how well it worked out for the existing kernels on
> > android.git.kernel.org/kernels/ perhaps it would get more attention.
> 
> I agree, it is ugly and probably hard to get right.  But something like 
> it is necessary to solve this race.
> 
> > Finally, as an aside, lets look at the problem with the posted rev-6
> > patches:
> > 1) enable suspend blocker policy
> > 2) release user-mode blocker
> > 3) start suspend
> > 4) get a modem interrupt (incoming call)
> > 5) ooops, IRQ came in after pm_suspend('mem') turned off interrupts.
> > bummer for you, thats life with this design.  Better hope your modem
> > hardware is modified to keep pulling on that wake up line because you're
> > past the point of no return now and going to suspend.
> 
> That is not a problem for level-sensitive IRQs.  If interrupts have
> been turned off then the modem will not receive any commands telling
> it to stop requesting an interrupt.  So the IRQ line will remain active 
> until the system goes to sleep, at which point it will immediately 
> wake up the system.
> 
> For edge-sensitive interrupts the situation isn't as simple.  The
> low-level IRQ code must handle this somehow.
> 
> > Grabbing a blocker in the ISR doesn't solve this.  So your hardware
> > needs to have a persistent wake up trigger that is robust against this
> > scenario.  And, if you have such hardware then why are we killing
> > ourselves to maximize the window of time between pm_suspend('mem') and
> > platform suspend where you can cancel the suspend?  The hardware will
> > simply wake up anyway.
> 
> That's not what we are killing ourselves for.  The point of suspend
> blockers is not to prevent races with the hardware.  It is to prevent 
> userspace from being frozen while there's still work to be done.
ok.

I'm going to think on this some more.  There must be a cleaner way to do
this.

--mgross
 

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-04  5:12   ` [linux-pm] " mark gross
  2010-05-04 13:59     ` Alan Stern
  2010-05-04 13:59     ` [linux-pm] " Alan Stern
@ 2010-05-04 20:40     ` Arve Hjønnevåg
  2 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-04 20:40 UTC (permalink / raw)
  To: markgross
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes, linux-kernel,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

2010/5/3 mark gross <640e9920@gmail.com>:
> On Fri, Apr 30, 2010 at 03:36:54PM -0700, Arve Hjønnevåg wrote:
...
>> +When the policy is "opportunisic", there is a special value, "on", that can be
>> +written to /sys/power/state. This will block the automatic sleep request, as if
>> +a suspend blocker was used by a device driver. This way the opportunistic
>> +suspend may be blocked by user space whithout switching back to the "forced"
>> +mode.
>
> You know things would be so much easier if the policy was a one-shot
> styled thing.  i.e. when enabled it does what it does, but upon resume
> the policy must be re-enabled by user mode to get the opportunistic
> behavior.  That way we don't need to grab the suspend blocker from the
> wake up irq handler all the way up to user mode as the example below
> calls out.  I suppose doing this would put a burden on the user mode code
> to keep track of if it has no pending blockers registered after a wake
> up from the suspend.  but that seems nicer to me than sprinkling
> overlapping blocker critical sections from the mettle up to user mode.
>
> Please consider making the policy a one shot API that needs to be
> re-enabled after resume by user mode.  That would remove my issue with
> the design.
>

Making it one shot does not change where kernel code needs to block
suspend, but it does force user space to poll trying to suspend while
suspend is blocked by a driver.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-04 16:03 [linux-pm] " mark gross
  2010-05-04 17:16 ` Alan Stern
@ 2010-05-04 17:16 ` Alan Stern
  1 sibling, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-04 17:16 UTC (permalink / raw)
  To: mark gross
  Cc: Len Brown, linux-doc, markgross, Oleg Nesterov, Jesse Barnes,
	Kernel development list, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Tue, 4 May 2010, mark gross wrote:

> On Tue, May 04, 2010 at 09:59:59AM -0400, Alan Stern wrote:
> > On Mon, 3 May 2010, mark gross wrote:
> > 
> > > You know things would be so much easier if the policy was a one-shot
> > > styled thing.  i.e. when enabled it does what it does, but upon resume
> > > the policy must be re-enabled by user mode to get the opportunistic
> > > behavior.  That way we don't need to grab the suspend blocker from the
> > > wake up irq handler all the way up to user mode as the example below
> > > calls out.  I suppose doing this would put a burden on the user mode code
> > > to keep track of if it has no pending blockers registered after a wake
> > > up from the suspend.  but that seems nicer to me than sprinkling
> > > overlapping blocker critical sections from the mettle up to user mode.
> > > 
> > > Please consider making the policy a one shot API that needs to be
> > > re-enabled after resume by user mode.  That would remove my issue with
> > > the design.
> > 
> > This won't work right if a second IRQ arrives while the first is being
> > processed.  Suppose the kernel driver for the second IRQ doesn't
> > activate a suspend blocker, and suppose all the userspace handlers for
> > the first IRQ end (and the opportunistic policy is re-enabled) before
> > the userspace handler for the second IRQ can start.  Then the system
> > will go back to sleep before userspace can handle the second IRQ.
> >
> 
> What?  If the suspend blocker API was a one shot styled api, after the
> suspend, the one shot is over and the behavior is that you do not fall
> back into suspend again until user mode re-enables the one-shot
> behavior.  

I was referring to your sentence: "That way we don't need to grab the 
suspend blocker from the wake up irq handler all the way up to user 
mode..."  The problem arises when kernel handlers don't do _something_ 
to prevent another suspend from occurring too soon.

> With what I am proposing the next suspend would not happen until the
> user mode code re-enables the suspend blocking behavior.  It would much
> cleaner for everyone and I see zero down side WRT the example you just
> posted.  
> 
> If user mode triggers a suspend by releasing the last blocker, you have
> until pm_suspend('mem') turns off interrupts to cancel it.  That race
> will never go away. 

(Actually the kernel can cancel the suspend even after interrupts are
turned off, if it has a reason to do so.)

In theory the race between interrupts and suspend don't need to be a
problem.  In practice it still is -- the PM core needs a few changes to
allow wakeup interrupts to cancel a suspend in progress.  Regardless, 
that's not what I was talking about.

> Let me think this through, please check that I'm understanding the issue
> please:
> 1) one-shot policy enable blocker PM suspend behavior;
> 2) release last blocker and call pm_suspend('mem') and suspend all the
> way down.
> 3) get wake up event, one-shot policy over, no-blockers in list
> 4) go all the way to user mode, 
> 5) user mode decides to not grab a blocker, and re-enables one-shot
> policy
> 6) pm_suspend('mem') called
> 7) interrupt comes in (say the modem rings)
> 8) modem driver handler needs to returns fail from pm_suspend call back,
> device resumes (I am proposing changing the posted api for doing this.)
> 9) user mode figures out one-shot needs to be re-enabled and it grabs a
> blocker to handle the call and re-enables the one-shot.

This is not the scenario I was describing.  Here's what I had in mind:

1) The system is asleep
2) Wakeup event occurs, one-shot policy over
3) Go all the way to user mode
4) A second wakeup interrupt occurs (say the modem rings)
5) The modem driver does not enable any suspend blockers
6) The modem driver queues an input event for userspace
7) The userspace handler invoked during 3) finishes and re-enables
	the one-shot policy
8) No suspend blockers are enabled, so the system goes to sleep
9) The userspace handler for the input event in 6) doesn't get to run

> So the real problem grabbing blockers from ISR's trying to solve is to
> reduce the window of time where a pm_suspend('mem') can be canceled.

No.  The real problem is how ISRs should prevent the system from
suspending before the events they generate can be handled by userspace.

> My proposal is to;
> 1) change the kernel blocker api to be
> "cancel-one-shot-policy-enable" that can be called from the ISR's for
> the wake up devices before the IRQ's are disabled to cancel an in-flight
> suspend.  I would make it 2 macros one goes in the pm_suspend callback
> to return fail, and the other in the ISR to disable the one-shot-policy.
>  
> 2) make the policy a one shot that user mode must re-enable after each
> suspend attempted.

Neither of these solves the race I described.

> Would it help if I coded up the above proposal as patch to the current
> (rev-6) of the blocker patchset?  I'm offering.
> 
> Because this part of the current design is broken, in that it demands
> the sprinkling of blocker critical sections from the hardware up to the
> user mode.  Its ugly, hard to get right and if more folks would take a
> look at how well it worked out for the existing kernels on
> android.git.kernel.org/kernels/ perhaps it would get more attention.

I agree, it is ugly and probably hard to get right.  But something like 
it is necessary to solve this race.

> Finally, as an aside, lets look at the problem with the posted rev-6
> patches:
> 1) enable suspend blocker policy
> 2) release user-mode blocker
> 3) start suspend
> 4) get a modem interrupt (incoming call)
> 5) ooops, IRQ came in after pm_suspend('mem') turned off interrupts.
> bummer for you, thats life with this design.  Better hope your modem
> hardware is modified to keep pulling on that wake up line because you're
> past the point of no return now and going to suspend.

That is not a problem for level-sensitive IRQs.  If interrupts have
been turned off then the modem will not receive any commands telling
it to stop requesting an interrupt.  So the IRQ line will remain active 
until the system goes to sleep, at which point it will immediately 
wake up the system.

For edge-sensitive interrupts the situation isn't as simple.  The
low-level IRQ code must handle this somehow.

> Grabbing a blocker in the ISR doesn't solve this.  So your hardware
> needs to have a persistent wake up trigger that is robust against this
> scenario.  And, if you have such hardware then why are we killing
> ourselves to maximize the window of time between pm_suspend('mem') and
> platform suspend where you can cancel the suspend?  The hardware will
> simply wake up anyway.

That's not what we are killing ourselves for.  The point of suspend
blockers is not to prevent races with the hardware.  It is to prevent 
userspace from being frozen while there's still work to be done.

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-04 13:59     ` [linux-pm] " Alan Stern
@ 2010-05-04 16:03       ` mark gross
  0 siblings, 0 replies; 195+ messages in thread
From: mark gross @ 2010-05-04 16:03 UTC (permalink / raw)
  To: Alan Stern
  Cc: Len Brown, linux-doc, markgross, Oleg Nesterov, Jesse Barnes,
	linux-kernel, Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang,
	Andrew Morton

On Tue, May 04, 2010 at 09:59:59AM -0400, Alan Stern wrote:
> On Mon, 3 May 2010, mark gross wrote:
> 
> > You know things would be so much easier if the policy was a one-shot
> > styled thing.  i.e. when enabled it does what it does, but upon resume
> > the policy must be re-enabled by user mode to get the opportunistic
> > behavior.  That way we don't need to grab the suspend blocker from the
> > wake up irq handler all the way up to user mode as the example below
> > calls out.  I suppose doing this would put a burden on the user mode code
> > to keep track of if it has no pending blockers registered after a wake
> > up from the suspend.  but that seems nicer to me than sprinkling
> > overlapping blocker critical sections from the mettle up to user mode.
> > 
> > Please consider making the policy a one shot API that needs to be
> > re-enabled after resume by user mode.  That would remove my issue with
> > the design.
> 
> This won't work right if a second IRQ arrives while the first is being
> processed.  Suppose the kernel driver for the second IRQ doesn't
> activate a suspend blocker, and suppose all the userspace handlers for
> the first IRQ end (and the opportunistic policy is re-enabled) before
> the userspace handler for the second IRQ can start.  Then the system
> will go back to sleep before userspace can handle the second IRQ.
>

What?  If the suspend blocker API was a one shot styled api, after the
suspend, the one shot is over and the behavior is that you do not fall
back into suspend again until user mode re-enables the one-shot
behavior.  

With what I am proposing the next suspend would not happen until the
user mode code re-enables the suspend blocking behavior.  It would much
cleaner for everyone and I see zero down side WRT the example you just
posted.  

If user mode triggers a suspend by releasing the last blocker, you have
until pm_suspend('mem') turns off interrupts to cancel it.  That race
will never go away. 

Let me think this through, please check that I'm understanding the issue
please:
1) one-shot policy enable blocker PM suspend behavior;
2) release last blocker and call pm_suspend('mem') and suspend all the
way down.
3) get wake up event, one-shot policy over, no-blockers in list
4) go all the way to user mode, 
5) user mode decides to not grab a blocker, and re-enables one-shot
policy
6) pm_suspend('mem') called
7) interrupt comes in (say the modem rings)
8) modem driver handler needs to returns fail from pm_suspend call back,
device resumes (I am proposing changing the posted api for doing this.)
9) user mode figures out one-shot needs to be re-enabled and it grabs a
blocker to handle the call and re-enables the one-shot.

So the real problem grabbing blockers from ISR's trying to solve is to
reduce the window of time where a pm_suspend('mem') can be canceled.

My proposal is to;
1) change the kernel blocker api to be
"cancel-one-shot-policy-enable" that can be called from the ISR's for
the wake up devices before the IRQ's are disabled to cancel an in-flight
suspend.  I would make it 2 macros one goes in the pm_suspend callback
to return fail, and the other in the ISR to disable the one-shot-policy.
 
2) make the policy a one shot that user mode must re-enable after each
suspend attempted.

Would it help if I coded up the above proposal as patch to the current
(rev-6) of the blocker patchset?  I'm offering.

Because this part of the current design is broken, in that it demands
the sprinkling of blocker critical sections from the hardware up to the
user mode.  Its ugly, hard to get right and if more folks would take a
look at how well it worked out for the existing kernels on
android.git.kernel.org/kernels/ perhaps it would get more attention.

Finally, as an aside, lets look at the problem with the posted rev-6
patches:
1) enable suspend blocker policy
2) release user-mode blocker
3) start suspend
4) get a modem interrupt (incoming call)
5) ooops, IRQ came in after pm_suspend('mem') turned off interrupts.
bummer for you, thats life with this design.  Better hope your modem
hardware is modified to keep pulling on that wake up line because you're
past the point of no return now and going to suspend.

Grabbing a blocker in the ISR doesn't solve this.  So your hardware
needs to have a persistent wake up trigger that is robust against this
scenario.  And, if you have such hardware then why are we killing
ourselves to maximize the window of time between pm_suspend('mem') and
platform suspend where you can cancel the suspend?  The hardware will
simply wake up anyway.

(further, on my hardware I would modify the platform suspend call back
to make one last check to see if an IRQ came in, and cancel the suspend
there as a last chance.)

 
--mgross

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-04  5:12   ` [linux-pm] " mark gross
@ 2010-05-04 13:59     ` Alan Stern
  2010-05-04 13:59     ` [linux-pm] " Alan Stern
  2010-05-04 20:40     ` Arve Hjønnevåg
  2 siblings, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-04 13:59 UTC (permalink / raw)
  To: markgross
  Cc: Len Brown, linux-doc, linux-kernel, Jesse Barnes, Oleg Nesterov,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Mon, 3 May 2010, mark gross wrote:

> You know things would be so much easier if the policy was a one-shot
> styled thing.  i.e. when enabled it does what it does, but upon resume
> the policy must be re-enabled by user mode to get the opportunistic
> behavior.  That way we don't need to grab the suspend blocker from the
> wake up irq handler all the way up to user mode as the example below
> calls out.  I suppose doing this would put a burden on the user mode code
> to keep track of if it has no pending blockers registered after a wake
> up from the suspend.  but that seems nicer to me than sprinkling
> overlapping blocker critical sections from the mettle up to user mode.
> 
> Please consider making the policy a one shot API that needs to be
> re-enabled after resume by user mode.  That would remove my issue with
> the design.

This won't work right if a second IRQ arrives while the first is being
processed.  Suppose the kernel driver for the second IRQ doesn't
activate a suspend blocker, and suppose all the userspace handlers for
the first IRQ end (and the opportunistic policy is re-enabled) before
the userspace handler for the second IRQ can start.  Then the system
will go back to sleep before userspace can handle the second IRQ.

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-30 22:36 ` Arve Hjønnevåg
                     ` (4 preceding siblings ...)
  2010-05-04  5:12   ` [linux-pm] " mark gross
@ 2010-05-04  5:12   ` mark gross
  2010-05-13 19:01   ` Paul Walmsley
  6 siblings, 0 replies; 195+ messages in thread
From: mark gross @ 2010-05-04  5:12 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes, linux-kernel,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Fri, Apr 30, 2010 at 03:36:54PM -0700, Arve Hjønnevåg wrote:
> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.
> 
> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> ---
>  Documentation/power/opportunistic-suspend.txt |  119 +++++++++++
>  include/linux/suspend_blocker.h               |   64 ++++++
>  kernel/power/Kconfig                          |   16 ++
>  kernel/power/Makefile                         |    1 +
>  kernel/power/main.c                           |   92 ++++++++-
>  kernel/power/power.h                          |    9 +
>  kernel/power/suspend.c                        |    4 +-
>  kernel/power/suspend_blocker.c                |  261 +++++++++++++++++++++++++
>  8 files changed, 559 insertions(+), 7 deletions(-)
>  create mode 100644 Documentation/power/opportunistic-suspend.txt
>  create mode 100755 include/linux/suspend_blocker.h
>  create mode 100644 kernel/power/suspend_blocker.c
> 
> diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
> new file mode 100644
> index 0000000..3d060e8
> --- /dev/null
> +++ b/Documentation/power/opportunistic-suspend.txt
> @@ -0,0 +1,119 @@
> +Opportunistic Suspend
> +=====================
> +
> +Opportunistic suspend is a feature allowing the system to be suspended (ie. put
> +into one of the available sleep states) automatically whenever it is regarded
> +as idle.  The suspend blockers framework described below is used to determine
> +when that happens.
> +
> +The /sys/power/policy sysfs attribute is used to switch the system between the
> +opportunistic and "forced" suspend behavior, where in the latter case the
> +system is only suspended if a specific value, corresponding to one of the
> +available system sleep states, is written into /sys/power/state.  However, in
> +the former, opportunistic, case the system is put into the sleep state
> +corresponding to the value written to /sys/power/state whenever there are no
> +active suspend blockers. The default policy is "forced". Also, suspend blockers
> +do not affect sleep states entered from idle.
> +
> +When the policy is "opportunisic", there is a special value, "on", that can be
> +written to /sys/power/state. This will block the automatic sleep request, as if
> +a suspend blocker was used by a device driver. This way the opportunistic
> +suspend may be blocked by user space whithout switching back to the "forced"
> +mode.

You know things would be so much easier if the policy was a one-shot
styled thing.  i.e. when enabled it does what it does, but upon resume
the policy must be re-enabled by user mode to get the opportunistic
behavior.  That way we don't need to grab the suspend blocker from the
wake up irq handler all the way up to user mode as the example below
calls out.  I suppose doing this would put a burden on the user mode code
to keep track of if it has no pending blockers registered after a wake
up from the suspend.  but that seems nicer to me than sprinkling
overlapping blocker critical sections from the mettle up to user mode.

Please consider making the policy a one shot API that needs to be
re-enabled after resume by user mode.  That would remove my issue with
the design.

--mgross

> +
> +A suspend blocker is an object used to inform the PM subsystem when the system
> +can or cannot be suspended in the "opportunistic" mode (the "forced" mode
> +ignores suspend blockers).  To use it, a device driver creates a struct
> +suspend_blocker that must be initialized with suspend_blocker_init(). Before
> +freeing the suspend_blocker structure or its name, suspend_blocker_destroy()
> +must be called on it.
> +
> +A suspend blocker is activated using suspend_block(), which prevents the PM
> +subsystem from putting the system into the requested sleep state in the
> +"opportunistic" mode until the suspend blocker is deactivated with
> +suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
> +the system will not suspend as long as at least one of them is active.
> +
> +If opportunistic suspend is already in progress when suspend_block() is called,
> +it will abort the suspend, unless suspend_ops->enter has already been
> +executed. If suspend is aborted this way, the system is usually not fully
> +operational at that point. The suspend callbacks of some drivers may still be
> +running and it usually takes time to restore the system to the fully operational
> +state.
> +
> +Here's an example showing how a cell phone or other embedded system can handle
> +keystrokes (or other input events) in the presence of suspend blockers. Use
> +set_irq_wake or a platform specific api to make sure the keypad interrupt wakes
> +up the cpu. Once the keypad driver has resumed, the sequence of events can look
> +like this:
> +
> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> +- The keypad-scan code detects a key change and reports it to the input-event
> +  driver.
> +- The input-event driver sees the key change, enqueues an event, and calls
> +  suspend_block on the input-event-queue suspend_blocker.
> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> +  on the keypad-scan suspend_blocker.
> +- The user-space input-event thread returns from select/poll, calls
> +  suspend_block on the process-input-events suspend_blocker and then calls read
> +  on the input-event device.
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> +- The user-space input-event thread returns from read. If it determines that
> +  the key should be ignored, it calls suspend_unblock on the
> +  process_input_events suspend_blocker and then calls select or poll. The
> +  system will automatically suspend again, since now no suspend blockers are
> +  active.
> +
> +If the key that was pressed instead should preform a simple action (for example,
> +adjusting the volume), this action can be performed right before calling
> +suspend_unblock on the process_input_events suspend_blocker. However, if the key
> +triggers a longer-running action, that action needs its own suspend_blocker and
> +suspend_block must be called on that suspend blocker before calling
> +suspend_unblock on the process_input_events suspend_blocker.
> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++
> +
> +
> +Driver API
> +==========
> +
> +A driver can use the suspend block api by adding a suspend_blocker variable to
> +its state and calling suspend_blocker_init. For instance:
> +struct state {
> +	struct suspend_blocker suspend_blocker;
> +}
> +
> +init() {
> +	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
> +}
> +
> +Before freeing the memory, suspend_blocker_destroy must be called:
> +
> +uninit() {
> +	suspend_blocker_destroy(&state->suspend_blocker);
> +}
> +
> +When the driver determines that it needs to run (usually in an interrupt
> +handler) it calls suspend_block:
> +	suspend_block(&state->suspend_blocker);
> +
> +When it no longer needs to run it calls suspend_unblock:
> +	suspend_unblock(&state->suspend_blocker);
> +
> +Calling suspend_block when the suspend blocker is active or suspend_unblock when
> +it is not active has no effect (i.e., these functions don't nest). This allows
> +drivers to update their state and call suspend suspend_block or suspend_unblock
> +based on the result.
> +For instance:
> +
> +if (list_empty(&state->pending_work))
> +	suspend_unblock(&state->suspend_blocker);
> +else
> +	suspend_block(&state->suspend_blocker);
> +
> diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
> new file mode 100755
> index 0000000..f9928cc
> --- /dev/null
> +++ b/include/linux/suspend_blocker.h
> @@ -0,0 +1,64 @@
> +/* include/linux/suspend_blocker.h
> + *
> + * Copyright (C) 2007-2009 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _LINUX_SUSPEND_BLOCKER_H
> +#define _LINUX_SUSPEND_BLOCKER_H
> +
> +#include <linux/list.h>
> +
> +/**
> + * struct suspend_blocker - the basic suspend_blocker structure
> + * @link:	List entry for active or inactive list.
> + * @flags:	Tracks initialized and active state.
> + * @name:	Name used for debugging.
> + *
> + * When a suspend_blocker is active it prevents the system from entering
> + * opportunistic suspend.
> + *
> + * The suspend_blocker structure must be initialized by suspend_blocker_init()
> + */
> +
> +struct suspend_blocker {
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	struct list_head    link;
> +	int                 flags;
> +	const char         *name;
> +#endif
> +};
> +
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
> +void suspend_blocker_destroy(struct suspend_blocker *blocker);
> +void suspend_block(struct suspend_blocker *blocker);
> +void suspend_unblock(struct suspend_blocker *blocker);
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker);
> +bool suspend_is_blocked(void);
> +
> +#else
> +
> +static inline void suspend_blocker_init(struct suspend_blocker *blocker,
> +					const char *name) {}
> +static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
> +static inline void suspend_block(struct suspend_blocker *blocker) {}
> +static inline void suspend_unblock(struct suspend_blocker *blocker) {}
> +static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
> +								{ return 0; }
> +static inline bool suspend_is_blocked(void) { return 0; }
> +
> +#endif
> +
> +#endif
> +
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 5c36ea9..55a06a1 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -130,6 +130,22 @@ config SUSPEND_FREEZER
>  
>  	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
>  
> +config OPPORTUNISTIC_SUSPEND
> +	bool "Suspend blockers"
> +	depends on PM_SLEEP
> +	select RTC_LIB
> +	default n
> +	---help---
> +	  Opportunistic sleep support.  Allows the system to be put into a sleep
> +	  state opportunistically, if it doesn't do any useful work at the
> +	  moment. The PM subsystem is switched into this mode of operation by
> +	  writing "opportunistic" into /sys/power/policy, while writing
> +	  "forced" to this file turns the opportunistic suspend feature off.
> +	  In the "opportunistic" mode suspend blockers are used to determine
> +	  when to suspend the system and the value written to /sys/power/state
> +	  determines the sleep state the system will be put into when there are
> +	  no active suspend blockers.
> +
>  config HIBERNATION_NVS
>  	bool
>  
> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
> index 4319181..ee5276d 100644
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
>  obj-$(CONFIG_PM_SLEEP)		+= console.o
>  obj-$(CONFIG_FREEZER)		+= process.o
>  obj-$(CONFIG_SUSPEND)		+= suspend.o
> +obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= suspend_blocker.o
>  obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
>  obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
>  obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
> diff --git a/kernel/power/main.c b/kernel/power/main.c
> index b58800b..030ecdc 100644
> --- a/kernel/power/main.c
> +++ b/kernel/power/main.c
> @@ -12,6 +12,7 @@
>  #include <linux/string.h>
>  #include <linux/resume-trace.h>
>  #include <linux/workqueue.h>
> +#include <linux/suspend_blocker.h>
>  
>  #include "power.h"
>  
> @@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
>  unsigned int pm_flags;
>  EXPORT_SYMBOL(pm_flags);
>  
> +struct policy {
> +	const char *name;
> +	bool (*valid_state)(suspend_state_t state);
> +	int (*set_state)(suspend_state_t state);
> +};
> +static struct policy policies[] = {
> +	{
> +		.name		= "forced",
> +		.valid_state	= valid_state,
> +		.set_state	= enter_state,
> +	},
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	{
> +		.name		= "opportunistic",
> +		.valid_state	= request_suspend_valid_state,
> +		.set_state	= request_suspend_state,
> +	},
> +#endif
> +};
> +static int policy;
> +
>  #ifdef CONFIG_PM_SLEEP
>  
>  /* Routines for PM-transition notifications */
> @@ -146,6 +168,12 @@ struct kobject *power_kobj;
>   *
>   *	store() accepts one of those strings, translates it into the 
>   *	proper enumerated value, and initiates a suspend transition.
> + *
> + *	If policy is set to opportunistic, store() does not block until the
> + *	system resumes, and it will try to re-enter the state until another
> + *	state is requested. Suspend blockers are respected and the requested
> + *	state will only be entered when no suspend blockers are active.
> + *	Write "on" to cancel.
>   */
>  static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  			  char *buf)
> @@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  	int i;
>  
>  	for (i = 0; i < PM_SUSPEND_MAX; i++) {
> -		if (pm_states[i] && valid_state(i))
> +		if (pm_states[i] && policies[policy].valid_state(i))
>  			s += sprintf(s,"%s ", pm_states[i]);
>  	}
>  #endif
>  #ifdef CONFIG_HIBERNATION
> -	s += sprintf(s, "%s\n", "disk");
> +	if (!policy)
> +		s += sprintf(s, "%s\n", "disk");
>  #else
>  	if (s != buf)
>  		/* convert the last space to a newline */
> @@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			   const char *buf, size_t n)
>  {
>  #ifdef CONFIG_SUSPEND
> -	suspend_state_t state = PM_SUSPEND_STANDBY;
> +	suspend_state_t state = PM_SUSPEND_ON;
>  	const char * const *s;
>  #endif
>  	char *p;
> @@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  	len = p ? p - buf : n;
>  
>  	/* First, check if we are requested to hibernate */
> -	if (len == 4 && !strncmp(buf, "disk", len)) {
> +	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
>  		error = hibernate();
>    goto Exit;
>  	}
> @@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			break;
>  	}
>  	if (state < PM_SUSPEND_MAX && *s)
> -		error = enter_state(state);
> +		error = policies[policy].set_state(state);
>  #endif
>  
>   Exit:
> @@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  
>  power_attr(state);
>  
> +/**
> + *	policy - set policy for state
> + */
> +
> +static ssize_t policy_show(struct kobject *kobj,
> +			   struct kobj_attribute *attr, char *buf)
> +{
> +	char *s = buf;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		if (i == policy)
> +			s += sprintf(s, "[%s] ", policies[i].name);
> +		else
> +			s += sprintf(s, "%s ", policies[i].name);
> +	}
> +	if (s != buf)
> +		/* convert the last space to a newline */
> +		*(s-1) = '\n';
> +	return (s - buf);
> +}
> +
> +static ssize_t policy_store(struct kobject *kobj,
> +			    struct kobj_attribute *attr,
> +			    const char *buf, size_t n)
> +{
> +	const char *s;
> +	char *p;
> +	int len;
> +	int i;
> +
> +	p = memchr(buf, '\n', n);
> +	len = p ? p - buf : n;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		s = policies[i].name;
> +		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
> +			mutex_lock(&pm_mutex);
> +			policies[policy].set_state(PM_SUSPEND_ON);
> +			policy = i;
> +			mutex_unlock(&pm_mutex);
> +			return n;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +power_attr(policy);
> +
>  #ifdef CONFIG_PM_TRACE
>  int pm_trace_enabled;
>  
> @@ -231,6 +309,7 @@ power_attr(pm_trace);
>  
>  static struct attribute * g[] = {
>  	&state_attr.attr,
> +	&policy_attr.attr,
>  #ifdef CONFIG_PM_TRACE
>  	&pm_trace_attr.attr,
>  #endif
> @@ -247,7 +326,7 @@ static struct attribute_group attr_group = {
>  	.attrs = g,
>  };
>  
> -#ifdef CONFIG_PM_RUNTIME
> +#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_OPPORTUNISTIC_SUSPEND)
>  struct workqueue_struct *pm_wq;
>  EXPORT_SYMBOL_GPL(pm_wq);
>  
> @@ -266,6 +345,7 @@ static int __init pm_init(void)
>  	int error = pm_start_workqueue();
>  	if (error)
>  		return error;
> +	suspend_block_init();
>  	power_kobj = kobject_create_and_add("power", NULL);
>  	if (!power_kobj)
>  		return -ENOMEM;
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index 46c5a26..67d10fd 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -236,3 +236,12 @@ static inline void suspend_thaw_processes(void)
>  {
>  }
>  #endif
> +
> +/* kernel/power/suspend_block.c */
> +extern int request_suspend_state(suspend_state_t state);
> +extern bool request_suspend_valid_state(suspend_state_t state);
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +extern void __init suspend_block_init(void);
> +#else
> +static inline void suspend_block_init(void) {}
> +#endif
> diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
> index 56e7dbb..dc42006 100644
> --- a/kernel/power/suspend.c
> +++ b/kernel/power/suspend.c
> @@ -16,10 +16,12 @@
>  #include <linux/cpu.h>
>  #include <linux/syscalls.h>
>  #include <linux/gfp.h>
> +#include <linux/suspend_blocker.h>
>  
>  #include "power.h"
>  
>  const char *const pm_states[PM_SUSPEND_MAX] = {
> +	[PM_SUSPEND_ON]		= "on",
>  	[PM_SUSPEND_STANDBY]	= "standby",
>  	[PM_SUSPEND_MEM]	= "mem",
>  };
> @@ -157,7 +159,7 @@ static int suspend_enter(suspend_state_t state)
>  
>  	error = sysdev_suspend(PMSG_SUSPEND);
>  	if (!error) {
> -		if (!suspend_test(TEST_CORE))
> +		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
>  			error = suspend_ops->enter(state);
>  		sysdev_resume();
>  	}
> diff --git a/kernel/power/suspend_blocker.c b/kernel/power/suspend_blocker.c
> new file mode 100644
> index 0000000..2c58b21
> --- /dev/null
> +++ b/kernel/power/suspend_blocker.c
> @@ -0,0 +1,261 @@
> +/* kernel/power/suspend_blocker.c
> + *
> + * Copyright (C) 2005-2010 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/suspend.h>
> +#include <linux/suspend_blocker.h>
> +#include "power.h"
> +
> +extern struct workqueue_struct *pm_wq;
> +
> +enum {
> +	DEBUG_EXIT_SUSPEND = 1U << 0,
> +	DEBUG_WAKEUP = 1U << 1,
> +	DEBUG_USER_STATE = 1U << 2,
> +	DEBUG_SUSPEND = 1U << 3,
> +	DEBUG_SUSPEND_BLOCKER = 1U << 4,
> +};
> +static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
> +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
> +
> +#define SB_INITIALIZED            (1U << 8)
> +#define SB_ACTIVE                 (1U << 9)
> +
> +static DEFINE_SPINLOCK(list_lock);
> +static DEFINE_SPINLOCK(state_lock);
> +static LIST_HEAD(inactive_blockers);
> +static LIST_HEAD(active_blockers);
> +static int current_event_num;
> +struct suspend_blocker main_suspend_blocker;
> +static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
> +static bool enable_suspend_blockers;
> +
> +#define pr_info_time(fmt, args...) \
> +	do { \
> +		struct timespec ts; \
> +		struct rtc_time tm; \
> +		getnstimeofday(&ts); \
> +		rtc_time_to_tm(ts.tv_sec, &tm); \
> +		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
> +			args, \
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
> +	} while (0);
> +
> +static void print_active_blockers_locked(void)
> +{
> +	struct suspend_blocker *blocker;
> +
> +	list_for_each_entry(blocker, &active_blockers, link)
> +		pr_info("active suspend blocker %s\n", blocker->name);
> +}
> +
> +/**
> + * suspend_is_blocked() - Check if suspend should be blocked
> + *
> + * suspend_is_blocked can be used by generic power management code to abort
> + * suspend.
> + *
> + * To preserve backward compatibility suspend_is_blocked returns 0 unless it
> + * is called during suspend initiated from the suspend_block code.
> + */
> +bool suspend_is_blocked(void)
> +{
> +	if (!enable_suspend_blockers)
> +		return 0;
> +	return !list_empty(&active_blockers);
> +}
> +
> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = true;
> +	while (!suspend_is_blocked()) {
> +		entry_event_num = current_event_num;
> +
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: enter suspend\n");
> +
> +		ret = pm_suspend(requested_suspend_state);
> +
> +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> +
> +		if (current_event_num == entry_event_num)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +	}
> +	enable_suspend_blockers = false;
> +}
> +static DECLARE_WORK(suspend_work, suspend_worker);
> +
> +/**
> + * suspend_blocker_init() - Initialize a suspend blocker
> + * @blocker:	The suspend blocker to initialize.
> + * @name:	The name of the suspend blocker to show in debug messages.
> + *
> + * The suspend blocker struct and name must not be freed before calling
> + * suspend_blocker_destroy.
> + */
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
> +{
> +	unsigned long irqflags = 0;
> +
> +	WARN_ON(!name);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_init name=%s\n", name);
> +
> +	blocker->name = name;
> +	blocker->flags = SB_INITIALIZED;
> +	INIT_LIST_HEAD(&blocker->link);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	list_add(&blocker->link, &inactive_blockers);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_init);
> +
> +/**
> + * suspend_blocker_destroy() - Destroy a suspend blocker
> + * @blocker:	The suspend blocker to destroy.
> + */
> +void suspend_blocker_destroy(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags &= ~SB_INITIALIZED;
> +	list_del(&blocker->link);
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(pm_wq, &suspend_work);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_destroy);
> +
> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +
> +	blocker->flags |= SB_ACTIVE;
> +	list_move(&blocker->link, &active_blockers);
> +	current_event_num++;
> +
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_block);
> +
> +/**
> + * suspend_unblock() - Unblock suspend
> + * @blocker:	The suspend blocker to unblock.
> + *
> + * If no other suspend blockers block suspend, the system will suspend.
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_unblock(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_unblock: %s\n", blocker->name);
> +
> +	list_move(&blocker->link, &inactive_blockers);
> +
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(pm_wq, &suspend_work);
> +	blocker->flags &= ~(SB_ACTIVE);
> +	if (blocker == &main_suspend_blocker) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			print_active_blockers_locked();
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_unblock);
> +
> +/**
> + * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
> + * @blocker:	The suspend blocker to check.
> + *
> + * Returns true if the suspend_blocker is currently active.
> + */
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker)
> +{
> +	WARN_ON(!(blocker->flags & SB_INITIALIZED));
> +
> +	return !!(blocker->flags & SB_ACTIVE);
> +}
> +EXPORT_SYMBOL(suspend_blocker_is_active);
> +
> +bool request_suspend_valid_state(suspend_state_t state)
> +{
> +	return (state == PM_SUSPEND_ON) || valid_state(state);
> +}
> +
> +int request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +
> +	if (!request_suspend_valid_state(state))
> +		return -ENODEV;
> +
> +	spin_lock_irqsave(&state_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_USER_STATE)
> +		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
> +			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> +			     requested_suspend_state, state,
> +			     ktime_to_ns(ktime_get()));
> +
> +	requested_suspend_state = state;
> +	if (state == PM_SUSPEND_ON)
> +		suspend_block(&main_suspend_blocker);
> +	else
> +		suspend_unblock(&main_suspend_blocker);
> +	spin_unlock_irqrestore(&state_lock, irqflags);
> +	return 0;
> +}
> +
> +void __init suspend_block_init(void)
> +{
> +	suspend_blocker_init(&main_suspend_blocker, "main");
> +	suspend_block(&main_suspend_blocker);
> +}
> -- 
> 1.6.5.1
> 
> _______________________________________________
> linux-pm mailing list
> linux-pm@lists.linux-foundation.org
> https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-03 22:11               ` Alan Stern
@ 2010-05-03 22:24                 ` Arve Hjønnevåg
  2010-05-03 22:24                 ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-03 22:24 UTC (permalink / raw)
  To: Alan Stern
  Cc: Rafael J. Wysocki, Pavel Machek, Linux-pm mailing list,
	Kernel development list, Tejun Heo, Oleg Nesterov, Len Brown,
	Randy Dunlap, Jesse Barnes, Nigel Cunningham, Cornelia Huck,
	Ming Lei, Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

On Mon, May 3, 2010 at 3:11 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> On Mon, 3 May 2010, Rafael J. Wysocki wrote:
>
>> The main problem is that the entire suspend subsystem is going to work in a
>> different way when suspend blockers are enforced.  Thus IMO it makes sense to
>> provide a switch between the "opportunistic" and "forced" modes, because that
>> clearly indicates to the user (or user space in general) how the whole suspend
>> subsystem actually works at the moment.
>>
>> As long as it's "opportunistic", the system will autosuspend if suspend
>> blockers are not active and the behavior of "state" reflects that.  If you want
>> to enforce a transition, switch to "forced" first.
>>
>> That's not at all confusing if you know what you're doing.  The defailt mode is
>> "forced", so the suspend subsystem works "as usual" by default.  You have to
>> directly switch it to "opportunistic" to change the behavior and once you've
>> done that, you shouldn't really be surprised that the behavior has changed.
>> That's what you've requested after all.
>
> How about changing the contents of /sys/power/state depending on the
> current policy?  When the policy is "forced" it should look the same as
> it does now.  When the policy is "opportunistic" it should contain
> "mem" and "on".

It already does this.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-03 22:11               ` Alan Stern
  2010-05-03 22:24                 ` Arve Hjønnevåg
@ 2010-05-03 22:24                 ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-05-03 22:24 UTC (permalink / raw)
  To: Alan Stern
  Cc: Len Brown, linux-doc, Kernel development list, Jesse Barnes,
	Oleg Nesterov, Tejun Heo, Linux-pm mailing list, Wu Fengguang,
	Andrew Morton

On Mon, May 3, 2010 at 3:11 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> On Mon, 3 May 2010, Rafael J. Wysocki wrote:
>
>> The main problem is that the entire suspend subsystem is going to work in a
>> different way when suspend blockers are enforced.  Thus IMO it makes sense to
>> provide a switch between the "opportunistic" and "forced" modes, because that
>> clearly indicates to the user (or user space in general) how the whole suspend
>> subsystem actually works at the moment.
>>
>> As long as it's "opportunistic", the system will autosuspend if suspend
>> blockers are not active and the behavior of "state" reflects that.  If you want
>> to enforce a transition, switch to "forced" first.
>>
>> That's not at all confusing if you know what you're doing.  The defailt mode is
>> "forced", so the suspend subsystem works "as usual" by default.  You have to
>> directly switch it to "opportunistic" to change the behavior and once you've
>> done that, you shouldn't really be surprised that the behavior has changed.
>> That's what you've requested after all.
>
> How about changing the contents of /sys/power/state depending on the
> current policy?  When the policy is "forced" it should look the same as
> it does now.  When the policy is "opportunistic" it should contain
> "mem" and "on".

It already does this.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-03 21:38             ` Rafael J. Wysocki
  2010-05-03 22:11               ` Alan Stern
@ 2010-05-03 22:11               ` Alan Stern
  2010-05-03 22:24                 ` Arve Hjønnevåg
  2010-05-03 22:24                 ` Arve Hjønnevåg
  1 sibling, 2 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-03 22:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Pavel Machek, Arve Hj??nnev??g, Linux-pm mailing list,
	Kernel development list, Tejun Heo, Oleg Nesterov, Len Brown,
	Randy Dunlap, Jesse Barnes, Nigel Cunningham, Cornelia Huck,
	Ming Lei, Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

On Mon, 3 May 2010, Rafael J. Wysocki wrote:

> The main problem is that the entire suspend subsystem is going to work in a
> different way when suspend blockers are enforced.  Thus IMO it makes sense to
> provide a switch between the "opportunistic" and "forced" modes, because that
> clearly indicates to the user (or user space in general) how the whole suspend
> subsystem actually works at the moment.
> 
> As long as it's "opportunistic", the system will autosuspend if suspend
> blockers are not active and the behavior of "state" reflects that.  If you want
> to enforce a transition, switch to "forced" first.
> 
> That's not at all confusing if you know what you're doing.  The defailt mode is
> "forced", so the suspend subsystem works "as usual" by default.  You have to
> directly switch it to "opportunistic" to change the behavior and once you've
> done that, you shouldn't really be surprised that the behavior has changed.
> That's what you've requested after all.

How about changing the contents of /sys/power/state depending on the 
current policy?  When the policy is "forced" it should look the same as 
it does now.  When the policy is "opportunistic" it should contain 
"mem" and "on".

Alan Stern


^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-03 21:38             ` Rafael J. Wysocki
@ 2010-05-03 22:11               ` Alan Stern
  2010-05-03 22:11               ` Alan Stern
  1 sibling, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-05-03 22:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes,
	Kernel development list, Tejun Heo, Linux-pm mailing list,
	Wu Fengguang, Andrew Morton

On Mon, 3 May 2010, Rafael J. Wysocki wrote:

> The main problem is that the entire suspend subsystem is going to work in a
> different way when suspend blockers are enforced.  Thus IMO it makes sense to
> provide a switch between the "opportunistic" and "forced" modes, because that
> clearly indicates to the user (or user space in general) how the whole suspend
> subsystem actually works at the moment.
> 
> As long as it's "opportunistic", the system will autosuspend if suspend
> blockers are not active and the behavior of "state" reflects that.  If you want
> to enforce a transition, switch to "forced" first.
> 
> That's not at all confusing if you know what you're doing.  The defailt mode is
> "forced", so the suspend subsystem works "as usual" by default.  You have to
> directly switch it to "opportunistic" to change the behavior and once you've
> done that, you shouldn't really be surprised that the behavior has changed.
> That's what you've requested after all.

How about changing the contents of /sys/power/state depending on the 
current policy?  When the policy is "forced" it should look the same as 
it does now.  When the policy is "opportunistic" it should contain 
"mem" and "on".

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-03 19:01           ` Pavel Machek
  2010-05-03 21:38             ` Rafael J. Wysocki
@ 2010-05-03 21:38             ` Rafael J. Wysocki
  2010-05-03 22:11               ` Alan Stern
  2010-05-03 22:11               ` Alan Stern
  1 sibling, 2 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-03 21:38 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Arve Hj??nnev??g, linux-pm, linux-kernel, Alan Stern, Tejun Heo,
	Oleg Nesterov, Len Brown, Randy Dunlap, Jesse Barnes,
	Magnus Damm, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

On Monday 03 May 2010, Pavel Machek wrote:
> Hi!
> 
> > > > > As I explained before (and got no reply),  the proposed interface is
> > > > > ugly. It uses one sysfs file to change semantics of another one.
> > > > 
> > > > In fact this behavior was discussed at the LF Collab Summit and no one
> > > > involved had any problem with that.
> > > 
> > > Well, I explained why I disliked in previous mail in more details,
> > 
> > We do exactly the same thing with 'pm_test', so I'm not sure what the problem is.
> > 
> > > and neither you nor Arve explained why it is good solution.
> > 
> > Because it's less confusing.  Having two different attributes returning
> > almost the same contents and working in a slightly different way wouldn't be
> > too clean IMO.
> 
> No, I don't think it is similar to pm_test. pm_test is debug-only, and
> orthogonal to state -- all combinations make sense.
> 
> With 'oportunistic > policy', state changes from blocking to
> nonblocking (surprise!). Plus, it is not orthogonal:
> 
> (assume we added s-t-flash on android for powersaving... or imagine I
> get oportunistic suspend working on PC --I was there with limited
> config on x60).
> 
> policy: oportunistic forced
> 
> state: on mem disk
> 
> First disadvantage of proposed interface is that while 'opportunistic
> mem' is active, I can't do 'forced disk' to save bit more power.
> 
> Next, not all combinations make sense.
> 
> oportunistic on == forced <nothing>

That's correct, but the "opportunistic on" thing is there to avoid switching
back and forth from "opportunistic" to "forced".  So that's a matter of
convenience rather than anything else.

> oportunistic disk -- probably something that will not be implemented
> any time soon.

Yes, and that's why "disk" won't work with "opportunistic" for now.

> oportunistic mem -- makes sense.
> 
> forced on -- NOP

There won't be "on" with "forced" (that probably needs changing at the moment).

> forced mem  -- makes sense.
> 
> forced disk -- makes sense.
> 
> So we have matrix of 7 possibilities, but only 4 make sense... IMO its confusing.

The main problem is that the entire suspend subsystem is going to work in a
different way when suspend blockers are enforced.  Thus IMO it makes sense to
provide a switch between the "opportunistic" and "forced" modes, because that
clearly indicates to the user (or user space in general) how the whole suspend
subsystem actually works at the moment.

As long as it's "opportunistic", the system will autosuspend if suspend
blockers are not active and the behavior of "state" reflects that.  If you want
to enforce a transition, switch to "forced" first.

That's not at all confusing if you know what you're doing.  The defailt mode is
"forced", so the suspend subsystem works "as usual" by default.  You have to
directly switch it to "opportunistic" to change the behavior and once you've
done that, you shouldn't really be surprised that the behavior has changed.
That's what you've requested after all.

So no, I don't really think it's confusing.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-03 19:01           ` Pavel Machek
@ 2010-05-03 21:38             ` Rafael J. Wysocki
  2010-05-03 21:38             ` Rafael J. Wysocki
  1 sibling, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-03 21:38 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes, linux-kernel,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Monday 03 May 2010, Pavel Machek wrote:
> Hi!
> 
> > > > > As I explained before (and got no reply),  the proposed interface is
> > > > > ugly. It uses one sysfs file to change semantics of another one.
> > > > 
> > > > In fact this behavior was discussed at the LF Collab Summit and no one
> > > > involved had any problem with that.
> > > 
> > > Well, I explained why I disliked in previous mail in more details,
> > 
> > We do exactly the same thing with 'pm_test', so I'm not sure what the problem is.
> > 
> > > and neither you nor Arve explained why it is good solution.
> > 
> > Because it's less confusing.  Having two different attributes returning
> > almost the same contents and working in a slightly different way wouldn't be
> > too clean IMO.
> 
> No, I don't think it is similar to pm_test. pm_test is debug-only, and
> orthogonal to state -- all combinations make sense.
> 
> With 'oportunistic > policy', state changes from blocking to
> nonblocking (surprise!). Plus, it is not orthogonal:
> 
> (assume we added s-t-flash on android for powersaving... or imagine I
> get oportunistic suspend working on PC --I was there with limited
> config on x60).
> 
> policy: oportunistic forced
> 
> state: on mem disk
> 
> First disadvantage of proposed interface is that while 'opportunistic
> mem' is active, I can't do 'forced disk' to save bit more power.
> 
> Next, not all combinations make sense.
> 
> oportunistic on == forced <nothing>

That's correct, but the "opportunistic on" thing is there to avoid switching
back and forth from "opportunistic" to "forced".  So that's a matter of
convenience rather than anything else.

> oportunistic disk -- probably something that will not be implemented
> any time soon.

Yes, and that's why "disk" won't work with "opportunistic" for now.

> oportunistic mem -- makes sense.
> 
> forced on -- NOP

There won't be "on" with "forced" (that probably needs changing at the moment).

> forced mem  -- makes sense.
> 
> forced disk -- makes sense.
> 
> So we have matrix of 7 possibilities, but only 4 make sense... IMO its confusing.

The main problem is that the entire suspend subsystem is going to work in a
different way when suspend blockers are enforced.  Thus IMO it makes sense to
provide a switch between the "opportunistic" and "forced" modes, because that
clearly indicates to the user (or user space in general) how the whole suspend
subsystem actually works at the moment.

As long as it's "opportunistic", the system will autosuspend if suspend
blockers are not active and the behavior of "state" reflects that.  If you want
to enforce a transition, switch to "forced" first.

That's not at all confusing if you know what you're doing.  The defailt mode is
"forced", so the suspend subsystem works "as usual" by default.  You have to
directly switch it to "opportunistic" to change the behavior and once you've
done that, you shouldn't really be surprised that the behavior has changed.
That's what you've requested after all.

So no, I don't really think it's confusing.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-02 21:29         ` Rafael J. Wysocki
@ 2010-05-03 19:01           ` Pavel Machek
  2010-05-03 21:38             ` Rafael J. Wysocki
  2010-05-03 21:38             ` Rafael J. Wysocki
  2010-05-03 19:01           ` Pavel Machek
  1 sibling, 2 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-03 19:01 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Arve Hj??nnev??g, linux-pm, linux-kernel, Alan Stern, Tejun Heo,
	Oleg Nesterov, Len Brown, Randy Dunlap, Jesse Barnes,
	Magnus Damm, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

Hi!

> > > > As I explained before (and got no reply),  the proposed interface is
> > > > ugly. It uses one sysfs file to change semantics of another one.
> > > 
> > > In fact this behavior was discussed at the LF Collab Summit and no one
> > > involved had any problem with that.
> > 
> > Well, I explained why I disliked in previous mail in more details,
> 
> We do exactly the same thing with 'pm_test', so I'm not sure what the problem is.
> 
> > and neither you nor Arve explained why it is good solution.
> 
> Because it's less confusing.  Having two different attributes returning
> almost the same contents and working in a slightly different way wouldn't be
> too clean IMO.

No, I don't think it is similar to pm_test. pm_test is debug-only, and
orthogonal to state -- all combinations make sense.

With 'oportunistic > policy', state changes from blocking to
nonblocking (surprise!). Plus, it is not orthogonal:

(assume we added s-t-flash on android for powersaving... or imagine I
get oportunistic suspend working on PC --I was there with limited
config on x60).

policy: oportunistic forced

state: on mem disk

First disadvantage of proposed interface is that while 'opportunistic
mem' is active, I can't do 'forced disk' to save bit more power.

Next, not all combinations make sense.

oportunistic on == forced <nothing>

oportunistic disk -- probably something that will not be implemented
any time soon.

oportunistic mem -- makes sense.

forced on -- NOP

forced mem  -- makes sense.

forced disk -- makes sense.

So we have matrix of 7 possibilities, but only 4 make sense... IMO its confusing.



								Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-02 21:29         ` Rafael J. Wysocki
  2010-05-03 19:01           ` Pavel Machek
@ 2010-05-03 19:01           ` Pavel Machek
  1 sibling, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-03 19:01 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes, linux-kernel,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

Hi!

> > > > As I explained before (and got no reply),  the proposed interface is
> > > > ugly. It uses one sysfs file to change semantics of another one.
> > > 
> > > In fact this behavior was discussed at the LF Collab Summit and no one
> > > involved had any problem with that.
> > 
> > Well, I explained why I disliked in previous mail in more details,
> 
> We do exactly the same thing with 'pm_test', so I'm not sure what the problem is.
> 
> > and neither you nor Arve explained why it is good solution.
> 
> Because it's less confusing.  Having two different attributes returning
> almost the same contents and working in a slightly different way wouldn't be
> too clean IMO.

No, I don't think it is similar to pm_test. pm_test is debug-only, and
orthogonal to state -- all combinations make sense.

With 'oportunistic > policy', state changes from blocking to
nonblocking (surprise!). Plus, it is not orthogonal:

(assume we added s-t-flash on android for powersaving... or imagine I
get oportunistic suspend working on PC --I was there with limited
config on x60).

policy: oportunistic forced

state: on mem disk

First disadvantage of proposed interface is that while 'opportunistic
mem' is active, I can't do 'forced disk' to save bit more power.

Next, not all combinations make sense.

oportunistic on == forced <nothing>

oportunistic disk -- probably something that will not be implemented
any time soon.

oportunistic mem -- makes sense.

forced on -- NOP

forced mem  -- makes sense.

forced disk -- makes sense.

So we have matrix of 7 possibilities, but only 4 make sense... IMO its confusing.



								Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-02 20:52       ` Pavel Machek
  2010-05-02 21:29         ` Rafael J. Wysocki
@ 2010-05-02 21:29         ` Rafael J. Wysocki
  2010-05-03 19:01           ` Pavel Machek
  2010-05-03 19:01           ` Pavel Machek
  1 sibling, 2 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-02 21:29 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Arve Hj??nnev??g, linux-pm, linux-kernel, Alan Stern, Tejun Heo,
	Oleg Nesterov, Len Brown, Randy Dunlap, Jesse Barnes,
	Magnus Damm, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

On Sunday 02 May 2010, Pavel Machek wrote:
> On Sun 2010-05-02 22:10:53, Rafael J. Wysocki wrote:
> > On Sunday 02 May 2010, Pavel Machek wrote:
> > > Hi!
> > > 
> > > > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > > > After setting the policy to opportunistic, writes to /sys/power/state
> > > > become non-blocking requests that specify which suspend state to enter
> > > > when no suspend blockers are active. A special state, "on", stops the
> > > > process by activating the "main" suspend blocker.
> > > 
> > > As I explained before (and got no reply),  the proposed interface is
> > > ugly. It uses one sysfs file to change semantics of another one.
> > 
> > In fact this behavior was discussed at the LF Collab Summit and no one
> > involved had any problem with that.
> 
> Well, I explained why I disliked in previous mail in more details,

We do exactly the same thing with 'pm_test', so I'm not sure what the problem is.

> and neither you nor Arve explained why it is good solution.

Because it's less confusing.  Having two different attributes returning
almost the same contents and working in a slightly different way wouldn't be
too clean IMO.

Also it reduces code duplication slightly.

> I was not on LF Collab summit, so unfortunately I can't comment on that.
> 
> > > > Signed-off-by: Arve Hj??nnev??g <arve@android.com>
> > > 
> > > NAK.
> > 
> > Ignored.
> 
> WTF?

Literally.  I'm not going to take that NAK into consideration.

Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-02 20:52       ` Pavel Machek
@ 2010-05-02 21:29         ` Rafael J. Wysocki
  2010-05-02 21:29         ` Rafael J. Wysocki
  1 sibling, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-02 21:29 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes, linux-kernel,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Sunday 02 May 2010, Pavel Machek wrote:
> On Sun 2010-05-02 22:10:53, Rafael J. Wysocki wrote:
> > On Sunday 02 May 2010, Pavel Machek wrote:
> > > Hi!
> > > 
> > > > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > > > After setting the policy to opportunistic, writes to /sys/power/state
> > > > become non-blocking requests that specify which suspend state to enter
> > > > when no suspend blockers are active. A special state, "on", stops the
> > > > process by activating the "main" suspend blocker.
> > > 
> > > As I explained before (and got no reply),  the proposed interface is
> > > ugly. It uses one sysfs file to change semantics of another one.
> > 
> > In fact this behavior was discussed at the LF Collab Summit and no one
> > involved had any problem with that.
> 
> Well, I explained why I disliked in previous mail in more details,

We do exactly the same thing with 'pm_test', so I'm not sure what the problem is.

> and neither you nor Arve explained why it is good solution.

Because it's less confusing.  Having two different attributes returning
almost the same contents and working in a slightly different way wouldn't be
too clean IMO.

Also it reduces code duplication slightly.

> I was not on LF Collab summit, so unfortunately I can't comment on that.
> 
> > > > Signed-off-by: Arve Hj??nnev??g <arve@android.com>
> > > 
> > > NAK.
> > 
> > Ignored.
> 
> WTF?

Literally.  I'm not going to take that NAK into consideration.

Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-02 20:10     ` Rafael J. Wysocki
@ 2010-05-02 20:52       ` Pavel Machek
  2010-05-02 21:29         ` Rafael J. Wysocki
  2010-05-02 21:29         ` Rafael J. Wysocki
  2010-05-02 20:52       ` Pavel Machek
  1 sibling, 2 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-02 20:52 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Arve Hj??nnev??g, linux-pm, linux-kernel, Alan Stern, Tejun Heo,
	Oleg Nesterov, Len Brown, Randy Dunlap, Jesse Barnes,
	Magnus Damm, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

On Sun 2010-05-02 22:10:53, Rafael J. Wysocki wrote:
> On Sunday 02 May 2010, Pavel Machek wrote:
> > Hi!
> > 
> > > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > > After setting the policy to opportunistic, writes to /sys/power/state
> > > become non-blocking requests that specify which suspend state to enter
> > > when no suspend blockers are active. A special state, "on", stops the
> > > process by activating the "main" suspend blocker.
> > 
> > As I explained before (and got no reply),  the proposed interface is
> > ugly. It uses one sysfs file to change semantics of another one.
> 
> In fact this behavior was discussed at the LF Collab Summit and no one
> involved had any problem with that.

Well, I explained why I disliked in previous mail in more details, and
neither you nor Arve explained why it is good solution.

I was not on LF Collab summit, so unfortunately I can't comment on that.

> > > Signed-off-by: Arve Hj??nnev??g <arve@android.com>
> > 
> > NAK.
> 
> Ignored.

WTF?
									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-02 20:10     ` Rafael J. Wysocki
  2010-05-02 20:52       ` Pavel Machek
@ 2010-05-02 20:52       ` Pavel Machek
  1 sibling, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-02 20:52 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes, linux-kernel,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Sun 2010-05-02 22:10:53, Rafael J. Wysocki wrote:
> On Sunday 02 May 2010, Pavel Machek wrote:
> > Hi!
> > 
> > > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > > After setting the policy to opportunistic, writes to /sys/power/state
> > > become non-blocking requests that specify which suspend state to enter
> > > when no suspend blockers are active. A special state, "on", stops the
> > > process by activating the "main" suspend blocker.
> > 
> > As I explained before (and got no reply),  the proposed interface is
> > ugly. It uses one sysfs file to change semantics of another one.
> 
> In fact this behavior was discussed at the LF Collab Summit and no one
> involved had any problem with that.

Well, I explained why I disliked in previous mail in more details, and
neither you nor Arve explained why it is good solution.

I was not on LF Collab summit, so unfortunately I can't comment on that.

> > > Signed-off-by: Arve Hj??nnev??g <arve@android.com>
> > 
> > NAK.
> 
> Ignored.

WTF?
									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-02  6:56   ` Pavel Machek
@ 2010-05-02 20:10     ` Rafael J. Wysocki
  2010-05-02 20:52       ` Pavel Machek
  2010-05-02 20:52       ` Pavel Machek
  2010-05-02 20:10     ` Rafael J. Wysocki
  1 sibling, 2 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-02 20:10 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Arve Hj??nnev??g, linux-pm, linux-kernel, Alan Stern, Tejun Heo,
	Oleg Nesterov, Len Brown, Randy Dunlap, Jesse Barnes,
	Magnus Damm, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

On Sunday 02 May 2010, Pavel Machek wrote:
> Hi!
> 
> > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > After setting the policy to opportunistic, writes to /sys/power/state
> > become non-blocking requests that specify which suspend state to enter
> > when no suspend blockers are active. A special state, "on", stops the
> > process by activating the "main" suspend blocker.
> 
> As I explained before (and got no reply),  the proposed interface is
> ugly. It uses one sysfs file to change semantics of another one.

In fact this behavior was discussed at the LF Collab Summit and no one
involved had any problem with that.

> > Signed-off-by: Arve Hj??nnev??g <arve@android.com>
> 
> NAK.

Ignored.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-05-02  6:56   ` Pavel Machek
  2010-05-02 20:10     ` Rafael J. Wysocki
@ 2010-05-02 20:10     ` Rafael J. Wysocki
  1 sibling, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-05-02 20:10 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes, linux-kernel,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Sunday 02 May 2010, Pavel Machek wrote:
> Hi!
> 
> > Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> > After setting the policy to opportunistic, writes to /sys/power/state
> > become non-blocking requests that specify which suspend state to enter
> > when no suspend blockers are active. A special state, "on", stops the
> > process by activating the "main" suspend blocker.
> 
> As I explained before (and got no reply),  the proposed interface is
> ugly. It uses one sysfs file to change semantics of another one.

In fact this behavior was discussed at the LF Collab Summit and no one
involved had any problem with that.

> > Signed-off-by: Arve Hj??nnev??g <arve@android.com>
> 
> NAK.

Ignored.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-30 22:36 ` Arve Hjønnevåg
                     ` (2 preceding siblings ...)
  2010-05-02  7:01   ` Pavel Machek
@ 2010-05-02  7:01   ` Pavel Machek
  2010-05-04  5:12   ` [linux-pm] " mark gross
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-02  7:01 UTC (permalink / raw)
  To: Arve Hj??nnev??g
  Cc: linux-pm, linux-kernel, Rafael J. Wysocki, Alan Stern, Tejun Heo,
	Oleg Nesterov, Len Brown, Randy Dunlap, Jesse Barnes,
	Magnus Damm, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

On Fri 2010-04-30 15:36:54, Arve Hj??nnev??g wrote:
> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.
> 
> Signed-off-by: Arve Hj??nnev??g <arve@android.com>

Also note that this patch mixes up adding new in-kernel interface with
adding new userland API.

The in-kernel interface seems to be sane and it would be nice to merge
it soon, unfortunately the strange userland API is in the same patch :-(.

> ---
>  Documentation/power/opportunistic-suspend.txt |  119 +++++++++++
>  include/linux/suspend_blocker.h               |   64 ++++++
>  kernel/power/Kconfig                          |   16 ++
>  kernel/power/Makefile                         |    1 +
>  kernel/power/main.c                           |   92 ++++++++-
>  kernel/power/power.h                          |    9 +
>  kernel/power/suspend.c                        |    4 +-
>  kernel/power/suspend_blocker.c                |  261 +++++++++++++++++++++++++
>  8 files changed, 559 insertions(+), 7 deletions(-)
>  create mode 100644 Documentation/power/opportunistic-suspend.txt
>  create mode 100755 include/linux/suspend_blocker.h
>  create mode 100644 kernel/power/suspend_blocker.c
> 
> diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
> new file mode 100644
> index 0000000..3d060e8
> --- /dev/null
> +++ b/Documentation/power/opportunistic-suspend.txt
> @@ -0,0 +1,119 @@
> +Opportunistic Suspend
> +=====================
> +
> +Opportunistic suspend is a feature allowing the system to be suspended (ie. put
> +into one of the available sleep states) automatically whenever it is regarded
> +as idle.  The suspend blockers framework described below is used to determine
> +when that happens.
> +
> +The /sys/power/policy sysfs attribute is used to switch the system between the
> +opportunistic and "forced" suspend behavior, where in the latter case the
> +system is only suspended if a specific value, corresponding to one of the
> +available system sleep states, is written into /sys/power/state.  However, in
> +the former, opportunistic, case the system is put into the sleep state
> +corresponding to the value written to /sys/power/state whenever there are no
> +active suspend blockers. The default policy is "forced". Also, suspend blockers
> +do not affect sleep states entered from idle.
> +
> +When the policy is "opportunisic", there is a special value, "on", that can be
> +written to /sys/power/state. This will block the automatic sleep request, as if
> +a suspend blocker was used by a device driver. This way the opportunistic
> +suspend may be blocked by user space whithout switching back to the "forced"
> +mode.
> +
> +A suspend blocker is an object used to inform the PM subsystem when the system
> +can or cannot be suspended in the "opportunistic" mode (the "forced" mode
> +ignores suspend blockers).  To use it, a device driver creates a struct
> +suspend_blocker that must be initialized with suspend_blocker_init(). Before
> +freeing the suspend_blocker structure or its name, suspend_blocker_destroy()
> +must be called on it.
> +
> +A suspend blocker is activated using suspend_block(), which prevents the PM
> +subsystem from putting the system into the requested sleep state in the
> +"opportunistic" mode until the suspend blocker is deactivated with
> +suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
> +the system will not suspend as long as at least one of them is active.
> +
> +If opportunistic suspend is already in progress when suspend_block() is called,
> +it will abort the suspend, unless suspend_ops->enter has already been
> +executed. If suspend is aborted this way, the system is usually not fully
> +operational at that point. The suspend callbacks of some drivers may still be
> +running and it usually takes time to restore the system to the fully operational
> +state.
> +
> +Here's an example showing how a cell phone or other embedded system can handle
> +keystrokes (or other input events) in the presence of suspend blockers. Use
> +set_irq_wake or a platform specific api to make sure the keypad interrupt wakes
> +up the cpu. Once the keypad driver has resumed, the sequence of events can look
> +like this:
> +
> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> +- The keypad-scan code detects a key change and reports it to the input-event
> +  driver.
> +- The input-event driver sees the key change, enqueues an event, and calls
> +  suspend_block on the input-event-queue suspend_blocker.
> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> +  on the keypad-scan suspend_blocker.
> +- The user-space input-event thread returns from select/poll, calls
> +  suspend_block on the process-input-events suspend_blocker and then calls read
> +  on the input-event device.
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> +- The user-space input-event thread returns from read. If it determines that
> +  the key should be ignored, it calls suspend_unblock on the
> +  process_input_events suspend_blocker and then calls select or poll. The
> +  system will automatically suspend again, since now no suspend blockers are
> +  active.
> +
> +If the key that was pressed instead should preform a simple action (for example,
> +adjusting the volume), this action can be performed right before calling
> +suspend_unblock on the process_input_events suspend_blocker. However, if the key
> +triggers a longer-running action, that action needs its own suspend_blocker and
> +suspend_block must be called on that suspend blocker before calling
> +suspend_unblock on the process_input_events suspend_blocker.
> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++
> +
> +
> +Driver API
> +==========
> +
> +A driver can use the suspend block api by adding a suspend_blocker variable to
> +its state and calling suspend_blocker_init. For instance:
> +struct state {
> +	struct suspend_blocker suspend_blocker;
> +}
> +
> +init() {
> +	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
> +}
> +
> +Before freeing the memory, suspend_blocker_destroy must be called:
> +
> +uninit() {
> +	suspend_blocker_destroy(&state->suspend_blocker);
> +}
> +
> +When the driver determines that it needs to run (usually in an interrupt
> +handler) it calls suspend_block:
> +	suspend_block(&state->suspend_blocker);
> +
> +When it no longer needs to run it calls suspend_unblock:
> +	suspend_unblock(&state->suspend_blocker);
> +
> +Calling suspend_block when the suspend blocker is active or suspend_unblock when
> +it is not active has no effect (i.e., these functions don't nest). This allows
> +drivers to update their state and call suspend suspend_block or suspend_unblock
> +based on the result.
> +For instance:
> +
> +if (list_empty(&state->pending_work))
> +	suspend_unblock(&state->suspend_blocker);
> +else
> +	suspend_block(&state->suspend_blocker);
> +
> diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
> new file mode 100755
> index 0000000..f9928cc
> --- /dev/null
> +++ b/include/linux/suspend_blocker.h
> @@ -0,0 +1,64 @@
> +/* include/linux/suspend_blocker.h
> + *
> + * Copyright (C) 2007-2009 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _LINUX_SUSPEND_BLOCKER_H
> +#define _LINUX_SUSPEND_BLOCKER_H
> +
> +#include <linux/list.h>
> +
> +/**
> + * struct suspend_blocker - the basic suspend_blocker structure
> + * @link:	List entry for active or inactive list.
> + * @flags:	Tracks initialized and active state.
> + * @name:	Name used for debugging.
> + *
> + * When a suspend_blocker is active it prevents the system from entering
> + * opportunistic suspend.
> + *
> + * The suspend_blocker structure must be initialized by suspend_blocker_init()
> + */
> +
> +struct suspend_blocker {
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	struct list_head    link;
> +	int                 flags;
> +	const char         *name;
> +#endif
> +};
> +
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
> +void suspend_blocker_destroy(struct suspend_blocker *blocker);
> +void suspend_block(struct suspend_blocker *blocker);
> +void suspend_unblock(struct suspend_blocker *blocker);
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker);
> +bool suspend_is_blocked(void);
> +
> +#else
> +
> +static inline void suspend_blocker_init(struct suspend_blocker *blocker,
> +					const char *name) {}
> +static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
> +static inline void suspend_block(struct suspend_blocker *blocker) {}
> +static inline void suspend_unblock(struct suspend_blocker *blocker) {}
> +static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
> +								{ return 0; }
> +static inline bool suspend_is_blocked(void) { return 0; }
> +
> +#endif
> +
> +#endif
> +
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 5c36ea9..55a06a1 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -130,6 +130,22 @@ config SUSPEND_FREEZER
>  
>  	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
>  
> +config OPPORTUNISTIC_SUSPEND
> +	bool "Suspend blockers"
> +	depends on PM_SLEEP
> +	select RTC_LIB
> +	default n
> +	---help---
> +	  Opportunistic sleep support.  Allows the system to be put into a sleep
> +	  state opportunistically, if it doesn't do any useful work at the
> +	  moment. The PM subsystem is switched into this mode of operation by
> +	  writing "opportunistic" into /sys/power/policy, while writing
> +	  "forced" to this file turns the opportunistic suspend feature off.
> +	  In the "opportunistic" mode suspend blockers are used to determine
> +	  when to suspend the system and the value written to /sys/power/state
> +	  determines the sleep state the system will be put into when there are
> +	  no active suspend blockers.
> +
>  config HIBERNATION_NVS
>  	bool
>  
> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
> index 4319181..ee5276d 100644
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
>  obj-$(CONFIG_PM_SLEEP)		+= console.o
>  obj-$(CONFIG_FREEZER)		+= process.o
>  obj-$(CONFIG_SUSPEND)		+= suspend.o
> +obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= suspend_blocker.o
>  obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
>  obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
>  obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
> diff --git a/kernel/power/main.c b/kernel/power/main.c
> index b58800b..030ecdc 100644
> --- a/kernel/power/main.c
> +++ b/kernel/power/main.c
> @@ -12,6 +12,7 @@
>  #include <linux/string.h>
>  #include <linux/resume-trace.h>
>  #include <linux/workqueue.h>
> +#include <linux/suspend_blocker.h>
>  
>  #include "power.h"
>  
> @@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
>  unsigned int pm_flags;
>  EXPORT_SYMBOL(pm_flags);
>  
> +struct policy {
> +	const char *name;
> +	bool (*valid_state)(suspend_state_t state);
> +	int (*set_state)(suspend_state_t state);
> +};
> +static struct policy policies[] = {
> +	{
> +		.name		= "forced",
> +		.valid_state	= valid_state,
> +		.set_state	= enter_state,
> +	},
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	{
> +		.name		= "opportunistic",
> +		.valid_state	= request_suspend_valid_state,
> +		.set_state	= request_suspend_state,
> +	},
> +#endif
> +};
> +static int policy;
> +
>  #ifdef CONFIG_PM_SLEEP
>  
>  /* Routines for PM-transition notifications */
> @@ -146,6 +168,12 @@ struct kobject *power_kobj;
>   *
>   *	store() accepts one of those strings, translates it into the 
>   *	proper enumerated value, and initiates a suspend transition.
> + *
> + *	If policy is set to opportunistic, store() does not block until the
> + *	system resumes, and it will try to re-enter the state until another
> + *	state is requested. Suspend blockers are respected and the requested
> + *	state will only be entered when no suspend blockers are active.
> + *	Write "on" to cancel.
>   */
>  static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  			  char *buf)
> @@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  	int i;
>  
>  	for (i = 0; i < PM_SUSPEND_MAX; i++) {
> -		if (pm_states[i] && valid_state(i))
> +		if (pm_states[i] && policies[policy].valid_state(i))
>  			s += sprintf(s,"%s ", pm_states[i]);
>  	}
>  #endif
>  #ifdef CONFIG_HIBERNATION
> -	s += sprintf(s, "%s\n", "disk");
> +	if (!policy)
> +		s += sprintf(s, "%s\n", "disk");
>  #else
>  	if (s != buf)
>  		/* convert the last space to a newline */
> @@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			   const char *buf, size_t n)
>  {
>  #ifdef CONFIG_SUSPEND
> -	suspend_state_t state = PM_SUSPEND_STANDBY;
> +	suspend_state_t state = PM_SUSPEND_ON;
>  	const char * const *s;
>  #endif
>  	char *p;
> @@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  	len = p ? p - buf : n;
>  
>  	/* First, check if we are requested to hibernate */
> -	if (len == 4 && !strncmp(buf, "disk", len)) {
> +	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
>  		error = hibernate();
>    goto Exit;
>  	}
> @@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			break;
>  	}
>  	if (state < PM_SUSPEND_MAX && *s)
> -		error = enter_state(state);
> +		error = policies[policy].set_state(state);
>  #endif
>  
>   Exit:
> @@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  
>  power_attr(state);
>  
> +/**
> + *	policy - set policy for state
> + */
> +
> +static ssize_t policy_show(struct kobject *kobj,
> +			   struct kobj_attribute *attr, char *buf)
> +{
> +	char *s = buf;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		if (i == policy)
> +			s += sprintf(s, "[%s] ", policies[i].name);
> +		else
> +			s += sprintf(s, "%s ", policies[i].name);
> +	}
> +	if (s != buf)
> +		/* convert the last space to a newline */
> +		*(s-1) = '\n';
> +	return (s - buf);
> +}
> +
> +static ssize_t policy_store(struct kobject *kobj,
> +			    struct kobj_attribute *attr,
> +			    const char *buf, size_t n)
> +{
> +	const char *s;
> +	char *p;
> +	int len;
> +	int i;
> +
> +	p = memchr(buf, '\n', n);
> +	len = p ? p - buf : n;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		s = policies[i].name;
> +		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
> +			mutex_lock(&pm_mutex);
> +			policies[policy].set_state(PM_SUSPEND_ON);
> +			policy = i;
> +			mutex_unlock(&pm_mutex);
> +			return n;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +power_attr(policy);
> +
>  #ifdef CONFIG_PM_TRACE
>  int pm_trace_enabled;
>  
> @@ -231,6 +309,7 @@ power_attr(pm_trace);
>  
>  static struct attribute * g[] = {
>  	&state_attr.attr,
> +	&policy_attr.attr,
>  #ifdef CONFIG_PM_TRACE
>  	&pm_trace_attr.attr,
>  #endif
> @@ -247,7 +326,7 @@ static struct attribute_group attr_group = {
>  	.attrs = g,
>  };
>  
> -#ifdef CONFIG_PM_RUNTIME
> +#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_OPPORTUNISTIC_SUSPEND)
>  struct workqueue_struct *pm_wq;
>  EXPORT_SYMBOL_GPL(pm_wq);
>  
> @@ -266,6 +345,7 @@ static int __init pm_init(void)
>  	int error = pm_start_workqueue();
>  	if (error)
>  		return error;
> +	suspend_block_init();
>  	power_kobj = kobject_create_and_add("power", NULL);
>  	if (!power_kobj)
>  		return -ENOMEM;
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index 46c5a26..67d10fd 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -236,3 +236,12 @@ static inline void suspend_thaw_processes(void)
>  {
>  }
>  #endif
> +
> +/* kernel/power/suspend_block.c */
> +extern int request_suspend_state(suspend_state_t state);
> +extern bool request_suspend_valid_state(suspend_state_t state);
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +extern void __init suspend_block_init(void);
> +#else
> +static inline void suspend_block_init(void) {}
> +#endif
> diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
> index 56e7dbb..dc42006 100644
> --- a/kernel/power/suspend.c
> +++ b/kernel/power/suspend.c
> @@ -16,10 +16,12 @@
>  #include <linux/cpu.h>
>  #include <linux/syscalls.h>
>  #include <linux/gfp.h>
> +#include <linux/suspend_blocker.h>
>  
>  #include "power.h"
>  
>  const char *const pm_states[PM_SUSPEND_MAX] = {
> +	[PM_SUSPEND_ON]		= "on",
>  	[PM_SUSPEND_STANDBY]	= "standby",
>  	[PM_SUSPEND_MEM]	= "mem",
>  };
> @@ -157,7 +159,7 @@ static int suspend_enter(suspend_state_t state)
>  
>  	error = sysdev_suspend(PMSG_SUSPEND);
>  	if (!error) {
> -		if (!suspend_test(TEST_CORE))
> +		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
>  			error = suspend_ops->enter(state);
>  		sysdev_resume();
>  	}
> diff --git a/kernel/power/suspend_blocker.c b/kernel/power/suspend_blocker.c
> new file mode 100644
> index 0000000..2c58b21
> --- /dev/null
> +++ b/kernel/power/suspend_blocker.c
> @@ -0,0 +1,261 @@
> +/* kernel/power/suspend_blocker.c
> + *
> + * Copyright (C) 2005-2010 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/suspend.h>
> +#include <linux/suspend_blocker.h>
> +#include "power.h"
> +
> +extern struct workqueue_struct *pm_wq;
> +
> +enum {
> +	DEBUG_EXIT_SUSPEND = 1U << 0,
> +	DEBUG_WAKEUP = 1U << 1,
> +	DEBUG_USER_STATE = 1U << 2,
> +	DEBUG_SUSPEND = 1U << 3,
> +	DEBUG_SUSPEND_BLOCKER = 1U << 4,
> +};
> +static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
> +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
> +
> +#define SB_INITIALIZED            (1U << 8)
> +#define SB_ACTIVE                 (1U << 9)
> +
> +static DEFINE_SPINLOCK(list_lock);
> +static DEFINE_SPINLOCK(state_lock);
> +static LIST_HEAD(inactive_blockers);
> +static LIST_HEAD(active_blockers);
> +static int current_event_num;
> +struct suspend_blocker main_suspend_blocker;
> +static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
> +static bool enable_suspend_blockers;
> +
> +#define pr_info_time(fmt, args...) \
> +	do { \
> +		struct timespec ts; \
> +		struct rtc_time tm; \
> +		getnstimeofday(&ts); \
> +		rtc_time_to_tm(ts.tv_sec, &tm); \
> +		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
> +			args, \
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
> +	} while (0);
> +
> +static void print_active_blockers_locked(void)
> +{
> +	struct suspend_blocker *blocker;
> +
> +	list_for_each_entry(blocker, &active_blockers, link)
> +		pr_info("active suspend blocker %s\n", blocker->name);
> +}
> +
> +/**
> + * suspend_is_blocked() - Check if suspend should be blocked
> + *
> + * suspend_is_blocked can be used by generic power management code to abort
> + * suspend.
> + *
> + * To preserve backward compatibility suspend_is_blocked returns 0 unless it
> + * is called during suspend initiated from the suspend_block code.
> + */
> +bool suspend_is_blocked(void)
> +{
> +	if (!enable_suspend_blockers)
> +		return 0;
> +	return !list_empty(&active_blockers);
> +}
> +
> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = true;
> +	while (!suspend_is_blocked()) {
> +		entry_event_num = current_event_num;
> +
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: enter suspend\n");
> +
> +		ret = pm_suspend(requested_suspend_state);
> +
> +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> +
> +		if (current_event_num == entry_event_num)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +	}
> +	enable_suspend_blockers = false;
> +}
> +static DECLARE_WORK(suspend_work, suspend_worker);
> +
> +/**
> + * suspend_blocker_init() - Initialize a suspend blocker
> + * @blocker:	The suspend blocker to initialize.
> + * @name:	The name of the suspend blocker to show in debug messages.
> + *
> + * The suspend blocker struct and name must not be freed before calling
> + * suspend_blocker_destroy.
> + */
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
> +{
> +	unsigned long irqflags = 0;
> +
> +	WARN_ON(!name);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_init name=%s\n", name);
> +
> +	blocker->name = name;
> +	blocker->flags = SB_INITIALIZED;
> +	INIT_LIST_HEAD(&blocker->link);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	list_add(&blocker->link, &inactive_blockers);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_init);
> +
> +/**
> + * suspend_blocker_destroy() - Destroy a suspend blocker
> + * @blocker:	The suspend blocker to destroy.
> + */
> +void suspend_blocker_destroy(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags &= ~SB_INITIALIZED;
> +	list_del(&blocker->link);
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(pm_wq, &suspend_work);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_destroy);
> +
> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +
> +	blocker->flags |= SB_ACTIVE;
> +	list_move(&blocker->link, &active_blockers);
> +	current_event_num++;
> +
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_block);
> +
> +/**
> + * suspend_unblock() - Unblock suspend
> + * @blocker:	The suspend blocker to unblock.
> + *
> + * If no other suspend blockers block suspend, the system will suspend.
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_unblock(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_unblock: %s\n", blocker->name);
> +
> +	list_move(&blocker->link, &inactive_blockers);
> +
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(pm_wq, &suspend_work);
> +	blocker->flags &= ~(SB_ACTIVE);
> +	if (blocker == &main_suspend_blocker) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			print_active_blockers_locked();
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_unblock);
> +
> +/**
> + * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
> + * @blocker:	The suspend blocker to check.
> + *
> + * Returns true if the suspend_blocker is currently active.
> + */
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker)
> +{
> +	WARN_ON(!(blocker->flags & SB_INITIALIZED));
> +
> +	return !!(blocker->flags & SB_ACTIVE);
> +}
> +EXPORT_SYMBOL(suspend_blocker_is_active);
> +
> +bool request_suspend_valid_state(suspend_state_t state)
> +{
> +	return (state == PM_SUSPEND_ON) || valid_state(state);
> +}
> +
> +int request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +
> +	if (!request_suspend_valid_state(state))
> +		return -ENODEV;
> +
> +	spin_lock_irqsave(&state_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_USER_STATE)
> +		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
> +			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> +			     requested_suspend_state, state,
> +			     ktime_to_ns(ktime_get()));
> +
> +	requested_suspend_state = state;
> +	if (state == PM_SUSPEND_ON)
> +		suspend_block(&main_suspend_blocker);
> +	else
> +		suspend_unblock(&main_suspend_blocker);
> +	spin_unlock_irqrestore(&state_lock, irqflags);
> +	return 0;
> +}
> +
> +void __init suspend_block_init(void)
> +{
> +	suspend_blocker_init(&main_suspend_blocker, "main");
> +	suspend_block(&main_suspend_blocker);
> +}

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-30 22:36 ` Arve Hjønnevåg
  2010-05-02  6:56   ` Pavel Machek
  2010-05-02  6:56   ` Pavel Machek
@ 2010-05-02  7:01   ` Pavel Machek
  2010-05-02  7:01   ` Pavel Machek
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-02  7:01 UTC (permalink / raw)
  To: Arve Hj??nnev??g
  Cc: Len Brown, linux-doc, linux-kernel, Jesse Barnes, Oleg Nesterov,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Fri 2010-04-30 15:36:54, Arve Hj??nnev??g wrote:
> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.
> 
> Signed-off-by: Arve Hj??nnev??g <arve@android.com>

Also note that this patch mixes up adding new in-kernel interface with
adding new userland API.

The in-kernel interface seems to be sane and it would be nice to merge
it soon, unfortunately the strange userland API is in the same patch :-(.

> ---
>  Documentation/power/opportunistic-suspend.txt |  119 +++++++++++
>  include/linux/suspend_blocker.h               |   64 ++++++
>  kernel/power/Kconfig                          |   16 ++
>  kernel/power/Makefile                         |    1 +
>  kernel/power/main.c                           |   92 ++++++++-
>  kernel/power/power.h                          |    9 +
>  kernel/power/suspend.c                        |    4 +-
>  kernel/power/suspend_blocker.c                |  261 +++++++++++++++++++++++++
>  8 files changed, 559 insertions(+), 7 deletions(-)
>  create mode 100644 Documentation/power/opportunistic-suspend.txt
>  create mode 100755 include/linux/suspend_blocker.h
>  create mode 100644 kernel/power/suspend_blocker.c
> 
> diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
> new file mode 100644
> index 0000000..3d060e8
> --- /dev/null
> +++ b/Documentation/power/opportunistic-suspend.txt
> @@ -0,0 +1,119 @@
> +Opportunistic Suspend
> +=====================
> +
> +Opportunistic suspend is a feature allowing the system to be suspended (ie. put
> +into one of the available sleep states) automatically whenever it is regarded
> +as idle.  The suspend blockers framework described below is used to determine
> +when that happens.
> +
> +The /sys/power/policy sysfs attribute is used to switch the system between the
> +opportunistic and "forced" suspend behavior, where in the latter case the
> +system is only suspended if a specific value, corresponding to one of the
> +available system sleep states, is written into /sys/power/state.  However, in
> +the former, opportunistic, case the system is put into the sleep state
> +corresponding to the value written to /sys/power/state whenever there are no
> +active suspend blockers. The default policy is "forced". Also, suspend blockers
> +do not affect sleep states entered from idle.
> +
> +When the policy is "opportunisic", there is a special value, "on", that can be
> +written to /sys/power/state. This will block the automatic sleep request, as if
> +a suspend blocker was used by a device driver. This way the opportunistic
> +suspend may be blocked by user space whithout switching back to the "forced"
> +mode.
> +
> +A suspend blocker is an object used to inform the PM subsystem when the system
> +can or cannot be suspended in the "opportunistic" mode (the "forced" mode
> +ignores suspend blockers).  To use it, a device driver creates a struct
> +suspend_blocker that must be initialized with suspend_blocker_init(). Before
> +freeing the suspend_blocker structure or its name, suspend_blocker_destroy()
> +must be called on it.
> +
> +A suspend blocker is activated using suspend_block(), which prevents the PM
> +subsystem from putting the system into the requested sleep state in the
> +"opportunistic" mode until the suspend blocker is deactivated with
> +suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
> +the system will not suspend as long as at least one of them is active.
> +
> +If opportunistic suspend is already in progress when suspend_block() is called,
> +it will abort the suspend, unless suspend_ops->enter has already been
> +executed. If suspend is aborted this way, the system is usually not fully
> +operational at that point. The suspend callbacks of some drivers may still be
> +running and it usually takes time to restore the system to the fully operational
> +state.
> +
> +Here's an example showing how a cell phone or other embedded system can handle
> +keystrokes (or other input events) in the presence of suspend blockers. Use
> +set_irq_wake or a platform specific api to make sure the keypad interrupt wakes
> +up the cpu. Once the keypad driver has resumed, the sequence of events can look
> +like this:
> +
> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> +- The keypad-scan code detects a key change and reports it to the input-event
> +  driver.
> +- The input-event driver sees the key change, enqueues an event, and calls
> +  suspend_block on the input-event-queue suspend_blocker.
> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> +  on the keypad-scan suspend_blocker.
> +- The user-space input-event thread returns from select/poll, calls
> +  suspend_block on the process-input-events suspend_blocker and then calls read
> +  on the input-event device.
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> +- The user-space input-event thread returns from read. If it determines that
> +  the key should be ignored, it calls suspend_unblock on the
> +  process_input_events suspend_blocker and then calls select or poll. The
> +  system will automatically suspend again, since now no suspend blockers are
> +  active.
> +
> +If the key that was pressed instead should preform a simple action (for example,
> +adjusting the volume), this action can be performed right before calling
> +suspend_unblock on the process_input_events suspend_blocker. However, if the key
> +triggers a longer-running action, that action needs its own suspend_blocker and
> +suspend_block must be called on that suspend blocker before calling
> +suspend_unblock on the process_input_events suspend_blocker.
> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++
> +
> +
> +Driver API
> +==========
> +
> +A driver can use the suspend block api by adding a suspend_blocker variable to
> +its state and calling suspend_blocker_init. For instance:
> +struct state {
> +	struct suspend_blocker suspend_blocker;
> +}
> +
> +init() {
> +	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
> +}
> +
> +Before freeing the memory, suspend_blocker_destroy must be called:
> +
> +uninit() {
> +	suspend_blocker_destroy(&state->suspend_blocker);
> +}
> +
> +When the driver determines that it needs to run (usually in an interrupt
> +handler) it calls suspend_block:
> +	suspend_block(&state->suspend_blocker);
> +
> +When it no longer needs to run it calls suspend_unblock:
> +	suspend_unblock(&state->suspend_blocker);
> +
> +Calling suspend_block when the suspend blocker is active or suspend_unblock when
> +it is not active has no effect (i.e., these functions don't nest). This allows
> +drivers to update their state and call suspend suspend_block or suspend_unblock
> +based on the result.
> +For instance:
> +
> +if (list_empty(&state->pending_work))
> +	suspend_unblock(&state->suspend_blocker);
> +else
> +	suspend_block(&state->suspend_blocker);
> +
> diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
> new file mode 100755
> index 0000000..f9928cc
> --- /dev/null
> +++ b/include/linux/suspend_blocker.h
> @@ -0,0 +1,64 @@
> +/* include/linux/suspend_blocker.h
> + *
> + * Copyright (C) 2007-2009 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _LINUX_SUSPEND_BLOCKER_H
> +#define _LINUX_SUSPEND_BLOCKER_H
> +
> +#include <linux/list.h>
> +
> +/**
> + * struct suspend_blocker - the basic suspend_blocker structure
> + * @link:	List entry for active or inactive list.
> + * @flags:	Tracks initialized and active state.
> + * @name:	Name used for debugging.
> + *
> + * When a suspend_blocker is active it prevents the system from entering
> + * opportunistic suspend.
> + *
> + * The suspend_blocker structure must be initialized by suspend_blocker_init()
> + */
> +
> +struct suspend_blocker {
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	struct list_head    link;
> +	int                 flags;
> +	const char         *name;
> +#endif
> +};
> +
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
> +void suspend_blocker_destroy(struct suspend_blocker *blocker);
> +void suspend_block(struct suspend_blocker *blocker);
> +void suspend_unblock(struct suspend_blocker *blocker);
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker);
> +bool suspend_is_blocked(void);
> +
> +#else
> +
> +static inline void suspend_blocker_init(struct suspend_blocker *blocker,
> +					const char *name) {}
> +static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
> +static inline void suspend_block(struct suspend_blocker *blocker) {}
> +static inline void suspend_unblock(struct suspend_blocker *blocker) {}
> +static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
> +								{ return 0; }
> +static inline bool suspend_is_blocked(void) { return 0; }
> +
> +#endif
> +
> +#endif
> +
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 5c36ea9..55a06a1 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -130,6 +130,22 @@ config SUSPEND_FREEZER
>  
>  	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
>  
> +config OPPORTUNISTIC_SUSPEND
> +	bool "Suspend blockers"
> +	depends on PM_SLEEP
> +	select RTC_LIB
> +	default n
> +	---help---
> +	  Opportunistic sleep support.  Allows the system to be put into a sleep
> +	  state opportunistically, if it doesn't do any useful work at the
> +	  moment. The PM subsystem is switched into this mode of operation by
> +	  writing "opportunistic" into /sys/power/policy, while writing
> +	  "forced" to this file turns the opportunistic suspend feature off.
> +	  In the "opportunistic" mode suspend blockers are used to determine
> +	  when to suspend the system and the value written to /sys/power/state
> +	  determines the sleep state the system will be put into when there are
> +	  no active suspend blockers.
> +
>  config HIBERNATION_NVS
>  	bool
>  
> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
> index 4319181..ee5276d 100644
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
>  obj-$(CONFIG_PM_SLEEP)		+= console.o
>  obj-$(CONFIG_FREEZER)		+= process.o
>  obj-$(CONFIG_SUSPEND)		+= suspend.o
> +obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= suspend_blocker.o
>  obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
>  obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
>  obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
> diff --git a/kernel/power/main.c b/kernel/power/main.c
> index b58800b..030ecdc 100644
> --- a/kernel/power/main.c
> +++ b/kernel/power/main.c
> @@ -12,6 +12,7 @@
>  #include <linux/string.h>
>  #include <linux/resume-trace.h>
>  #include <linux/workqueue.h>
> +#include <linux/suspend_blocker.h>
>  
>  #include "power.h"
>  
> @@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
>  unsigned int pm_flags;
>  EXPORT_SYMBOL(pm_flags);
>  
> +struct policy {
> +	const char *name;
> +	bool (*valid_state)(suspend_state_t state);
> +	int (*set_state)(suspend_state_t state);
> +};
> +static struct policy policies[] = {
> +	{
> +		.name		= "forced",
> +		.valid_state	= valid_state,
> +		.set_state	= enter_state,
> +	},
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	{
> +		.name		= "opportunistic",
> +		.valid_state	= request_suspend_valid_state,
> +		.set_state	= request_suspend_state,
> +	},
> +#endif
> +};
> +static int policy;
> +
>  #ifdef CONFIG_PM_SLEEP
>  
>  /* Routines for PM-transition notifications */
> @@ -146,6 +168,12 @@ struct kobject *power_kobj;
>   *
>   *	store() accepts one of those strings, translates it into the 
>   *	proper enumerated value, and initiates a suspend transition.
> + *
> + *	If policy is set to opportunistic, store() does not block until the
> + *	system resumes, and it will try to re-enter the state until another
> + *	state is requested. Suspend blockers are respected and the requested
> + *	state will only be entered when no suspend blockers are active.
> + *	Write "on" to cancel.
>   */
>  static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  			  char *buf)
> @@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  	int i;
>  
>  	for (i = 0; i < PM_SUSPEND_MAX; i++) {
> -		if (pm_states[i] && valid_state(i))
> +		if (pm_states[i] && policies[policy].valid_state(i))
>  			s += sprintf(s,"%s ", pm_states[i]);
>  	}
>  #endif
>  #ifdef CONFIG_HIBERNATION
> -	s += sprintf(s, "%s\n", "disk");
> +	if (!policy)
> +		s += sprintf(s, "%s\n", "disk");
>  #else
>  	if (s != buf)
>  		/* convert the last space to a newline */
> @@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			   const char *buf, size_t n)
>  {
>  #ifdef CONFIG_SUSPEND
> -	suspend_state_t state = PM_SUSPEND_STANDBY;
> +	suspend_state_t state = PM_SUSPEND_ON;
>  	const char * const *s;
>  #endif
>  	char *p;
> @@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  	len = p ? p - buf : n;
>  
>  	/* First, check if we are requested to hibernate */
> -	if (len == 4 && !strncmp(buf, "disk", len)) {
> +	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
>  		error = hibernate();
>    goto Exit;
>  	}
> @@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			break;
>  	}
>  	if (state < PM_SUSPEND_MAX && *s)
> -		error = enter_state(state);
> +		error = policies[policy].set_state(state);
>  #endif
>  
>   Exit:
> @@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  
>  power_attr(state);
>  
> +/**
> + *	policy - set policy for state
> + */
> +
> +static ssize_t policy_show(struct kobject *kobj,
> +			   struct kobj_attribute *attr, char *buf)
> +{
> +	char *s = buf;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		if (i == policy)
> +			s += sprintf(s, "[%s] ", policies[i].name);
> +		else
> +			s += sprintf(s, "%s ", policies[i].name);
> +	}
> +	if (s != buf)
> +		/* convert the last space to a newline */
> +		*(s-1) = '\n';
> +	return (s - buf);
> +}
> +
> +static ssize_t policy_store(struct kobject *kobj,
> +			    struct kobj_attribute *attr,
> +			    const char *buf, size_t n)
> +{
> +	const char *s;
> +	char *p;
> +	int len;
> +	int i;
> +
> +	p = memchr(buf, '\n', n);
> +	len = p ? p - buf : n;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		s = policies[i].name;
> +		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
> +			mutex_lock(&pm_mutex);
> +			policies[policy].set_state(PM_SUSPEND_ON);
> +			policy = i;
> +			mutex_unlock(&pm_mutex);
> +			return n;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +power_attr(policy);
> +
>  #ifdef CONFIG_PM_TRACE
>  int pm_trace_enabled;
>  
> @@ -231,6 +309,7 @@ power_attr(pm_trace);
>  
>  static struct attribute * g[] = {
>  	&state_attr.attr,
> +	&policy_attr.attr,
>  #ifdef CONFIG_PM_TRACE
>  	&pm_trace_attr.attr,
>  #endif
> @@ -247,7 +326,7 @@ static struct attribute_group attr_group = {
>  	.attrs = g,
>  };
>  
> -#ifdef CONFIG_PM_RUNTIME
> +#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_OPPORTUNISTIC_SUSPEND)
>  struct workqueue_struct *pm_wq;
>  EXPORT_SYMBOL_GPL(pm_wq);
>  
> @@ -266,6 +345,7 @@ static int __init pm_init(void)
>  	int error = pm_start_workqueue();
>  	if (error)
>  		return error;
> +	suspend_block_init();
>  	power_kobj = kobject_create_and_add("power", NULL);
>  	if (!power_kobj)
>  		return -ENOMEM;
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index 46c5a26..67d10fd 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -236,3 +236,12 @@ static inline void suspend_thaw_processes(void)
>  {
>  }
>  #endif
> +
> +/* kernel/power/suspend_block.c */
> +extern int request_suspend_state(suspend_state_t state);
> +extern bool request_suspend_valid_state(suspend_state_t state);
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +extern void __init suspend_block_init(void);
> +#else
> +static inline void suspend_block_init(void) {}
> +#endif
> diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
> index 56e7dbb..dc42006 100644
> --- a/kernel/power/suspend.c
> +++ b/kernel/power/suspend.c
> @@ -16,10 +16,12 @@
>  #include <linux/cpu.h>
>  #include <linux/syscalls.h>
>  #include <linux/gfp.h>
> +#include <linux/suspend_blocker.h>
>  
>  #include "power.h"
>  
>  const char *const pm_states[PM_SUSPEND_MAX] = {
> +	[PM_SUSPEND_ON]		= "on",
>  	[PM_SUSPEND_STANDBY]	= "standby",
>  	[PM_SUSPEND_MEM]	= "mem",
>  };
> @@ -157,7 +159,7 @@ static int suspend_enter(suspend_state_t state)
>  
>  	error = sysdev_suspend(PMSG_SUSPEND);
>  	if (!error) {
> -		if (!suspend_test(TEST_CORE))
> +		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
>  			error = suspend_ops->enter(state);
>  		sysdev_resume();
>  	}
> diff --git a/kernel/power/suspend_blocker.c b/kernel/power/suspend_blocker.c
> new file mode 100644
> index 0000000..2c58b21
> --- /dev/null
> +++ b/kernel/power/suspend_blocker.c
> @@ -0,0 +1,261 @@
> +/* kernel/power/suspend_blocker.c
> + *
> + * Copyright (C) 2005-2010 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/suspend.h>
> +#include <linux/suspend_blocker.h>
> +#include "power.h"
> +
> +extern struct workqueue_struct *pm_wq;
> +
> +enum {
> +	DEBUG_EXIT_SUSPEND = 1U << 0,
> +	DEBUG_WAKEUP = 1U << 1,
> +	DEBUG_USER_STATE = 1U << 2,
> +	DEBUG_SUSPEND = 1U << 3,
> +	DEBUG_SUSPEND_BLOCKER = 1U << 4,
> +};
> +static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
> +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
> +
> +#define SB_INITIALIZED            (1U << 8)
> +#define SB_ACTIVE                 (1U << 9)
> +
> +static DEFINE_SPINLOCK(list_lock);
> +static DEFINE_SPINLOCK(state_lock);
> +static LIST_HEAD(inactive_blockers);
> +static LIST_HEAD(active_blockers);
> +static int current_event_num;
> +struct suspend_blocker main_suspend_blocker;
> +static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
> +static bool enable_suspend_blockers;
> +
> +#define pr_info_time(fmt, args...) \
> +	do { \
> +		struct timespec ts; \
> +		struct rtc_time tm; \
> +		getnstimeofday(&ts); \
> +		rtc_time_to_tm(ts.tv_sec, &tm); \
> +		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
> +			args, \
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
> +	} while (0);
> +
> +static void print_active_blockers_locked(void)
> +{
> +	struct suspend_blocker *blocker;
> +
> +	list_for_each_entry(blocker, &active_blockers, link)
> +		pr_info("active suspend blocker %s\n", blocker->name);
> +}
> +
> +/**
> + * suspend_is_blocked() - Check if suspend should be blocked
> + *
> + * suspend_is_blocked can be used by generic power management code to abort
> + * suspend.
> + *
> + * To preserve backward compatibility suspend_is_blocked returns 0 unless it
> + * is called during suspend initiated from the suspend_block code.
> + */
> +bool suspend_is_blocked(void)
> +{
> +	if (!enable_suspend_blockers)
> +		return 0;
> +	return !list_empty(&active_blockers);
> +}
> +
> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = true;
> +	while (!suspend_is_blocked()) {
> +		entry_event_num = current_event_num;
> +
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: enter suspend\n");
> +
> +		ret = pm_suspend(requested_suspend_state);
> +
> +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> +
> +		if (current_event_num == entry_event_num)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +	}
> +	enable_suspend_blockers = false;
> +}
> +static DECLARE_WORK(suspend_work, suspend_worker);
> +
> +/**
> + * suspend_blocker_init() - Initialize a suspend blocker
> + * @blocker:	The suspend blocker to initialize.
> + * @name:	The name of the suspend blocker to show in debug messages.
> + *
> + * The suspend blocker struct and name must not be freed before calling
> + * suspend_blocker_destroy.
> + */
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
> +{
> +	unsigned long irqflags = 0;
> +
> +	WARN_ON(!name);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_init name=%s\n", name);
> +
> +	blocker->name = name;
> +	blocker->flags = SB_INITIALIZED;
> +	INIT_LIST_HEAD(&blocker->link);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	list_add(&blocker->link, &inactive_blockers);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_init);
> +
> +/**
> + * suspend_blocker_destroy() - Destroy a suspend blocker
> + * @blocker:	The suspend blocker to destroy.
> + */
> +void suspend_blocker_destroy(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags &= ~SB_INITIALIZED;
> +	list_del(&blocker->link);
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(pm_wq, &suspend_work);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_destroy);
> +
> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +
> +	blocker->flags |= SB_ACTIVE;
> +	list_move(&blocker->link, &active_blockers);
> +	current_event_num++;
> +
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_block);
> +
> +/**
> + * suspend_unblock() - Unblock suspend
> + * @blocker:	The suspend blocker to unblock.
> + *
> + * If no other suspend blockers block suspend, the system will suspend.
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_unblock(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_unblock: %s\n", blocker->name);
> +
> +	list_move(&blocker->link, &inactive_blockers);
> +
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(pm_wq, &suspend_work);
> +	blocker->flags &= ~(SB_ACTIVE);
> +	if (blocker == &main_suspend_blocker) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			print_active_blockers_locked();
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_unblock);
> +
> +/**
> + * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
> + * @blocker:	The suspend blocker to check.
> + *
> + * Returns true if the suspend_blocker is currently active.
> + */
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker)
> +{
> +	WARN_ON(!(blocker->flags & SB_INITIALIZED));
> +
> +	return !!(blocker->flags & SB_ACTIVE);
> +}
> +EXPORT_SYMBOL(suspend_blocker_is_active);
> +
> +bool request_suspend_valid_state(suspend_state_t state)
> +{
> +	return (state == PM_SUSPEND_ON) || valid_state(state);
> +}
> +
> +int request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +
> +	if (!request_suspend_valid_state(state))
> +		return -ENODEV;
> +
> +	spin_lock_irqsave(&state_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_USER_STATE)
> +		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
> +			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> +			     requested_suspend_state, state,
> +			     ktime_to_ns(ktime_get()));
> +
> +	requested_suspend_state = state;
> +	if (state == PM_SUSPEND_ON)
> +		suspend_block(&main_suspend_blocker);
> +	else
> +		suspend_unblock(&main_suspend_blocker);
> +	spin_unlock_irqrestore(&state_lock, irqflags);
> +	return 0;
> +}
> +
> +void __init suspend_block_init(void)
> +{
> +	suspend_blocker_init(&main_suspend_blocker, "main");
> +	suspend_block(&main_suspend_blocker);
> +}

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-30 22:36 ` Arve Hjønnevåg
  2010-05-02  6:56   ` Pavel Machek
@ 2010-05-02  6:56   ` Pavel Machek
  2010-05-02 20:10     ` Rafael J. Wysocki
  2010-05-02 20:10     ` Rafael J. Wysocki
  2010-05-02  7:01   ` Pavel Machek
                     ` (4 subsequent siblings)
  6 siblings, 2 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-02  6:56 UTC (permalink / raw)
  To: Arve Hj??nnev??g
  Cc: linux-pm, linux-kernel, Rafael J. Wysocki, Alan Stern, Tejun Heo,
	Oleg Nesterov, Len Brown, Randy Dunlap, Jesse Barnes,
	Magnus Damm, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

Hi!

> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.

As I explained before (and got no reply),  the proposed interface is
ugly. It uses one sysfs file to change semantics of another one.

> Signed-off-by: Arve Hj??nnev??g <arve@android.com>

NAK.

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-30 22:36 ` Arve Hjønnevåg
@ 2010-05-02  6:56   ` Pavel Machek
  2010-05-02  6:56   ` Pavel Machek
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-05-02  6:56 UTC (permalink / raw)
  To: Arve Hj??nnev??g
  Cc: Len Brown, linux-doc, linux-kernel, Jesse Barnes, Oleg Nesterov,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

Hi!

> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.

As I explained before (and got no reply),  the proposed interface is
ugly. It uses one sysfs file to change semantics of another one.

> Signed-off-by: Arve Hj??nnev??g <arve@android.com>

NAK.

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* [PATCH 1/8] PM: Add suspend block api.
  2010-04-30 22:36 [PATCH 0/8] Suspend block api (version 6) Arve Hjønnevåg
  2010-04-30 22:36 ` [PATCH 1/8] PM: Add suspend block api Arve Hjønnevåg
@ 2010-04-30 22:36 ` Arve Hjønnevåg
  2010-05-02  6:56   ` Pavel Machek
                     ` (6 more replies)
  1 sibling, 7 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-04-30 22:36 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: Rafael J. Wysocki, Alan Stern, Tejun Heo, Oleg Nesterov,
	Arve Hjønnevåg, Len Brown, Pavel Machek, Randy Dunlap,
	Jesse Barnes, Magnus Damm, Nigel Cunningham, Cornelia Huck,
	Ming Lei, Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

Adds /sys/power/policy that selects the behaviour of /sys/power/state.
After setting the policy to opportunistic, writes to /sys/power/state
become non-blocking requests that specify which suspend state to enter
when no suspend blockers are active. A special state, "on", stops the
process by activating the "main" suspend blocker.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/opportunistic-suspend.txt |  119 +++++++++++
 include/linux/suspend_blocker.h               |   64 ++++++
 kernel/power/Kconfig                          |   16 ++
 kernel/power/Makefile                         |    1 +
 kernel/power/main.c                           |   92 ++++++++-
 kernel/power/power.h                          |    9 +
 kernel/power/suspend.c                        |    4 +-
 kernel/power/suspend_blocker.c                |  261 +++++++++++++++++++++++++
 8 files changed, 559 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/power/opportunistic-suspend.txt
 create mode 100755 include/linux/suspend_blocker.h
 create mode 100644 kernel/power/suspend_blocker.c

diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
new file mode 100644
index 0000000..3d060e8
--- /dev/null
+++ b/Documentation/power/opportunistic-suspend.txt
@@ -0,0 +1,119 @@
+Opportunistic Suspend
+=====================
+
+Opportunistic suspend is a feature allowing the system to be suspended (ie. put
+into one of the available sleep states) automatically whenever it is regarded
+as idle.  The suspend blockers framework described below is used to determine
+when that happens.
+
+The /sys/power/policy sysfs attribute is used to switch the system between the
+opportunistic and "forced" suspend behavior, where in the latter case the
+system is only suspended if a specific value, corresponding to one of the
+available system sleep states, is written into /sys/power/state.  However, in
+the former, opportunistic, case the system is put into the sleep state
+corresponding to the value written to /sys/power/state whenever there are no
+active suspend blockers. The default policy is "forced". Also, suspend blockers
+do not affect sleep states entered from idle.
+
+When the policy is "opportunisic", there is a special value, "on", that can be
+written to /sys/power/state. This will block the automatic sleep request, as if
+a suspend blocker was used by a device driver. This way the opportunistic
+suspend may be blocked by user space whithout switching back to the "forced"
+mode.
+
+A suspend blocker is an object used to inform the PM subsystem when the system
+can or cannot be suspended in the "opportunistic" mode (the "forced" mode
+ignores suspend blockers).  To use it, a device driver creates a struct
+suspend_blocker that must be initialized with suspend_blocker_init(). Before
+freeing the suspend_blocker structure or its name, suspend_blocker_destroy()
+must be called on it.
+
+A suspend blocker is activated using suspend_block(), which prevents the PM
+subsystem from putting the system into the requested sleep state in the
+"opportunistic" mode until the suspend blocker is deactivated with
+suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
+the system will not suspend as long as at least one of them is active.
+
+If opportunistic suspend is already in progress when suspend_block() is called,
+it will abort the suspend, unless suspend_ops->enter has already been
+executed. If suspend is aborted this way, the system is usually not fully
+operational at that point. The suspend callbacks of some drivers may still be
+running and it usually takes time to restore the system to the fully operational
+state.
+
+Here's an example showing how a cell phone or other embedded system can handle
+keystrokes (or other input events) in the presence of suspend blockers. Use
+set_irq_wake or a platform specific api to make sure the keypad interrupt wakes
+up the cpu. Once the keypad driver has resumed, the sequence of events can look
+like this:
+
+- The Keypad driver gets an interrupt. It then calls suspend_block on the
+  keypad-scan suspend_blocker and starts scanning the keypad matrix.
+- The keypad-scan code detects a key change and reports it to the input-event
+  driver.
+- The input-event driver sees the key change, enqueues an event, and calls
+  suspend_block on the input-event-queue suspend_blocker.
+- The keypad-scan code detects that no keys are held and calls suspend_unblock
+  on the keypad-scan suspend_blocker.
+- The user-space input-event thread returns from select/poll, calls
+  suspend_block on the process-input-events suspend_blocker and then calls read
+  on the input-event device.
+- The input-event driver dequeues the key-event and, since the queue is now
+  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
+- The user-space input-event thread returns from read. If it determines that
+  the key should be ignored, it calls suspend_unblock on the
+  process_input_events suspend_blocker and then calls select or poll. The
+  system will automatically suspend again, since now no suspend blockers are
+  active.
+
+If the key that was pressed instead should preform a simple action (for example,
+adjusting the volume), this action can be performed right before calling
+suspend_unblock on the process_input_events suspend_blocker. However, if the key
+triggers a longer-running action, that action needs its own suspend_blocker and
+suspend_block must be called on that suspend blocker before calling
+suspend_unblock on the process_input_events suspend_blocker.
+
+                 Key pressed   Key released
+                     |             |
+keypad-scan          ++++++++++++++++++
+input-event-queue        +++          +++
+process-input-events       +++          +++
+
+
+Driver API
+==========
+
+A driver can use the suspend block api by adding a suspend_blocker variable to
+its state and calling suspend_blocker_init. For instance:
+struct state {
+	struct suspend_blocker suspend_blocker;
+}
+
+init() {
+	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
+}
+
+Before freeing the memory, suspend_blocker_destroy must be called:
+
+uninit() {
+	suspend_blocker_destroy(&state->suspend_blocker);
+}
+
+When the driver determines that it needs to run (usually in an interrupt
+handler) it calls suspend_block:
+	suspend_block(&state->suspend_blocker);
+
+When it no longer needs to run it calls suspend_unblock:
+	suspend_unblock(&state->suspend_blocker);
+
+Calling suspend_block when the suspend blocker is active or suspend_unblock when
+it is not active has no effect (i.e., these functions don't nest). This allows
+drivers to update their state and call suspend suspend_block or suspend_unblock
+based on the result.
+For instance:
+
+if (list_empty(&state->pending_work))
+	suspend_unblock(&state->suspend_blocker);
+else
+	suspend_block(&state->suspend_blocker);
+
diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
new file mode 100755
index 0000000..f9928cc
--- /dev/null
+++ b/include/linux/suspend_blocker.h
@@ -0,0 +1,64 @@
+/* include/linux/suspend_blocker.h
+ *
+ * Copyright (C) 2007-2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SUSPEND_BLOCKER_H
+#define _LINUX_SUSPEND_BLOCKER_H
+
+#include <linux/list.h>
+
+/**
+ * struct suspend_blocker - the basic suspend_blocker structure
+ * @link:	List entry for active or inactive list.
+ * @flags:	Tracks initialized and active state.
+ * @name:	Name used for debugging.
+ *
+ * When a suspend_blocker is active it prevents the system from entering
+ * opportunistic suspend.
+ *
+ * The suspend_blocker structure must be initialized by suspend_blocker_init()
+ */
+
+struct suspend_blocker {
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	struct list_head    link;
+	int                 flags;
+	const char         *name;
+#endif
+};
+
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
+void suspend_blocker_destroy(struct suspend_blocker *blocker);
+void suspend_block(struct suspend_blocker *blocker);
+void suspend_unblock(struct suspend_blocker *blocker);
+bool suspend_blocker_is_active(struct suspend_blocker *blocker);
+bool suspend_is_blocked(void);
+
+#else
+
+static inline void suspend_blocker_init(struct suspend_blocker *blocker,
+					const char *name) {}
+static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
+static inline void suspend_block(struct suspend_blocker *blocker) {}
+static inline void suspend_unblock(struct suspend_blocker *blocker) {}
+static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
+								{ return 0; }
+static inline bool suspend_is_blocked(void) { return 0; }
+
+#endif
+
+#endif
+
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 5c36ea9..55a06a1 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -130,6 +130,22 @@ config SUSPEND_FREEZER
 
 	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
 
+config OPPORTUNISTIC_SUSPEND
+	bool "Suspend blockers"
+	depends on PM_SLEEP
+	select RTC_LIB
+	default n
+	---help---
+	  Opportunistic sleep support.  Allows the system to be put into a sleep
+	  state opportunistically, if it doesn't do any useful work at the
+	  moment. The PM subsystem is switched into this mode of operation by
+	  writing "opportunistic" into /sys/power/policy, while writing
+	  "forced" to this file turns the opportunistic suspend feature off.
+	  In the "opportunistic" mode suspend blockers are used to determine
+	  when to suspend the system and the value written to /sys/power/state
+	  determines the sleep state the system will be put into when there are
+	  no active suspend blockers.
+
 config HIBERNATION_NVS
 	bool
 
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 4319181..ee5276d 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
+obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= suspend_blocker.o
 obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
 obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
 obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
diff --git a/kernel/power/main.c b/kernel/power/main.c
index b58800b..030ecdc 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -12,6 +12,7 @@
 #include <linux/string.h>
 #include <linux/resume-trace.h>
 #include <linux/workqueue.h>
+#include <linux/suspend_blocker.h>
 
 #include "power.h"
 
@@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
 unsigned int pm_flags;
 EXPORT_SYMBOL(pm_flags);
 
+struct policy {
+	const char *name;
+	bool (*valid_state)(suspend_state_t state);
+	int (*set_state)(suspend_state_t state);
+};
+static struct policy policies[] = {
+	{
+		.name		= "forced",
+		.valid_state	= valid_state,
+		.set_state	= enter_state,
+	},
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	{
+		.name		= "opportunistic",
+		.valid_state	= request_suspend_valid_state,
+		.set_state	= request_suspend_state,
+	},
+#endif
+};
+static int policy;
+
 #ifdef CONFIG_PM_SLEEP
 
 /* Routines for PM-transition notifications */
@@ -146,6 +168,12 @@ struct kobject *power_kobj;
  *
  *	store() accepts one of those strings, translates it into the 
  *	proper enumerated value, and initiates a suspend transition.
+ *
+ *	If policy is set to opportunistic, store() does not block until the
+ *	system resumes, and it will try to re-enter the state until another
+ *	state is requested. Suspend blockers are respected and the requested
+ *	state will only be entered when no suspend blockers are active.
+ *	Write "on" to cancel.
  */
 static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 			  char *buf)
@@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 	int i;
 
 	for (i = 0; i < PM_SUSPEND_MAX; i++) {
-		if (pm_states[i] && valid_state(i))
+		if (pm_states[i] && policies[policy].valid_state(i))
 			s += sprintf(s,"%s ", pm_states[i]);
 	}
 #endif
 #ifdef CONFIG_HIBERNATION
-	s += sprintf(s, "%s\n", "disk");
+	if (!policy)
+		s += sprintf(s, "%s\n", "disk");
 #else
 	if (s != buf)
 		/* convert the last space to a newline */
@@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			   const char *buf, size_t n)
 {
 #ifdef CONFIG_SUSPEND
-	suspend_state_t state = PM_SUSPEND_STANDBY;
+	suspend_state_t state = PM_SUSPEND_ON;
 	const char * const *s;
 #endif
 	char *p;
@@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 	len = p ? p - buf : n;
 
 	/* First, check if we are requested to hibernate */
-	if (len == 4 && !strncmp(buf, "disk", len)) {
+	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
 		error = hibernate();
   goto Exit;
 	}
@@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			break;
 	}
 	if (state < PM_SUSPEND_MAX && *s)
-		error = enter_state(state);
+		error = policies[policy].set_state(state);
 #endif
 
  Exit:
@@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 
 power_attr(state);
 
+/**
+ *	policy - set policy for state
+ */
+
+static ssize_t policy_show(struct kobject *kobj,
+			   struct kobj_attribute *attr, char *buf)
+{
+	char *s = buf;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		if (i == policy)
+			s += sprintf(s, "[%s] ", policies[i].name);
+		else
+			s += sprintf(s, "%s ", policies[i].name);
+	}
+	if (s != buf)
+		/* convert the last space to a newline */
+		*(s-1) = '\n';
+	return (s - buf);
+}
+
+static ssize_t policy_store(struct kobject *kobj,
+			    struct kobj_attribute *attr,
+			    const char *buf, size_t n)
+{
+	const char *s;
+	char *p;
+	int len;
+	int i;
+
+	p = memchr(buf, '\n', n);
+	len = p ? p - buf : n;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		s = policies[i].name;
+		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
+			mutex_lock(&pm_mutex);
+			policies[policy].set_state(PM_SUSPEND_ON);
+			policy = i;
+			mutex_unlock(&pm_mutex);
+			return n;
+		}
+	}
+	return -EINVAL;
+}
+
+power_attr(policy);
+
 #ifdef CONFIG_PM_TRACE
 int pm_trace_enabled;
 
@@ -231,6 +309,7 @@ power_attr(pm_trace);
 
 static struct attribute * g[] = {
 	&state_attr.attr,
+	&policy_attr.attr,
 #ifdef CONFIG_PM_TRACE
 	&pm_trace_attr.attr,
 #endif
@@ -247,7 +326,7 @@ static struct attribute_group attr_group = {
 	.attrs = g,
 };
 
-#ifdef CONFIG_PM_RUNTIME
+#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_OPPORTUNISTIC_SUSPEND)
 struct workqueue_struct *pm_wq;
 EXPORT_SYMBOL_GPL(pm_wq);
 
@@ -266,6 +345,7 @@ static int __init pm_init(void)
 	int error = pm_start_workqueue();
 	if (error)
 		return error;
+	suspend_block_init();
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 46c5a26..67d10fd 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -236,3 +236,12 @@ static inline void suspend_thaw_processes(void)
 {
 }
 #endif
+
+/* kernel/power/suspend_block.c */
+extern int request_suspend_state(suspend_state_t state);
+extern bool request_suspend_valid_state(suspend_state_t state);
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+extern void __init suspend_block_init(void);
+#else
+static inline void suspend_block_init(void) {}
+#endif
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 56e7dbb..dc42006 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -16,10 +16,12 @@
 #include <linux/cpu.h>
 #include <linux/syscalls.h>
 #include <linux/gfp.h>
+#include <linux/suspend_blocker.h>
 
 #include "power.h"
 
 const char *const pm_states[PM_SUSPEND_MAX] = {
+	[PM_SUSPEND_ON]		= "on",
 	[PM_SUSPEND_STANDBY]	= "standby",
 	[PM_SUSPEND_MEM]	= "mem",
 };
@@ -157,7 +159,7 @@ static int suspend_enter(suspend_state_t state)
 
 	error = sysdev_suspend(PMSG_SUSPEND);
 	if (!error) {
-		if (!suspend_test(TEST_CORE))
+		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
 			error = suspend_ops->enter(state);
 		sysdev_resume();
 	}
diff --git a/kernel/power/suspend_blocker.c b/kernel/power/suspend_blocker.c
new file mode 100644
index 0000000..2c58b21
--- /dev/null
+++ b/kernel/power/suspend_blocker.c
@@ -0,0 +1,261 @@
+/* kernel/power/suspend_blocker.c
+ *
+ * Copyright (C) 2005-2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/suspend.h>
+#include <linux/suspend_blocker.h>
+#include "power.h"
+
+extern struct workqueue_struct *pm_wq;
+
+enum {
+	DEBUG_EXIT_SUSPEND = 1U << 0,
+	DEBUG_WAKEUP = 1U << 1,
+	DEBUG_USER_STATE = 1U << 2,
+	DEBUG_SUSPEND = 1U << 3,
+	DEBUG_SUSPEND_BLOCKER = 1U << 4,
+};
+static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SB_INITIALIZED            (1U << 8)
+#define SB_ACTIVE                 (1U << 9)
+
+static DEFINE_SPINLOCK(list_lock);
+static DEFINE_SPINLOCK(state_lock);
+static LIST_HEAD(inactive_blockers);
+static LIST_HEAD(active_blockers);
+static int current_event_num;
+struct suspend_blocker main_suspend_blocker;
+static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
+static bool enable_suspend_blockers;
+
+#define pr_info_time(fmt, args...) \
+	do { \
+		struct timespec ts; \
+		struct rtc_time tm; \
+		getnstimeofday(&ts); \
+		rtc_time_to_tm(ts.tv_sec, &tm); \
+		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
+			args, \
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
+			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
+	} while (0);
+
+static void print_active_blockers_locked(void)
+{
+	struct suspend_blocker *blocker;
+
+	list_for_each_entry(blocker, &active_blockers, link)
+		pr_info("active suspend blocker %s\n", blocker->name);
+}
+
+/**
+ * suspend_is_blocked() - Check if suspend should be blocked
+ *
+ * suspend_is_blocked can be used by generic power management code to abort
+ * suspend.
+ *
+ * To preserve backward compatibility suspend_is_blocked returns 0 unless it
+ * is called during suspend initiated from the suspend_block code.
+ */
+bool suspend_is_blocked(void)
+{
+	if (!enable_suspend_blockers)
+		return 0;
+	return !list_empty(&active_blockers);
+}
+
+static void suspend_worker(struct work_struct *work)
+{
+	int ret;
+	int entry_event_num;
+
+	enable_suspend_blockers = true;
+	while (!suspend_is_blocked()) {
+		entry_event_num = current_event_num;
+
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("suspend: enter suspend\n");
+
+		ret = pm_suspend(requested_suspend_state);
+
+		if (debug_mask & DEBUG_EXIT_SUSPEND)
+			pr_info_time("suspend: exit suspend, ret = %d ", ret);
+
+		if (current_event_num == entry_event_num)
+			pr_info("suspend: pm_suspend returned with no event\n");
+	}
+	enable_suspend_blockers = false;
+}
+static DECLARE_WORK(suspend_work, suspend_worker);
+
+/**
+ * suspend_blocker_init() - Initialize a suspend blocker
+ * @blocker:	The suspend blocker to initialize.
+ * @name:	The name of the suspend blocker to show in debug messages.
+ *
+ * The suspend blocker struct and name must not be freed before calling
+ * suspend_blocker_destroy.
+ */
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
+{
+	unsigned long irqflags = 0;
+
+	WARN_ON(!name);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_blocker_init name=%s\n", name);
+
+	blocker->name = name;
+	blocker->flags = SB_INITIALIZED;
+	INIT_LIST_HEAD(&blocker->link);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	list_add(&blocker->link, &inactive_blockers);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_blocker_init);
+
+/**
+ * suspend_blocker_destroy() - Destroy a suspend blocker
+ * @blocker:	The suspend blocker to destroy.
+ */
+void suspend_blocker_destroy(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	blocker->flags &= ~SB_INITIALIZED;
+	list_del(&blocker->link);
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(pm_wq, &suspend_work);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_blocker_destroy);
+
+/**
+ * suspend_block() - Block suspend
+ * @blocker:	The suspend blocker to use
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_block(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_block: %s\n", blocker->name);
+
+	blocker->flags |= SB_ACTIVE;
+	list_move(&blocker->link, &active_blockers);
+	current_event_num++;
+
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_block);
+
+/**
+ * suspend_unblock() - Unblock suspend
+ * @blocker:	The suspend blocker to unblock.
+ *
+ * If no other suspend blockers block suspend, the system will suspend.
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_unblock(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_unblock: %s\n", blocker->name);
+
+	list_move(&blocker->link, &inactive_blockers);
+
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(pm_wq, &suspend_work);
+	blocker->flags &= ~(SB_ACTIVE);
+	if (blocker == &main_suspend_blocker) {
+		if (debug_mask & DEBUG_SUSPEND)
+			print_active_blockers_locked();
+	}
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_unblock);
+
+/**
+ * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
+ * @blocker:	The suspend blocker to check.
+ *
+ * Returns true if the suspend_blocker is currently active.
+ */
+bool suspend_blocker_is_active(struct suspend_blocker *blocker)
+{
+	WARN_ON(!(blocker->flags & SB_INITIALIZED));
+
+	return !!(blocker->flags & SB_ACTIVE);
+}
+EXPORT_SYMBOL(suspend_blocker_is_active);
+
+bool request_suspend_valid_state(suspend_state_t state)
+{
+	return (state == PM_SUSPEND_ON) || valid_state(state);
+}
+
+int request_suspend_state(suspend_state_t state)
+{
+	unsigned long irqflags;
+
+	if (!request_suspend_valid_state(state))
+		return -ENODEV;
+
+	spin_lock_irqsave(&state_lock, irqflags);
+
+	if (debug_mask & DEBUG_USER_STATE)
+		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
+			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
+			     requested_suspend_state, state,
+			     ktime_to_ns(ktime_get()));
+
+	requested_suspend_state = state;
+	if (state == PM_SUSPEND_ON)
+		suspend_block(&main_suspend_blocker);
+	else
+		suspend_unblock(&main_suspend_blocker);
+	spin_unlock_irqrestore(&state_lock, irqflags);
+	return 0;
+}
+
+void __init suspend_block_init(void)
+{
+	suspend_blocker_init(&main_suspend_blocker, "main");
+	suspend_block(&main_suspend_blocker);
+}
-- 
1.6.5.1


^ permalink raw reply related	[flat|nested] 195+ messages in thread

* [PATCH 1/8] PM: Add suspend block api.
  2010-04-30 22:36 [PATCH 0/8] Suspend block api (version 6) Arve Hjønnevåg
@ 2010-04-30 22:36 ` Arve Hjønnevåg
  2010-04-30 22:36 ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-04-30 22:36 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes, Tejun Heo,
	Magnus Damm, Andrew Morton, Wu Fengguang

Adds /sys/power/policy that selects the behaviour of /sys/power/state.
After setting the policy to opportunistic, writes to /sys/power/state
become non-blocking requests that specify which suspend state to enter
when no suspend blockers are active. A special state, "on", stops the
process by activating the "main" suspend blocker.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/opportunistic-suspend.txt |  119 +++++++++++
 include/linux/suspend_blocker.h               |   64 ++++++
 kernel/power/Kconfig                          |   16 ++
 kernel/power/Makefile                         |    1 +
 kernel/power/main.c                           |   92 ++++++++-
 kernel/power/power.h                          |    9 +
 kernel/power/suspend.c                        |    4 +-
 kernel/power/suspend_blocker.c                |  261 +++++++++++++++++++++++++
 8 files changed, 559 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/power/opportunistic-suspend.txt
 create mode 100755 include/linux/suspend_blocker.h
 create mode 100644 kernel/power/suspend_blocker.c

diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
new file mode 100644
index 0000000..3d060e8
--- /dev/null
+++ b/Documentation/power/opportunistic-suspend.txt
@@ -0,0 +1,119 @@
+Opportunistic Suspend
+=====================
+
+Opportunistic suspend is a feature allowing the system to be suspended (ie. put
+into one of the available sleep states) automatically whenever it is regarded
+as idle.  The suspend blockers framework described below is used to determine
+when that happens.
+
+The /sys/power/policy sysfs attribute is used to switch the system between the
+opportunistic and "forced" suspend behavior, where in the latter case the
+system is only suspended if a specific value, corresponding to one of the
+available system sleep states, is written into /sys/power/state.  However, in
+the former, opportunistic, case the system is put into the sleep state
+corresponding to the value written to /sys/power/state whenever there are no
+active suspend blockers. The default policy is "forced". Also, suspend blockers
+do not affect sleep states entered from idle.
+
+When the policy is "opportunisic", there is a special value, "on", that can be
+written to /sys/power/state. This will block the automatic sleep request, as if
+a suspend blocker was used by a device driver. This way the opportunistic
+suspend may be blocked by user space whithout switching back to the "forced"
+mode.
+
+A suspend blocker is an object used to inform the PM subsystem when the system
+can or cannot be suspended in the "opportunistic" mode (the "forced" mode
+ignores suspend blockers).  To use it, a device driver creates a struct
+suspend_blocker that must be initialized with suspend_blocker_init(). Before
+freeing the suspend_blocker structure or its name, suspend_blocker_destroy()
+must be called on it.
+
+A suspend blocker is activated using suspend_block(), which prevents the PM
+subsystem from putting the system into the requested sleep state in the
+"opportunistic" mode until the suspend blocker is deactivated with
+suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
+the system will not suspend as long as at least one of them is active.
+
+If opportunistic suspend is already in progress when suspend_block() is called,
+it will abort the suspend, unless suspend_ops->enter has already been
+executed. If suspend is aborted this way, the system is usually not fully
+operational at that point. The suspend callbacks of some drivers may still be
+running and it usually takes time to restore the system to the fully operational
+state.
+
+Here's an example showing how a cell phone or other embedded system can handle
+keystrokes (or other input events) in the presence of suspend blockers. Use
+set_irq_wake or a platform specific api to make sure the keypad interrupt wakes
+up the cpu. Once the keypad driver has resumed, the sequence of events can look
+like this:
+
+- The Keypad driver gets an interrupt. It then calls suspend_block on the
+  keypad-scan suspend_blocker and starts scanning the keypad matrix.
+- The keypad-scan code detects a key change and reports it to the input-event
+  driver.
+- The input-event driver sees the key change, enqueues an event, and calls
+  suspend_block on the input-event-queue suspend_blocker.
+- The keypad-scan code detects that no keys are held and calls suspend_unblock
+  on the keypad-scan suspend_blocker.
+- The user-space input-event thread returns from select/poll, calls
+  suspend_block on the process-input-events suspend_blocker and then calls read
+  on the input-event device.
+- The input-event driver dequeues the key-event and, since the queue is now
+  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
+- The user-space input-event thread returns from read. If it determines that
+  the key should be ignored, it calls suspend_unblock on the
+  process_input_events suspend_blocker and then calls select or poll. The
+  system will automatically suspend again, since now no suspend blockers are
+  active.
+
+If the key that was pressed instead should preform a simple action (for example,
+adjusting the volume), this action can be performed right before calling
+suspend_unblock on the process_input_events suspend_blocker. However, if the key
+triggers a longer-running action, that action needs its own suspend_blocker and
+suspend_block must be called on that suspend blocker before calling
+suspend_unblock on the process_input_events suspend_blocker.
+
+                 Key pressed   Key released
+                     |             |
+keypad-scan          ++++++++++++++++++
+input-event-queue        +++          +++
+process-input-events       +++          +++
+
+
+Driver API
+==========
+
+A driver can use the suspend block api by adding a suspend_blocker variable to
+its state and calling suspend_blocker_init. For instance:
+struct state {
+	struct suspend_blocker suspend_blocker;
+}
+
+init() {
+	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
+}
+
+Before freeing the memory, suspend_blocker_destroy must be called:
+
+uninit() {
+	suspend_blocker_destroy(&state->suspend_blocker);
+}
+
+When the driver determines that it needs to run (usually in an interrupt
+handler) it calls suspend_block:
+	suspend_block(&state->suspend_blocker);
+
+When it no longer needs to run it calls suspend_unblock:
+	suspend_unblock(&state->suspend_blocker);
+
+Calling suspend_block when the suspend blocker is active or suspend_unblock when
+it is not active has no effect (i.e., these functions don't nest). This allows
+drivers to update their state and call suspend suspend_block or suspend_unblock
+based on the result.
+For instance:
+
+if (list_empty(&state->pending_work))
+	suspend_unblock(&state->suspend_blocker);
+else
+	suspend_block(&state->suspend_blocker);
+
diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
new file mode 100755
index 0000000..f9928cc
--- /dev/null
+++ b/include/linux/suspend_blocker.h
@@ -0,0 +1,64 @@
+/* include/linux/suspend_blocker.h
+ *
+ * Copyright (C) 2007-2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SUSPEND_BLOCKER_H
+#define _LINUX_SUSPEND_BLOCKER_H
+
+#include <linux/list.h>
+
+/**
+ * struct suspend_blocker - the basic suspend_blocker structure
+ * @link:	List entry for active or inactive list.
+ * @flags:	Tracks initialized and active state.
+ * @name:	Name used for debugging.
+ *
+ * When a suspend_blocker is active it prevents the system from entering
+ * opportunistic suspend.
+ *
+ * The suspend_blocker structure must be initialized by suspend_blocker_init()
+ */
+
+struct suspend_blocker {
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	struct list_head    link;
+	int                 flags;
+	const char         *name;
+#endif
+};
+
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
+void suspend_blocker_destroy(struct suspend_blocker *blocker);
+void suspend_block(struct suspend_blocker *blocker);
+void suspend_unblock(struct suspend_blocker *blocker);
+bool suspend_blocker_is_active(struct suspend_blocker *blocker);
+bool suspend_is_blocked(void);
+
+#else
+
+static inline void suspend_blocker_init(struct suspend_blocker *blocker,
+					const char *name) {}
+static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
+static inline void suspend_block(struct suspend_blocker *blocker) {}
+static inline void suspend_unblock(struct suspend_blocker *blocker) {}
+static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
+								{ return 0; }
+static inline bool suspend_is_blocked(void) { return 0; }
+
+#endif
+
+#endif
+
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 5c36ea9..55a06a1 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -130,6 +130,22 @@ config SUSPEND_FREEZER
 
 	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
 
+config OPPORTUNISTIC_SUSPEND
+	bool "Suspend blockers"
+	depends on PM_SLEEP
+	select RTC_LIB
+	default n
+	---help---
+	  Opportunistic sleep support.  Allows the system to be put into a sleep
+	  state opportunistically, if it doesn't do any useful work at the
+	  moment. The PM subsystem is switched into this mode of operation by
+	  writing "opportunistic" into /sys/power/policy, while writing
+	  "forced" to this file turns the opportunistic suspend feature off.
+	  In the "opportunistic" mode suspend blockers are used to determine
+	  when to suspend the system and the value written to /sys/power/state
+	  determines the sleep state the system will be put into when there are
+	  no active suspend blockers.
+
 config HIBERNATION_NVS
 	bool
 
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 4319181..ee5276d 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
+obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= suspend_blocker.o
 obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
 obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
 obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
diff --git a/kernel/power/main.c b/kernel/power/main.c
index b58800b..030ecdc 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -12,6 +12,7 @@
 #include <linux/string.h>
 #include <linux/resume-trace.h>
 #include <linux/workqueue.h>
+#include <linux/suspend_blocker.h>
 
 #include "power.h"
 
@@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
 unsigned int pm_flags;
 EXPORT_SYMBOL(pm_flags);
 
+struct policy {
+	const char *name;
+	bool (*valid_state)(suspend_state_t state);
+	int (*set_state)(suspend_state_t state);
+};
+static struct policy policies[] = {
+	{
+		.name		= "forced",
+		.valid_state	= valid_state,
+		.set_state	= enter_state,
+	},
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	{
+		.name		= "opportunistic",
+		.valid_state	= request_suspend_valid_state,
+		.set_state	= request_suspend_state,
+	},
+#endif
+};
+static int policy;
+
 #ifdef CONFIG_PM_SLEEP
 
 /* Routines for PM-transition notifications */
@@ -146,6 +168,12 @@ struct kobject *power_kobj;
  *
  *	store() accepts one of those strings, translates it into the 
  *	proper enumerated value, and initiates a suspend transition.
+ *
+ *	If policy is set to opportunistic, store() does not block until the
+ *	system resumes, and it will try to re-enter the state until another
+ *	state is requested. Suspend blockers are respected and the requested
+ *	state will only be entered when no suspend blockers are active.
+ *	Write "on" to cancel.
  */
 static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 			  char *buf)
@@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 	int i;
 
 	for (i = 0; i < PM_SUSPEND_MAX; i++) {
-		if (pm_states[i] && valid_state(i))
+		if (pm_states[i] && policies[policy].valid_state(i))
 			s += sprintf(s,"%s ", pm_states[i]);
 	}
 #endif
 #ifdef CONFIG_HIBERNATION
-	s += sprintf(s, "%s\n", "disk");
+	if (!policy)
+		s += sprintf(s, "%s\n", "disk");
 #else
 	if (s != buf)
 		/* convert the last space to a newline */
@@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			   const char *buf, size_t n)
 {
 #ifdef CONFIG_SUSPEND
-	suspend_state_t state = PM_SUSPEND_STANDBY;
+	suspend_state_t state = PM_SUSPEND_ON;
 	const char * const *s;
 #endif
 	char *p;
@@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 	len = p ? p - buf : n;
 
 	/* First, check if we are requested to hibernate */
-	if (len == 4 && !strncmp(buf, "disk", len)) {
+	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
 		error = hibernate();
   goto Exit;
 	}
@@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			break;
 	}
 	if (state < PM_SUSPEND_MAX && *s)
-		error = enter_state(state);
+		error = policies[policy].set_state(state);
 #endif
 
  Exit:
@@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 
 power_attr(state);
 
+/**
+ *	policy - set policy for state
+ */
+
+static ssize_t policy_show(struct kobject *kobj,
+			   struct kobj_attribute *attr, char *buf)
+{
+	char *s = buf;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		if (i == policy)
+			s += sprintf(s, "[%s] ", policies[i].name);
+		else
+			s += sprintf(s, "%s ", policies[i].name);
+	}
+	if (s != buf)
+		/* convert the last space to a newline */
+		*(s-1) = '\n';
+	return (s - buf);
+}
+
+static ssize_t policy_store(struct kobject *kobj,
+			    struct kobj_attribute *attr,
+			    const char *buf, size_t n)
+{
+	const char *s;
+	char *p;
+	int len;
+	int i;
+
+	p = memchr(buf, '\n', n);
+	len = p ? p - buf : n;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		s = policies[i].name;
+		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
+			mutex_lock(&pm_mutex);
+			policies[policy].set_state(PM_SUSPEND_ON);
+			policy = i;
+			mutex_unlock(&pm_mutex);
+			return n;
+		}
+	}
+	return -EINVAL;
+}
+
+power_attr(policy);
+
 #ifdef CONFIG_PM_TRACE
 int pm_trace_enabled;
 
@@ -231,6 +309,7 @@ power_attr(pm_trace);
 
 static struct attribute * g[] = {
 	&state_attr.attr,
+	&policy_attr.attr,
 #ifdef CONFIG_PM_TRACE
 	&pm_trace_attr.attr,
 #endif
@@ -247,7 +326,7 @@ static struct attribute_group attr_group = {
 	.attrs = g,
 };
 
-#ifdef CONFIG_PM_RUNTIME
+#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_OPPORTUNISTIC_SUSPEND)
 struct workqueue_struct *pm_wq;
 EXPORT_SYMBOL_GPL(pm_wq);
 
@@ -266,6 +345,7 @@ static int __init pm_init(void)
 	int error = pm_start_workqueue();
 	if (error)
 		return error;
+	suspend_block_init();
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 46c5a26..67d10fd 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -236,3 +236,12 @@ static inline void suspend_thaw_processes(void)
 {
 }
 #endif
+
+/* kernel/power/suspend_block.c */
+extern int request_suspend_state(suspend_state_t state);
+extern bool request_suspend_valid_state(suspend_state_t state);
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+extern void __init suspend_block_init(void);
+#else
+static inline void suspend_block_init(void) {}
+#endif
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 56e7dbb..dc42006 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -16,10 +16,12 @@
 #include <linux/cpu.h>
 #include <linux/syscalls.h>
 #include <linux/gfp.h>
+#include <linux/suspend_blocker.h>
 
 #include "power.h"
 
 const char *const pm_states[PM_SUSPEND_MAX] = {
+	[PM_SUSPEND_ON]		= "on",
 	[PM_SUSPEND_STANDBY]	= "standby",
 	[PM_SUSPEND_MEM]	= "mem",
 };
@@ -157,7 +159,7 @@ static int suspend_enter(suspend_state_t state)
 
 	error = sysdev_suspend(PMSG_SUSPEND);
 	if (!error) {
-		if (!suspend_test(TEST_CORE))
+		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
 			error = suspend_ops->enter(state);
 		sysdev_resume();
 	}
diff --git a/kernel/power/suspend_blocker.c b/kernel/power/suspend_blocker.c
new file mode 100644
index 0000000..2c58b21
--- /dev/null
+++ b/kernel/power/suspend_blocker.c
@@ -0,0 +1,261 @@
+/* kernel/power/suspend_blocker.c
+ *
+ * Copyright (C) 2005-2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/suspend.h>
+#include <linux/suspend_blocker.h>
+#include "power.h"
+
+extern struct workqueue_struct *pm_wq;
+
+enum {
+	DEBUG_EXIT_SUSPEND = 1U << 0,
+	DEBUG_WAKEUP = 1U << 1,
+	DEBUG_USER_STATE = 1U << 2,
+	DEBUG_SUSPEND = 1U << 3,
+	DEBUG_SUSPEND_BLOCKER = 1U << 4,
+};
+static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SB_INITIALIZED            (1U << 8)
+#define SB_ACTIVE                 (1U << 9)
+
+static DEFINE_SPINLOCK(list_lock);
+static DEFINE_SPINLOCK(state_lock);
+static LIST_HEAD(inactive_blockers);
+static LIST_HEAD(active_blockers);
+static int current_event_num;
+struct suspend_blocker main_suspend_blocker;
+static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
+static bool enable_suspend_blockers;
+
+#define pr_info_time(fmt, args...) \
+	do { \
+		struct timespec ts; \
+		struct rtc_time tm; \
+		getnstimeofday(&ts); \
+		rtc_time_to_tm(ts.tv_sec, &tm); \
+		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
+			args, \
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
+			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
+	} while (0);
+
+static void print_active_blockers_locked(void)
+{
+	struct suspend_blocker *blocker;
+
+	list_for_each_entry(blocker, &active_blockers, link)
+		pr_info("active suspend blocker %s\n", blocker->name);
+}
+
+/**
+ * suspend_is_blocked() - Check if suspend should be blocked
+ *
+ * suspend_is_blocked can be used by generic power management code to abort
+ * suspend.
+ *
+ * To preserve backward compatibility suspend_is_blocked returns 0 unless it
+ * is called during suspend initiated from the suspend_block code.
+ */
+bool suspend_is_blocked(void)
+{
+	if (!enable_suspend_blockers)
+		return 0;
+	return !list_empty(&active_blockers);
+}
+
+static void suspend_worker(struct work_struct *work)
+{
+	int ret;
+	int entry_event_num;
+
+	enable_suspend_blockers = true;
+	while (!suspend_is_blocked()) {
+		entry_event_num = current_event_num;
+
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("suspend: enter suspend\n");
+
+		ret = pm_suspend(requested_suspend_state);
+
+		if (debug_mask & DEBUG_EXIT_SUSPEND)
+			pr_info_time("suspend: exit suspend, ret = %d ", ret);
+
+		if (current_event_num == entry_event_num)
+			pr_info("suspend: pm_suspend returned with no event\n");
+	}
+	enable_suspend_blockers = false;
+}
+static DECLARE_WORK(suspend_work, suspend_worker);
+
+/**
+ * suspend_blocker_init() - Initialize a suspend blocker
+ * @blocker:	The suspend blocker to initialize.
+ * @name:	The name of the suspend blocker to show in debug messages.
+ *
+ * The suspend blocker struct and name must not be freed before calling
+ * suspend_blocker_destroy.
+ */
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
+{
+	unsigned long irqflags = 0;
+
+	WARN_ON(!name);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_blocker_init name=%s\n", name);
+
+	blocker->name = name;
+	blocker->flags = SB_INITIALIZED;
+	INIT_LIST_HEAD(&blocker->link);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	list_add(&blocker->link, &inactive_blockers);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_blocker_init);
+
+/**
+ * suspend_blocker_destroy() - Destroy a suspend blocker
+ * @blocker:	The suspend blocker to destroy.
+ */
+void suspend_blocker_destroy(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	blocker->flags &= ~SB_INITIALIZED;
+	list_del(&blocker->link);
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(pm_wq, &suspend_work);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_blocker_destroy);
+
+/**
+ * suspend_block() - Block suspend
+ * @blocker:	The suspend blocker to use
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_block(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_block: %s\n", blocker->name);
+
+	blocker->flags |= SB_ACTIVE;
+	list_move(&blocker->link, &active_blockers);
+	current_event_num++;
+
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_block);
+
+/**
+ * suspend_unblock() - Unblock suspend
+ * @blocker:	The suspend blocker to unblock.
+ *
+ * If no other suspend blockers block suspend, the system will suspend.
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_unblock(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_unblock: %s\n", blocker->name);
+
+	list_move(&blocker->link, &inactive_blockers);
+
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(pm_wq, &suspend_work);
+	blocker->flags &= ~(SB_ACTIVE);
+	if (blocker == &main_suspend_blocker) {
+		if (debug_mask & DEBUG_SUSPEND)
+			print_active_blockers_locked();
+	}
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_unblock);
+
+/**
+ * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
+ * @blocker:	The suspend blocker to check.
+ *
+ * Returns true if the suspend_blocker is currently active.
+ */
+bool suspend_blocker_is_active(struct suspend_blocker *blocker)
+{
+	WARN_ON(!(blocker->flags & SB_INITIALIZED));
+
+	return !!(blocker->flags & SB_ACTIVE);
+}
+EXPORT_SYMBOL(suspend_blocker_is_active);
+
+bool request_suspend_valid_state(suspend_state_t state)
+{
+	return (state == PM_SUSPEND_ON) || valid_state(state);
+}
+
+int request_suspend_state(suspend_state_t state)
+{
+	unsigned long irqflags;
+
+	if (!request_suspend_valid_state(state))
+		return -ENODEV;
+
+	spin_lock_irqsave(&state_lock, irqflags);
+
+	if (debug_mask & DEBUG_USER_STATE)
+		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
+			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
+			     requested_suspend_state, state,
+			     ktime_to_ns(ktime_get()));
+
+	requested_suspend_state = state;
+	if (state == PM_SUSPEND_ON)
+		suspend_block(&main_suspend_blocker);
+	else
+		suspend_unblock(&main_suspend_blocker);
+	spin_unlock_irqrestore(&state_lock, irqflags);
+	return 0;
+}
+
+void __init suspend_block_init(void)
+{
+	suspend_blocker_init(&main_suspend_blocker, "main");
+	suspend_block(&main_suspend_blocker);
+}
-- 
1.6.5.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-30  4:24           ` Tejun Heo
  (?)
  (?)
@ 2010-04-30 17:26           ` Oleg Nesterov
  2010-05-20  8:30             ` Tejun Heo
  2010-05-20  8:30             ` Tejun Heo
  -1 siblings, 2 replies; 195+ messages in thread
From: Oleg Nesterov @ 2010-04-30 17:26 UTC (permalink / raw)
  To: Tejun Heo
  Cc: Rafael J. Wysocki, Arve Hjønnevåg, linux-pm,
	linux-kernel, Alan Stern, Len Brown, Pavel Machek, Randy Dunlap,
	Jesse Barnes, Magnus Damm, Nigel Cunningham, Cornelia Huck,
	Ming Lei, Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

On 04/30, Tejun Heo wrote:
>
> On 04/29/2010 11:16 PM, Rafael J. Wysocki wrote:
> >>> Also, we already have one PM workqueue.  It is used for runtime PM, but I guess
> >>> it may be used just as well for the opportunistic suspend.  It is freezable,
> >>> but would it hurt?
> >>
> > Freezable workqueues have to be singlethread or else there will be unfixable
> > races, so you can safely assume things will stay as they are in this respect.
>
> Rafael, can you elaborate a bit more on this?  Just in case I missed
> something while doing cmwq as it currently doesn't have such limit.

Currently _cpu_down() can't flush and/or stop the frozen cwq->thread.

IIRC this is fixable, but needs the nasty complications. We should
thaw + stop the frozen cwq->thread, then move the pending works to
another CPU.

Oleg.


^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-30  4:24           ` Tejun Heo
  (?)
@ 2010-04-30 17:26           ` Oleg Nesterov
  -1 siblings, 0 replies; 195+ messages in thread
From: Oleg Nesterov @ 2010-04-30 17:26 UTC (permalink / raw)
  To: Tejun Heo
  Cc: Len Brown, linux-doc, linux-kernel, Jesse Barnes, Magnus Damm,
	linux-pm, Wu Fengguang, Andrew Morton

On 04/30, Tejun Heo wrote:
>
> On 04/29/2010 11:16 PM, Rafael J. Wysocki wrote:
> >>> Also, we already have one PM workqueue.  It is used for runtime PM, but I guess
> >>> it may be used just as well for the opportunistic suspend.  It is freezable,
> >>> but would it hurt?
> >>
> > Freezable workqueues have to be singlethread or else there will be unfixable
> > races, so you can safely assume things will stay as they are in this respect.
>
> Rafael, can you elaborate a bit more on this?  Just in case I missed
> something while doing cmwq as it currently doesn't have such limit.

Currently _cpu_down() can't flush and/or stop the frozen cwq->thread.

IIRC this is fixable, but needs the nasty complications. We should
thaw + stop the frozen cwq->thread, then move the pending works to
another CPU.

Oleg.

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-29 23:39             ` Arve Hjønnevåg
@ 2010-04-30 14:41               ` Alan Stern
  -1 siblings, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-04-30 14:41 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Rafael J. Wysocki, Linux-pm mailing list,
	Kernel development list, Tejun Heo, Oleg Nesterov, Len Brown,
	Pavel Machek, Randy Dunlap, Jesse Barnes, Nigel Cunningham,
	Cornelia Huck, Ming Lei, Wu Fengguang, Andrew Morton,
	Maxim Levitsky, linux-doc

On Thu, 29 Apr 2010, Arve Hjønnevåg wrote:

> >> That does not work though. Unless every key turns the screen on you
> >> will have a race every time the user presses a key you want to ignore.
> >
> > Of course.  You are confirming what I wrote immediately below: Suspend
> Yet you offered it as an example of why "Handling the screen doesn't
> need suspend blockers".

What I meant was this: Without suspend blockers you can still turn the
screen on and off, but you can't avoid races.  Therefore: You don't
need suspend blockers to handle the screen, but you do need them to
handle races.

> > blockers help resolve races.  Note that this race has nothing to do
> > with the _screen_ in particular -- exactly the same race occurs if you
> > decide to turn on the audio speaker or some other piece of hardware.
> >
> I agree with this, but that does not mean that describing how you can
> handle the screen with suspend blockers is a bad example.

Agreed; it's not a bad example.  My objection is to the way the example
is presented.

> I think suspend blockers do allow user programs to make decisions.
> Without suspend blockers some decisions can only be safely be made in
> the kernel/drivers.

This is our real disagreement -- it's mainly a question of the meaning
of words.  When one says "A allows B to make a decision", this means
that without A, B would literally be unable to decide what to do.  It
wouldn't be able to "make up its mind" (like the donkey that starves
while stuck halfway between two piles of hay because it can't decide 
which pile to eat).

That's not what you mean to say.  In your case there's no difficulty in
making the choice -- the program knows exactly what it wants to do.  
The problem is that without suspend blockers, it can't _carry out_ the
chosen action.  To put it another way, the suspend blockers don't help
with deciding which action to take; rather they help with taking the
action after it has been decided upon.

Or put this way: Suppose suspend blockers were not available.  Then the
desired action would not be safe, so the program wouldn't be able to
take it.  The decision would thus be even simpler, since one of the
choices would be eliminated.  In this way, lack of suspend blockers
makes the decision easier, not harder.  So you shouldn't say that
suspend blockers allow the program to make the decision.

What your example _really_ shows is how a program can carry out a 
prolonged action that requires the system to be awake for an extended 
period of time.  Even with suspend blockers, the right way to do this 
safely isn't necessarily obvious.  That's how the example helps.

> How about:
> 
> - The user-space input-event thread returns from read. If it
> determines that the key should be ignored, it calls suspend_unblock on
> the process_input_events suspend_blocker and then calls select or
> poll. The system will automatically suspend again, since now no
> suspend blockers are active.

Do you want to mention here that everything will work correctly even if 
the system suspends before the thread can call select or poll?

> If the key that was pressed instead should preform a simple action
> (for example, adjusting the volume), this action can be performed
> right before calling suspend_unblock on the process_input_events
> suspend_blocker. However, if the key triggers a longer-running action,
> that action needs its own suspend_blocker and suspend_block must be
> called on that suspend blocker before calling suspend_unblock on the
> process_input_events suspend_blocker.

That's good!  I like it.  Now if you can change the beginning of the
example, to say that it shows how programs should use suspend blockers
instead of showing that suspend blockers allow programs to make
decisions.

Alan Stern


^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
@ 2010-04-30 14:41               ` Alan Stern
  0 siblings, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-04-30 14:41 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, linux-doc, Kernel development list, Jesse Barnes,
	Oleg Nesterov, Tejun Heo, Linux-pm mailing list, Wu Fengguang,
	Andrew Morton

On Thu, 29 Apr 2010, Arve Hjønnevåg wrote:

> >> That does not work though. Unless every key turns the screen on you
> >> will have a race every time the user presses a key you want to ignore.
> >
> > Of course.  You are confirming what I wrote immediately below: Suspend
> Yet you offered it as an example of why "Handling the screen doesn't
> need suspend blockers".

What I meant was this: Without suspend blockers you can still turn the
screen on and off, but you can't avoid races.  Therefore: You don't
need suspend blockers to handle the screen, but you do need them to
handle races.

> > blockers help resolve races.  Note that this race has nothing to do
> > with the _screen_ in particular -- exactly the same race occurs if you
> > decide to turn on the audio speaker or some other piece of hardware.
> >
> I agree with this, but that does not mean that describing how you can
> handle the screen with suspend blockers is a bad example.

Agreed; it's not a bad example.  My objection is to the way the example
is presented.

> I think suspend blockers do allow user programs to make decisions.
> Without suspend blockers some decisions can only be safely be made in
> the kernel/drivers.

This is our real disagreement -- it's mainly a question of the meaning
of words.  When one says "A allows B to make a decision", this means
that without A, B would literally be unable to decide what to do.  It
wouldn't be able to "make up its mind" (like the donkey that starves
while stuck halfway between two piles of hay because it can't decide 
which pile to eat).

That's not what you mean to say.  In your case there's no difficulty in
making the choice -- the program knows exactly what it wants to do.  
The problem is that without suspend blockers, it can't _carry out_ the
chosen action.  To put it another way, the suspend blockers don't help
with deciding which action to take; rather they help with taking the
action after it has been decided upon.

Or put this way: Suppose suspend blockers were not available.  Then the
desired action would not be safe, so the program wouldn't be able to
take it.  The decision would thus be even simpler, since one of the
choices would be eliminated.  In this way, lack of suspend blockers
makes the decision easier, not harder.  So you shouldn't say that
suspend blockers allow the program to make the decision.

What your example _really_ shows is how a program can carry out a 
prolonged action that requires the system to be awake for an extended 
period of time.  Even with suspend blockers, the right way to do this 
safely isn't necessarily obvious.  That's how the example helps.

> How about:
> 
> - The user-space input-event thread returns from read. If it
> determines that the key should be ignored, it calls suspend_unblock on
> the process_input_events suspend_blocker and then calls select or
> poll. The system will automatically suspend again, since now no
> suspend blockers are active.

Do you want to mention here that everything will work correctly even if 
the system suspends before the thread can call select or poll?

> If the key that was pressed instead should preform a simple action
> (for example, adjusting the volume), this action can be performed
> right before calling suspend_unblock on the process_input_events
> suspend_blocker. However, if the key triggers a longer-running action,
> that action needs its own suspend_blocker and suspend_block must be
> called on that suspend blocker before calling suspend_unblock on the
> process_input_events suspend_blocker.

That's good!  I like it.  Now if you can change the beginning of the
example, to say that it shows how programs should use suspend blockers
instead of showing that suspend blockers allow programs to make
decisions.

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-29 21:16         ` Rafael J. Wysocki
@ 2010-04-30  4:24           ` Tejun Heo
  -1 siblings, 0 replies; 195+ messages in thread
From: Tejun Heo @ 2010-04-30  4:24 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Arve Hjønnevåg, linux-pm, linux-kernel, Alan Stern,
	Oleg Nesterov, Len Brown, Pavel Machek, Randy Dunlap,
	Jesse Barnes, Magnus Damm, Nigel Cunningham, Cornelia Huck,
	Ming Lei, Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

Hello,

On 04/29/2010 11:16 PM, Rafael J. Wysocki wrote:
>>> Also, we already have one PM workqueue.  It is used for runtime PM, but I guess
>>> it may be used just as well for the opportunistic suspend.  It is freezable,
>>> but would it hurt?
>>
>> No, it works, the freezable flag is just ignored when I call
>> pm_suspend and I don't run anything else on the workqueue while
>> threads are frozen. It does need to be a single threaded workqueue
>> though, so make sure you don't just change that.
> 
> Freezable workqueues have to be singlethread or else there will be unfixable
> races, so you can safely assume things will stay as they are in this respect.

Rafael, can you elaborate a bit more on this?  Just in case I missed
something while doing cmwq as it currently doesn't have such limit.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
@ 2010-04-30  4:24           ` Tejun Heo
  0 siblings, 0 replies; 195+ messages in thread
From: Tejun Heo @ 2010-04-30  4:24 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes, linux-kernel,
	Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

Hello,

On 04/29/2010 11:16 PM, Rafael J. Wysocki wrote:
>>> Also, we already have one PM workqueue.  It is used for runtime PM, but I guess
>>> it may be used just as well for the opportunistic suspend.  It is freezable,
>>> but would it hurt?
>>
>> No, it works, the freezable flag is just ignored when I call
>> pm_suspend and I don't run anything else on the workqueue while
>> threads are frozen. It does need to be a single threaded workqueue
>> though, so make sure you don't just change that.
> 
> Freezable workqueues have to be singlethread or else there will be unfixable
> races, so you can safely assume things will stay as they are in this respect.

Rafael, can you elaborate a bit more on this?  Just in case I missed
something while doing cmwq as it currently doesn't have such limit.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-29 15:41           ` Alan Stern
@ 2010-04-29 23:39             ` Arve Hjønnevåg
  -1 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-04-29 23:39 UTC (permalink / raw)
  To: Alan Stern
  Cc: Rafael J. Wysocki, Linux-pm mailing list,
	Kernel development list, Tejun Heo, Oleg Nesterov, Len Brown,
	Pavel Machek, Randy Dunlap, Jesse Barnes, Nigel Cunningham,
	Cornelia Huck, Ming Lei, Wu Fengguang, Andrew Morton,
	Maxim Levitsky, linux-doc

2010/4/29 Alan Stern <stern@rowland.harvard.edu>:
> On Wed, 28 Apr 2010, Arve Hjønnevåg wrote:
>
>> >> >  suspend blockers can be used to allow
>> >> > +user-space to decide whether a keystroke received while the system is suspended
>> >> > +should cause the screen to be turned back on or allow the system to go back into
>> >> > +suspend.
>> >>
>> >> That's not right.  Handling the screen doesn't need suspend blockers:
>> >> The program decides what to do and then either turns on the screen or
>> >> else writes "mem" to /sys/power/state.
>>
>> That does not work though. Unless every key turns the screen on you
>> will have a race every time the user presses a key you want to ignore.
>
> Of course.  You are confirming what I wrote immediately below: Suspend
Yet you offered it as an example of why "Handling the screen doesn't
need suspend blockers".

> blockers help resolve races.  Note that this race has nothing to do
> with the _screen_ in particular -- exactly the same race occurs if you
> decide to turn on the audio speaker or some other piece of hardware.
>
I agree with this, but that does not mean that describing how you can
handle the screen with suspend blockers is a bad example.

>> >>  What suspend blockers add is
>> >> the ability to resolve races and satisfy multiple constraints when
>> >> going into suspend -- which has nothing to do with operating the
>> >> screen.
>>
>> I'm not sure I agree with this. You cannot reliably turn the screen on
>> from user space when the user presses a wakeup-key without suspend
>> blockers.
>
> Let's say that it has nothing to do _specifically_ with the screen.
> _Any_ action you want to take in userspace is difficult to coordinate
> with system suspends if you don't have suspend blockers.
>
>> >>
>> >> I _think_ what you're trying to get at can be expressed this way:
>> >>
>> >>       Here's an example showing how a cell phone or other embedded
>> >>       system can handle keystrokes (or other input events) in the
>> >>       presence of suspend blockers.  Use set_irq_wake...
>>
>> OK, but the last version was what you (Alan) suggested last year.
>
> So at least my mental processes have remained consistent over the span
> of a year.  Nice to know I haven't undergone a complete personality
> change...  :-)
>
>> >>       ...
>> >>
>> >>       - The user-space input-event thread returns from read.  It
>> >>       carries out whatever activities are appropriate (for example,
>> >>       powering up the display screen, running other programs, and so
>> >>       on).  When it is finished, it calls suspend_unblock on the
>> >>       process_input_events suspend_blocker and then calls select or
>> >>       poll.  The system will automatically suspend again when it is
>> >>       idle and no suspend blockers remain active.
>> >
>> > Yeah, that sounds better.  Arve, what do you think?
>> >
>>
>> Idle is irrelevant and needs to be removed. This new last step is also
>> no longer a concrete example, but if you really think is it better I
>> can change it.
>
> Perhaps you would prefer to change this completely.  Write up a
> description of what can go wrong when suspend blockers _aren't_ used,
> and show how suspend blockers can prevent the problem from occurring.
>
> But whatever you do, don't make it appear that suspend blockers allow
> user programs to make decisions (which is what you wrote before).  They
> don't -- programs can make whatever decisions they want.  Suspend
> blockers merely help them carry out the actions they have decided upon
> in a safe manner.

I think suspend blockers do allow user programs to make decisions.
Without suspend blockers some decisions can only be safely be made in
the kernel/drivers.

>
> And don't make it appear that suspend blockers can only be used for
> solving screen-related problems.

How about:

- The user-space input-event thread returns from read. If it
determines that the key should be ignored, it calls suspend_unblock on
the process_input_events suspend_blocker and then calls select or
poll. The system will automatically suspend again, since now no
suspend blockers are active.

If the key that was pressed instead should preform a simple action
(for example, adjusting the volume), this action can be performed
right before calling suspend_unblock on the process_input_events
suspend_blocker. However, if the key triggers a longer-running action,
that action needs its own suspend_blocker and suspend_block must be
called on that suspend blocker before calling suspend_unblock on the
process_input_events suspend_blocker.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
@ 2010-04-29 23:39             ` Arve Hjønnevåg
  0 siblings, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-04-29 23:39 UTC (permalink / raw)
  To: Alan Stern
  Cc: Len Brown, linux-doc, Kernel development list, Jesse Barnes,
	Oleg Nesterov, Tejun Heo, Linux-pm mailing list, Wu Fengguang,
	Andrew Morton

2010/4/29 Alan Stern <stern@rowland.harvard.edu>:
> On Wed, 28 Apr 2010, Arve Hjønnevåg wrote:
>
>> >> >  suspend blockers can be used to allow
>> >> > +user-space to decide whether a keystroke received while the system is suspended
>> >> > +should cause the screen to be turned back on or allow the system to go back into
>> >> > +suspend.
>> >>
>> >> That's not right.  Handling the screen doesn't need suspend blockers:
>> >> The program decides what to do and then either turns on the screen or
>> >> else writes "mem" to /sys/power/state.
>>
>> That does not work though. Unless every key turns the screen on you
>> will have a race every time the user presses a key you want to ignore.
>
> Of course.  You are confirming what I wrote immediately below: Suspend
Yet you offered it as an example of why "Handling the screen doesn't
need suspend blockers".

> blockers help resolve races.  Note that this race has nothing to do
> with the _screen_ in particular -- exactly the same race occurs if you
> decide to turn on the audio speaker or some other piece of hardware.
>
I agree with this, but that does not mean that describing how you can
handle the screen with suspend blockers is a bad example.

>> >>  What suspend blockers add is
>> >> the ability to resolve races and satisfy multiple constraints when
>> >> going into suspend -- which has nothing to do with operating the
>> >> screen.
>>
>> I'm not sure I agree with this. You cannot reliably turn the screen on
>> from user space when the user presses a wakeup-key without suspend
>> blockers.
>
> Let's say that it has nothing to do _specifically_ with the screen.
> _Any_ action you want to take in userspace is difficult to coordinate
> with system suspends if you don't have suspend blockers.
>
>> >>
>> >> I _think_ what you're trying to get at can be expressed this way:
>> >>
>> >>       Here's an example showing how a cell phone or other embedded
>> >>       system can handle keystrokes (or other input events) in the
>> >>       presence of suspend blockers.  Use set_irq_wake...
>>
>> OK, but the last version was what you (Alan) suggested last year.
>
> So at least my mental processes have remained consistent over the span
> of a year.  Nice to know I haven't undergone a complete personality
> change...  :-)
>
>> >>       ...
>> >>
>> >>       - The user-space input-event thread returns from read.  It
>> >>       carries out whatever activities are appropriate (for example,
>> >>       powering up the display screen, running other programs, and so
>> >>       on).  When it is finished, it calls suspend_unblock on the
>> >>       process_input_events suspend_blocker and then calls select or
>> >>       poll.  The system will automatically suspend again when it is
>> >>       idle and no suspend blockers remain active.
>> >
>> > Yeah, that sounds better.  Arve, what do you think?
>> >
>>
>> Idle is irrelevant and needs to be removed. This new last step is also
>> no longer a concrete example, but if you really think is it better I
>> can change it.
>
> Perhaps you would prefer to change this completely.  Write up a
> description of what can go wrong when suspend blockers _aren't_ used,
> and show how suspend blockers can prevent the problem from occurring.
>
> But whatever you do, don't make it appear that suspend blockers allow
> user programs to make decisions (which is what you wrote before).  They
> don't -- programs can make whatever decisions they want.  Suspend
> blockers merely help them carry out the actions they have decided upon
> in a safe manner.

I think suspend blockers do allow user programs to make decisions.
Without suspend blockers some decisions can only be safely be made in
the kernel/drivers.

>
> And don't make it appear that suspend blockers can only be used for
> solving screen-related problems.

How about:

- The user-space input-event thread returns from read. If it
determines that the key should be ignored, it calls suspend_unblock on
the process_input_events suspend_blocker and then calls select or
poll. The system will automatically suspend again, since now no
suspend blockers are active.

If the key that was pressed instead should preform a simple action
(for example, adjusting the volume), this action can be performed
right before calling suspend_unblock on the process_input_events
suspend_blocker. However, if the key triggers a longer-running action,
that action needs its own suspend_blocker and suspend_block must be
called on that suspend blocker before calling suspend_unblock on the
process_input_events suspend_blocker.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-29  3:37     ` Arve Hjønnevåg
@ 2010-04-29 21:16         ` Rafael J. Wysocki
  0 siblings, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-04-29 21:16 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: linux-pm, linux-kernel, Alan Stern, Tejun Heo, Oleg Nesterov,
	Len Brown, Pavel Machek, Randy Dunlap, Jesse Barnes, Magnus Damm,
	Nigel Cunningham, Cornelia Huck, Ming Lei, Wu Fengguang,
	Andrew Morton, Maxim Levitsky, linux-doc

On Thursday 29 April 2010, Arve Hjønnevåg wrote:
> 2010/4/28 Rafael J. Wysocki <rjw@sisk.pl>:
> ...
> >>
> >> +/**
> >> + *   policy - set policy for state
> >> + */
> >> +
> >> +static ssize_t policy_show(struct kobject *kobj,
> >> +                        struct kobj_attribute *attr, char *buf)
> >> +{
> >> +     char *s = buf;
> >> +     int i;
> >> +
> >> +     for (i = 0; i < ARRAY_SIZE(policies); i++) {
> >> +             if (i == policy)
> >> +                     s += sprintf(s, "[%s] ", policies[i].name);
> >> +             else
> >> +                     s += sprintf(s, "%s ", policies[i].name);
> >> +     }
> >> +     if (s != buf)
> >> +             /* convert the last space to a newline */
> >> +             *(s-1) = '\n';
> >> +     return (s - buf);
> >> +}
> >> +
> >> +static ssize_t policy_store(struct kobject *kobj,
> >> +                         struct kobj_attribute *attr,
> >> +                         const char *buf, size_t n)
> >> +{
> >> +     const char *s;
> >> +     char *p;
> >> +     int len;
> >> +     int i;
> >> +
> >> +     p = memchr(buf, '\n', n);
> >> +     len = p ? p - buf : n;
> >> +
> >> +     for (i = 0; i < ARRAY_SIZE(policies); i++) {
> >> +             s = policies[i].name;
> >> +             if (s && len == strlen(s) && !strncmp(buf, s, len)) {
> >> +                     mutex_lock(&pm_mutex);
> >> +                     policies[policy].set_state(PM_SUSPEND_ON);
> >> +                     policy = i;
> >> +                     mutex_unlock(&pm_mutex);
> >> +                     return n;
> >> +             }
> >> +     }
> >> +     return -EINVAL;
> >> +}
> >> +
> >> +power_attr(policy);
> >> +
> >>  #ifdef CONFIG_PM_TRACE
> >>  int pm_trace_enabled;
> >>
> >
> > Would you mind if I changed the above so that "policy" doesn't even show up
> > if CONFIG_OPPORTUNISTIC_SUSPEND is unset?
> >
> I don't mind, but It did not seem worth the trouble to hide it. It
> will only list the supported policies, and it is easy to add or remove
> policies this way.
> 
> > ...
> >> +static void suspend_worker(struct work_struct *work)
> >> +{
> >> +     int ret;
> >> +     int entry_event_num;
> >> +
> >> +     enable_suspend_blockers = true;
> >> +     while (!suspend_is_blocked()) {
> >> +             entry_event_num = current_event_num;
> >> +
> >> +             if (debug_mask & DEBUG_SUSPEND)
> >> +                     pr_info("suspend: enter suspend\n");
> >> +
> >> +             ret = pm_suspend(requested_suspend_state);
> >> +
> >> +             if (debug_mask & DEBUG_EXIT_SUSPEND)
> >> +                     pr_info_time("suspend: exit suspend, ret = %d ", ret);
> >> +
> >> +             if (current_event_num == entry_event_num)
> >> +                     pr_info("suspend: pm_suspend returned with no event\n");
> >
> > Hmm, what exactly is this for?  It looks like a debug thing to me.  I'd use
> > pr_debug() here and in both debug printk()s above.  Would you agree?
> >
> 
> If the driver that caused the wakeup does not use suspend blockers, we
> the only choice is to try to suspend again. I want to know if this
> happened. The stats patch disable this printk by default since it will
> show up in the stats, and the timeout patch (not included here) delays
> the retry.
> 
> ...
> >> +EXPORT_SYMBOL(suspend_blocker_init);
> >
> > Is there a strong objection to changing that (and the other instances below) to
> > EXPORT_SYMBOL_GPL?
> >
> 
> I don't know if it is a strong objection, but I prefer that this api
> is available to all drivers. I don't want to prevent a user from using
> opportunistic suspend because a non-gpl driver could not use suspend
> blockers. I changed the suspend blocking work functions to be gpl only
> though, since they are not required, and the workqueue api is
> available to gpl code anyway.
> 
> ...
> >> +bool request_suspend_valid_state(suspend_state_t state)
> >> +{
> >> +     return (state == PM_SUSPEND_ON) || valid_state(state);
> >> +}
> >> +
> >> +int request_suspend_state(suspend_state_t state)
> >> +{
> >> +     unsigned long irqflags;
> >> +
> >> +     if (!request_suspend_valid_state(state))
> >> +             return -ENODEV;
> >> +
> >> +     spin_lock_irqsave(&state_lock, irqflags);
> >> +
> >> +     if (debug_mask & DEBUG_USER_STATE)
> >> +             pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
> >> +                          state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> >> +                          requested_suspend_state, state,
> >> +                          ktime_to_ns(ktime_get()));
> >> +
> >> +     requested_suspend_state = state;
> >> +     if (state == PM_SUSPEND_ON)
> >> +             suspend_block(&main_suspend_blocker);
> >> +     else
> >> +             suspend_unblock(&main_suspend_blocker);
> >> +     spin_unlock_irqrestore(&state_lock, irqflags);
> >> +     return 0;
> >> +}
> >
> > I think the two functions above should be static, shouldn't they?
> 
> No, they are used from main.c.
> 
> >
> >> +static int __init suspend_block_init(void)
> >> +{
> >> +     suspend_work_queue = create_singlethread_workqueue("suspend");
> >> +     if (!suspend_work_queue)
> >> +             return -ENOMEM;
> >> +
> >> +     suspend_blocker_init(&main_suspend_blocker, "main");
> >> +     suspend_block(&main_suspend_blocker);
> >> +     return 0;
> >> +}
> >> +
> >> +core_initcall(suspend_block_init);
> >
> > Hmm.  Why don't you want to put that initialization into pm_init() (in
> > kernel/power/main.c)?
> 
> It was not needed before, but I changed pm_init to call
> suspend_block_init after creating pm_wq.
> 
> >
> > Also, we already have one PM workqueue.  It is used for runtime PM, but I guess
> > it may be used just as well for the opportunistic suspend.  It is freezable,
> > but would it hurt?
> 
> No, it works, the freezable flag is just ignored when I call
> pm_suspend and I don't run anything else on the workqueue while
> threads are frozen. It does need to be a single threaded workqueue
> though, so make sure you don't just change that.

Freezable workqueues have to be singlethread or else there will be unfixable
races, so you can safely assume things will stay as they are in this respect.

Rafael


> 
> 


^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
@ 2010-04-29 21:16         ` Rafael J. Wysocki
  0 siblings, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-04-29 21:16 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, linux-doc, linux-kernel, Jesse Barnes, Oleg Nesterov,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Thursday 29 April 2010, Arve Hjønnevåg wrote:
> 2010/4/28 Rafael J. Wysocki <rjw@sisk.pl>:
> ...
> >>
> >> +/**
> >> + *   policy - set policy for state
> >> + */
> >> +
> >> +static ssize_t policy_show(struct kobject *kobj,
> >> +                        struct kobj_attribute *attr, char *buf)
> >> +{
> >> +     char *s = buf;
> >> +     int i;
> >> +
> >> +     for (i = 0; i < ARRAY_SIZE(policies); i++) {
> >> +             if (i == policy)
> >> +                     s += sprintf(s, "[%s] ", policies[i].name);
> >> +             else
> >> +                     s += sprintf(s, "%s ", policies[i].name);
> >> +     }
> >> +     if (s != buf)
> >> +             /* convert the last space to a newline */
> >> +             *(s-1) = '\n';
> >> +     return (s - buf);
> >> +}
> >> +
> >> +static ssize_t policy_store(struct kobject *kobj,
> >> +                         struct kobj_attribute *attr,
> >> +                         const char *buf, size_t n)
> >> +{
> >> +     const char *s;
> >> +     char *p;
> >> +     int len;
> >> +     int i;
> >> +
> >> +     p = memchr(buf, '\n', n);
> >> +     len = p ? p - buf : n;
> >> +
> >> +     for (i = 0; i < ARRAY_SIZE(policies); i++) {
> >> +             s = policies[i].name;
> >> +             if (s && len == strlen(s) && !strncmp(buf, s, len)) {
> >> +                     mutex_lock(&pm_mutex);
> >> +                     policies[policy].set_state(PM_SUSPEND_ON);
> >> +                     policy = i;
> >> +                     mutex_unlock(&pm_mutex);
> >> +                     return n;
> >> +             }
> >> +     }
> >> +     return -EINVAL;
> >> +}
> >> +
> >> +power_attr(policy);
> >> +
> >>  #ifdef CONFIG_PM_TRACE
> >>  int pm_trace_enabled;
> >>
> >
> > Would you mind if I changed the above so that "policy" doesn't even show up
> > if CONFIG_OPPORTUNISTIC_SUSPEND is unset?
> >
> I don't mind, but It did not seem worth the trouble to hide it. It
> will only list the supported policies, and it is easy to add or remove
> policies this way.
> 
> > ...
> >> +static void suspend_worker(struct work_struct *work)
> >> +{
> >> +     int ret;
> >> +     int entry_event_num;
> >> +
> >> +     enable_suspend_blockers = true;
> >> +     while (!suspend_is_blocked()) {
> >> +             entry_event_num = current_event_num;
> >> +
> >> +             if (debug_mask & DEBUG_SUSPEND)
> >> +                     pr_info("suspend: enter suspend\n");
> >> +
> >> +             ret = pm_suspend(requested_suspend_state);
> >> +
> >> +             if (debug_mask & DEBUG_EXIT_SUSPEND)
> >> +                     pr_info_time("suspend: exit suspend, ret = %d ", ret);
> >> +
> >> +             if (current_event_num == entry_event_num)
> >> +                     pr_info("suspend: pm_suspend returned with no event\n");
> >
> > Hmm, what exactly is this for?  It looks like a debug thing to me.  I'd use
> > pr_debug() here and in both debug printk()s above.  Would you agree?
> >
> 
> If the driver that caused the wakeup does not use suspend blockers, we
> the only choice is to try to suspend again. I want to know if this
> happened. The stats patch disable this printk by default since it will
> show up in the stats, and the timeout patch (not included here) delays
> the retry.
> 
> ...
> >> +EXPORT_SYMBOL(suspend_blocker_init);
> >
> > Is there a strong objection to changing that (and the other instances below) to
> > EXPORT_SYMBOL_GPL?
> >
> 
> I don't know if it is a strong objection, but I prefer that this api
> is available to all drivers. I don't want to prevent a user from using
> opportunistic suspend because a non-gpl driver could not use suspend
> blockers. I changed the suspend blocking work functions to be gpl only
> though, since they are not required, and the workqueue api is
> available to gpl code anyway.
> 
> ...
> >> +bool request_suspend_valid_state(suspend_state_t state)
> >> +{
> >> +     return (state == PM_SUSPEND_ON) || valid_state(state);
> >> +}
> >> +
> >> +int request_suspend_state(suspend_state_t state)
> >> +{
> >> +     unsigned long irqflags;
> >> +
> >> +     if (!request_suspend_valid_state(state))
> >> +             return -ENODEV;
> >> +
> >> +     spin_lock_irqsave(&state_lock, irqflags);
> >> +
> >> +     if (debug_mask & DEBUG_USER_STATE)
> >> +             pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
> >> +                          state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> >> +                          requested_suspend_state, state,
> >> +                          ktime_to_ns(ktime_get()));
> >> +
> >> +     requested_suspend_state = state;
> >> +     if (state == PM_SUSPEND_ON)
> >> +             suspend_block(&main_suspend_blocker);
> >> +     else
> >> +             suspend_unblock(&main_suspend_blocker);
> >> +     spin_unlock_irqrestore(&state_lock, irqflags);
> >> +     return 0;
> >> +}
> >
> > I think the two functions above should be static, shouldn't they?
> 
> No, they are used from main.c.
> 
> >
> >> +static int __init suspend_block_init(void)
> >> +{
> >> +     suspend_work_queue = create_singlethread_workqueue("suspend");
> >> +     if (!suspend_work_queue)
> >> +             return -ENOMEM;
> >> +
> >> +     suspend_blocker_init(&main_suspend_blocker, "main");
> >> +     suspend_block(&main_suspend_blocker);
> >> +     return 0;
> >> +}
> >> +
> >> +core_initcall(suspend_block_init);
> >
> > Hmm.  Why don't you want to put that initialization into pm_init() (in
> > kernel/power/main.c)?
> 
> It was not needed before, but I changed pm_init to call
> suspend_block_init after creating pm_wq.
> 
> >
> > Also, we already have one PM workqueue.  It is used for runtime PM, but I guess
> > it may be used just as well for the opportunistic suspend.  It is freezable,
> > but would it hurt?
> 
> No, it works, the freezable flag is just ignored when I call
> pm_suspend and I don't run anything else on the workqueue while
> threads are frozen. It does need to be a single threaded workqueue
> though, so make sure you don't just change that.

Freezable workqueues have to be singlethread or else there will be unfixable
races, so you can safely assume things will stay as they are in this respect.

Rafael


> 
> 

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28 23:35       ` Arve Hjønnevåg
@ 2010-04-29 15:41           ` Alan Stern
  0 siblings, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-04-29 15:41 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Rafael J. Wysocki, Linux-pm mailing list,
	Kernel development list, Tejun Heo, Oleg Nesterov, Len Brown,
	Pavel Machek, Randy Dunlap, Jesse Barnes, Nigel Cunningham,
	Cornelia Huck, Ming Lei, Wu Fengguang, Andrew Morton,
	Maxim Levitsky, linux-doc

On Wed, 28 Apr 2010, Arve Hjønnevåg wrote:

> >> >  suspend blockers can be used to allow
> >> > +user-space to decide whether a keystroke received while the system is suspended
> >> > +should cause the screen to be turned back on or allow the system to go back into
> >> > +suspend.
> >>
> >> That's not right.  Handling the screen doesn't need suspend blockers:
> >> The program decides what to do and then either turns on the screen or
> >> else writes "mem" to /sys/power/state.
> 
> That does not work though. Unless every key turns the screen on you
> will have a race every time the user presses a key you want to ignore.

Of course.  You are confirming what I wrote immediately below: Suspend
blockers help resolve races.  Note that this race has nothing to do
with the _screen_ in particular -- exactly the same race occurs if you
decide to turn on the audio speaker or some other piece of hardware.

> >>  What suspend blockers add is
> >> the ability to resolve races and satisfy multiple constraints when
> >> going into suspend -- which has nothing to do with operating the
> >> screen.
> 
> I'm not sure I agree with this. You cannot reliably turn the screen on
> from user space when the user presses a wakeup-key without suspend
> blockers.

Let's say that it has nothing to do _specifically_ with the screen.  
_Any_ action you want to take in userspace is difficult to coordinate 
with system suspends if you don't have suspend blockers.

> >>
> >> I _think_ what you're trying to get at can be expressed this way:
> >>
> >>       Here's an example showing how a cell phone or other embedded
> >>       system can handle keystrokes (or other input events) in the
> >>       presence of suspend blockers.  Use set_irq_wake...
> 
> OK, but the last version was what you (Alan) suggested last year.

So at least my mental processes have remained consistent over the span
of a year.  Nice to know I haven't undergone a complete personality 
change...  :-)

> >>       ...
> >>
> >>       - The user-space input-event thread returns from read.  It
> >>       carries out whatever activities are appropriate (for example,
> >>       powering up the display screen, running other programs, and so
> >>       on).  When it is finished, it calls suspend_unblock on the
> >>       process_input_events suspend_blocker and then calls select or
> >>       poll.  The system will automatically suspend again when it is
> >>       idle and no suspend blockers remain active.
> >
> > Yeah, that sounds better.  Arve, what do you think?
> >
> 
> Idle is irrelevant and needs to be removed. This new last step is also
> no longer a concrete example, but if you really think is it better I
> can change it.

Perhaps you would prefer to change this completely.  Write up a 
description of what can go wrong when suspend blockers _aren't_ used, 
and show how suspend blockers can prevent the problem from occurring.

But whatever you do, don't make it appear that suspend blockers allow
user programs to make decisions (which is what you wrote before).  They 
don't -- programs can make whatever decisions they want.  Suspend 
blockers merely help them carry out the actions they have decided upon 
in a safe manner.

And don't make it appear that suspend blockers can only be used for
solving screen-related problems.

Alan Stern


^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
@ 2010-04-29 15:41           ` Alan Stern
  0 siblings, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-04-29 15:41 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, linux-doc, Kernel development list, Jesse Barnes,
	Oleg Nesterov, Tejun Heo, Linux-pm mailing list, Wu Fengguang,
	Andrew Morton

On Wed, 28 Apr 2010, Arve Hjønnevåg wrote:

> >> >  suspend blockers can be used to allow
> >> > +user-space to decide whether a keystroke received while the system is suspended
> >> > +should cause the screen to be turned back on or allow the system to go back into
> >> > +suspend.
> >>
> >> That's not right.  Handling the screen doesn't need suspend blockers:
> >> The program decides what to do and then either turns on the screen or
> >> else writes "mem" to /sys/power/state.
> 
> That does not work though. Unless every key turns the screen on you
> will have a race every time the user presses a key you want to ignore.

Of course.  You are confirming what I wrote immediately below: Suspend
blockers help resolve races.  Note that this race has nothing to do
with the _screen_ in particular -- exactly the same race occurs if you
decide to turn on the audio speaker or some other piece of hardware.

> >>  What suspend blockers add is
> >> the ability to resolve races and satisfy multiple constraints when
> >> going into suspend -- which has nothing to do with operating the
> >> screen.
> 
> I'm not sure I agree with this. You cannot reliably turn the screen on
> from user space when the user presses a wakeup-key without suspend
> blockers.

Let's say that it has nothing to do _specifically_ with the screen.  
_Any_ action you want to take in userspace is difficult to coordinate 
with system suspends if you don't have suspend blockers.

> >>
> >> I _think_ what you're trying to get at can be expressed this way:
> >>
> >>       Here's an example showing how a cell phone or other embedded
> >>       system can handle keystrokes (or other input events) in the
> >>       presence of suspend blockers.  Use set_irq_wake...
> 
> OK, but the last version was what you (Alan) suggested last year.

So at least my mental processes have remained consistent over the span
of a year.  Nice to know I haven't undergone a complete personality 
change...  :-)

> >>       ...
> >>
> >>       - The user-space input-event thread returns from read.  It
> >>       carries out whatever activities are appropriate (for example,
> >>       powering up the display screen, running other programs, and so
> >>       on).  When it is finished, it calls suspend_unblock on the
> >>       process_input_events suspend_blocker and then calls select or
> >>       poll.  The system will automatically suspend again when it is
> >>       idle and no suspend blockers remain active.
> >
> > Yeah, that sounds better.  Arve, what do you think?
> >
> 
> Idle is irrelevant and needs to be removed. This new last step is also
> no longer a concrete example, but if you really think is it better I
> can change it.

Perhaps you would prefer to change this completely.  Write up a 
description of what can go wrong when suspend blockers _aren't_ used, 
and show how suspend blockers can prevent the problem from occurring.

But whatever you do, don't make it appear that suspend blockers allow
user programs to make decisions (which is what you wrote before).  They 
don't -- programs can make whatever decisions they want.  Suspend 
blockers merely help them carry out the actions they have decided upon 
in a safe manner.

And don't make it appear that suspend blockers can only be used for
solving screen-related problems.

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28 20:50   ` Rafael J. Wysocki
@ 2010-04-29  3:37     ` Arve Hjønnevåg
  2010-04-29 21:16         ` Rafael J. Wysocki
  2010-04-29  3:37     ` Arve Hjønnevåg
  1 sibling, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-04-29  3:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-pm, linux-kernel, Alan Stern, Tejun Heo, Oleg Nesterov,
	Len Brown, Pavel Machek, Randy Dunlap, Jesse Barnes, Magnus Damm,
	Nigel Cunningham, Cornelia Huck, Ming Lei, Wu Fengguang,
	Andrew Morton, Maxim Levitsky, linux-doc

2010/4/28 Rafael J. Wysocki <rjw@sisk.pl>:
...
>>
>> +/**
>> + *   policy - set policy for state
>> + */
>> +
>> +static ssize_t policy_show(struct kobject *kobj,
>> +                        struct kobj_attribute *attr, char *buf)
>> +{
>> +     char *s = buf;
>> +     int i;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(policies); i++) {
>> +             if (i == policy)
>> +                     s += sprintf(s, "[%s] ", policies[i].name);
>> +             else
>> +                     s += sprintf(s, "%s ", policies[i].name);
>> +     }
>> +     if (s != buf)
>> +             /* convert the last space to a newline */
>> +             *(s-1) = '\n';
>> +     return (s - buf);
>> +}
>> +
>> +static ssize_t policy_store(struct kobject *kobj,
>> +                         struct kobj_attribute *attr,
>> +                         const char *buf, size_t n)
>> +{
>> +     const char *s;
>> +     char *p;
>> +     int len;
>> +     int i;
>> +
>> +     p = memchr(buf, '\n', n);
>> +     len = p ? p - buf : n;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(policies); i++) {
>> +             s = policies[i].name;
>> +             if (s && len == strlen(s) && !strncmp(buf, s, len)) {
>> +                     mutex_lock(&pm_mutex);
>> +                     policies[policy].set_state(PM_SUSPEND_ON);
>> +                     policy = i;
>> +                     mutex_unlock(&pm_mutex);
>> +                     return n;
>> +             }
>> +     }
>> +     return -EINVAL;
>> +}
>> +
>> +power_attr(policy);
>> +
>>  #ifdef CONFIG_PM_TRACE
>>  int pm_trace_enabled;
>>
>
> Would you mind if I changed the above so that "policy" doesn't even show up
> if CONFIG_OPPORTUNISTIC_SUSPEND is unset?
>
I don't mind, but It did not seem worth the trouble to hide it. It
will only list the supported policies, and it is easy to add or remove
policies this way.

> ...
>> +static void suspend_worker(struct work_struct *work)
>> +{
>> +     int ret;
>> +     int entry_event_num;
>> +
>> +     enable_suspend_blockers = true;
>> +     while (!suspend_is_blocked()) {
>> +             entry_event_num = current_event_num;
>> +
>> +             if (debug_mask & DEBUG_SUSPEND)
>> +                     pr_info("suspend: enter suspend\n");
>> +
>> +             ret = pm_suspend(requested_suspend_state);
>> +
>> +             if (debug_mask & DEBUG_EXIT_SUSPEND)
>> +                     pr_info_time("suspend: exit suspend, ret = %d ", ret);
>> +
>> +             if (current_event_num == entry_event_num)
>> +                     pr_info("suspend: pm_suspend returned with no event\n");
>
> Hmm, what exactly is this for?  It looks like a debug thing to me.  I'd use
> pr_debug() here and in both debug printk()s above.  Would you agree?
>

If the driver that caused the wakeup does not use suspend blockers, we
the only choice is to try to suspend again. I want to know if this
happened. The stats patch disable this printk by default since it will
show up in the stats, and the timeout patch (not included here) delays
the retry.

...
>> +EXPORT_SYMBOL(suspend_blocker_init);
>
> Is there a strong objection to changing that (and the other instances below) to
> EXPORT_SYMBOL_GPL?
>

I don't know if it is a strong objection, but I prefer that this api
is available to all drivers. I don't want to prevent a user from using
opportunistic suspend because a non-gpl driver could not use suspend
blockers. I changed the suspend blocking work functions to be gpl only
though, since they are not required, and the workqueue api is
available to gpl code anyway.

...
>> +bool request_suspend_valid_state(suspend_state_t state)
>> +{
>> +     return (state == PM_SUSPEND_ON) || valid_state(state);
>> +}
>> +
>> +int request_suspend_state(suspend_state_t state)
>> +{
>> +     unsigned long irqflags;
>> +
>> +     if (!request_suspend_valid_state(state))
>> +             return -ENODEV;
>> +
>> +     spin_lock_irqsave(&state_lock, irqflags);
>> +
>> +     if (debug_mask & DEBUG_USER_STATE)
>> +             pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
>> +                          state != PM_SUSPEND_ON ? "sleep" : "wakeup",
>> +                          requested_suspend_state, state,
>> +                          ktime_to_ns(ktime_get()));
>> +
>> +     requested_suspend_state = state;
>> +     if (state == PM_SUSPEND_ON)
>> +             suspend_block(&main_suspend_blocker);
>> +     else
>> +             suspend_unblock(&main_suspend_blocker);
>> +     spin_unlock_irqrestore(&state_lock, irqflags);
>> +     return 0;
>> +}
>
> I think the two functions above should be static, shouldn't they?

No, they are used from main.c.

>
>> +static int __init suspend_block_init(void)
>> +{
>> +     suspend_work_queue = create_singlethread_workqueue("suspend");
>> +     if (!suspend_work_queue)
>> +             return -ENOMEM;
>> +
>> +     suspend_blocker_init(&main_suspend_blocker, "main");
>> +     suspend_block(&main_suspend_blocker);
>> +     return 0;
>> +}
>> +
>> +core_initcall(suspend_block_init);
>
> Hmm.  Why don't you want to put that initialization into pm_init() (in
> kernel/power/main.c)?

It was not needed before, but I changed pm_init to call
suspend_block_init after creating pm_wq.

>
> Also, we already have one PM workqueue.  It is used for runtime PM, but I guess
> it may be used just as well for the opportunistic suspend.  It is freezable,
> but would it hurt?

No, it works, the freezable flag is just ignored when I call
pm_suspend and I don't run anything else on the workqueue while
threads are frozen. It does need to be a single threaded workqueue
though, so make sure you don't just change that.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28 20:50   ` Rafael J. Wysocki
  2010-04-29  3:37     ` Arve Hjønnevåg
@ 2010-04-29  3:37     ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-04-29  3:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Len Brown, linux-doc, linux-kernel, Jesse Barnes, Oleg Nesterov,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

2010/4/28 Rafael J. Wysocki <rjw@sisk.pl>:
...
>>
>> +/**
>> + *   policy - set policy for state
>> + */
>> +
>> +static ssize_t policy_show(struct kobject *kobj,
>> +                        struct kobj_attribute *attr, char *buf)
>> +{
>> +     char *s = buf;
>> +     int i;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(policies); i++) {
>> +             if (i == policy)
>> +                     s += sprintf(s, "[%s] ", policies[i].name);
>> +             else
>> +                     s += sprintf(s, "%s ", policies[i].name);
>> +     }
>> +     if (s != buf)
>> +             /* convert the last space to a newline */
>> +             *(s-1) = '\n';
>> +     return (s - buf);
>> +}
>> +
>> +static ssize_t policy_store(struct kobject *kobj,
>> +                         struct kobj_attribute *attr,
>> +                         const char *buf, size_t n)
>> +{
>> +     const char *s;
>> +     char *p;
>> +     int len;
>> +     int i;
>> +
>> +     p = memchr(buf, '\n', n);
>> +     len = p ? p - buf : n;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(policies); i++) {
>> +             s = policies[i].name;
>> +             if (s && len == strlen(s) && !strncmp(buf, s, len)) {
>> +                     mutex_lock(&pm_mutex);
>> +                     policies[policy].set_state(PM_SUSPEND_ON);
>> +                     policy = i;
>> +                     mutex_unlock(&pm_mutex);
>> +                     return n;
>> +             }
>> +     }
>> +     return -EINVAL;
>> +}
>> +
>> +power_attr(policy);
>> +
>>  #ifdef CONFIG_PM_TRACE
>>  int pm_trace_enabled;
>>
>
> Would you mind if I changed the above so that "policy" doesn't even show up
> if CONFIG_OPPORTUNISTIC_SUSPEND is unset?
>
I don't mind, but It did not seem worth the trouble to hide it. It
will only list the supported policies, and it is easy to add or remove
policies this way.

> ...
>> +static void suspend_worker(struct work_struct *work)
>> +{
>> +     int ret;
>> +     int entry_event_num;
>> +
>> +     enable_suspend_blockers = true;
>> +     while (!suspend_is_blocked()) {
>> +             entry_event_num = current_event_num;
>> +
>> +             if (debug_mask & DEBUG_SUSPEND)
>> +                     pr_info("suspend: enter suspend\n");
>> +
>> +             ret = pm_suspend(requested_suspend_state);
>> +
>> +             if (debug_mask & DEBUG_EXIT_SUSPEND)
>> +                     pr_info_time("suspend: exit suspend, ret = %d ", ret);
>> +
>> +             if (current_event_num == entry_event_num)
>> +                     pr_info("suspend: pm_suspend returned with no event\n");
>
> Hmm, what exactly is this for?  It looks like a debug thing to me.  I'd use
> pr_debug() here and in both debug printk()s above.  Would you agree?
>

If the driver that caused the wakeup does not use suspend blockers, we
the only choice is to try to suspend again. I want to know if this
happened. The stats patch disable this printk by default since it will
show up in the stats, and the timeout patch (not included here) delays
the retry.

...
>> +EXPORT_SYMBOL(suspend_blocker_init);
>
> Is there a strong objection to changing that (and the other instances below) to
> EXPORT_SYMBOL_GPL?
>

I don't know if it is a strong objection, but I prefer that this api
is available to all drivers. I don't want to prevent a user from using
opportunistic suspend because a non-gpl driver could not use suspend
blockers. I changed the suspend blocking work functions to be gpl only
though, since they are not required, and the workqueue api is
available to gpl code anyway.

...
>> +bool request_suspend_valid_state(suspend_state_t state)
>> +{
>> +     return (state == PM_SUSPEND_ON) || valid_state(state);
>> +}
>> +
>> +int request_suspend_state(suspend_state_t state)
>> +{
>> +     unsigned long irqflags;
>> +
>> +     if (!request_suspend_valid_state(state))
>> +             return -ENODEV;
>> +
>> +     spin_lock_irqsave(&state_lock, irqflags);
>> +
>> +     if (debug_mask & DEBUG_USER_STATE)
>> +             pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
>> +                          state != PM_SUSPEND_ON ? "sleep" : "wakeup",
>> +                          requested_suspend_state, state,
>> +                          ktime_to_ns(ktime_get()));
>> +
>> +     requested_suspend_state = state;
>> +     if (state == PM_SUSPEND_ON)
>> +             suspend_block(&main_suspend_blocker);
>> +     else
>> +             suspend_unblock(&main_suspend_blocker);
>> +     spin_unlock_irqrestore(&state_lock, irqflags);
>> +     return 0;
>> +}
>
> I think the two functions above should be static, shouldn't they?

No, they are used from main.c.

>
>> +static int __init suspend_block_init(void)
>> +{
>> +     suspend_work_queue = create_singlethread_workqueue("suspend");
>> +     if (!suspend_work_queue)
>> +             return -ENOMEM;
>> +
>> +     suspend_blocker_init(&main_suspend_blocker, "main");
>> +     suspend_block(&main_suspend_blocker);
>> +     return 0;
>> +}
>> +
>> +core_initcall(suspend_block_init);
>
> Hmm.  Why don't you want to put that initialization into pm_init() (in
> kernel/power/main.c)?

It was not needed before, but I changed pm_init to call
suspend_block_init after creating pm_wq.

>
> Also, we already have one PM workqueue.  It is used for runtime PM, but I guess
> it may be used just as well for the opportunistic suspend.  It is freezable,
> but would it hurt?

No, it works, the freezable flag is just ignored when I call
pm_suspend and I don't run anything else on the workqueue while
threads are frozen. It does need to be a single threaded workqueue
though, so make sure you don't just change that.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28 21:13     ` Rafael J. Wysocki
  2010-04-28 23:35       ` Arve Hjønnevåg
@ 2010-04-28 23:35       ` Arve Hjønnevåg
  2010-04-29 15:41           ` Alan Stern
  1 sibling, 1 reply; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-04-28 23:35 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Alan Stern, Linux-pm mailing list, Kernel development list,
	Tejun Heo, Oleg Nesterov, Len Brown, Pavel Machek, Randy Dunlap,
	Jesse Barnes, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

2010/4/28 Rafael J. Wysocki <rjw@sisk.pl>:
> On Wednesday 28 April 2010, Alan Stern wrote:
>> On Tue, 27 Apr 2010, [UTF-8] Arve Hjønnevåg wrote:
>>
>> > +For example, in cell phones or other embedded systems, where powering the screen
>> > +is a significant drain on the battery, suspend blockers can be used to allow
>> > +user-space to decide whether a keystroke received while the system is suspended
>> > +should cause the screen to be turned back on or allow the system to go back into
>> > +suspend. Use set_irq_wake or a platform specific api to make sure the keypad
>> > +interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
>> > +events can look like this:
>> > +
>> > +- The Keypad driver gets an interrupt. It then calls suspend_block on the
>> > +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
>> > +- The keypad-scan code detects a key change and reports it to the input-event
>> > +  driver.
>> > +- The input-event driver sees the key change, enqueues an event, and calls
>> > +  suspend_block on the input-event-queue suspend_blocker.
>> > +- The keypad-scan code detects that no keys are held and calls suspend_unblock
>> > +  on the keypad-scan suspend_blocker.
>> > +- The user-space input-event thread returns from select/poll, calls
>> > +  suspend_block on the process-input-events suspend_blocker and then calls read
>> > +  on the input-event device.
>> > +- The input-event driver dequeues the key-event and, since the queue is now
>> > +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
>> > +- The user-space input-event thread returns from read. If it determines that
>> > +  the key should leave the screen off, it calls suspend_unblock on the
>> > +  process_input_events suspend_blocker and then calls select or poll. The
>> > +  system will automatically suspend again, since now no suspend blockers are
>> > +  active.
>> > +
>> > +                 Key pressed   Key released
>> > +                     |             |
>> > +keypad-scan          ++++++++++++++++++
>> > +input-event-queue        +++          +++
>> > +process-input-events       +++          +++
>>
>> This is better than before, but it still isn't ideal.  Here's what I
>> mean:
>>
>> >  suspend blockers can be used to allow
>> > +user-space to decide whether a keystroke received while the system is suspended
>> > +should cause the screen to be turned back on or allow the system to go back into
>> > +suspend.
>>
>> That's not right.  Handling the screen doesn't need suspend blockers:
>> The program decides what to do and then either turns on the screen or
>> else writes "mem" to /sys/power/state.

That does not work though. Unless every key turns the screen on you
will have a race every time the user presses a key you want to ignore.

>>  What suspend blockers add is
>> the ability to resolve races and satisfy multiple constraints when
>> going into suspend -- which has nothing to do with operating the
>> screen.

I'm not sure I agree with this. You cannot reliably turn the screen on
from user space when the user presses a wakeup-key without suspend
blockers.

>>
>> I _think_ what you're trying to get at can be expressed this way:
>>
>>       Here's an example showing how a cell phone or other embedded
>>       system can handle keystrokes (or other input events) in the
>>       presence of suspend blockers.  Use set_irq_wake...

OK, but the last version was what you (Alan) suggested last year.

>>
>>       ...
>>
>>       - The user-space input-event thread returns from read.  It
>>       carries out whatever activities are appropriate (for example,
>>       powering up the display screen, running other programs, and so
>>       on).  When it is finished, it calls suspend_unblock on the
>>       process_input_events suspend_blocker and then calls select or
>>       poll.  The system will automatically suspend again when it is
>>       idle and no suspend blockers remain active.
>
> Yeah, that sounds better.  Arve, what do you think?
>

Idle is irrelevant and needs to be removed. This new last step is also
no longer a concrete example, but if you really think is it better I
can change it.

>> > +/**
>> > + * suspend_block() - Block suspend
>> > + * @blocker:       The suspend blocker to use
>> > + *
>> > + * It is safe to call this function from interrupt context.
>> > + */
>> > +void suspend_block(struct suspend_blocker *blocker)
>> > +{
>> > +   unsigned long irqflags;
>> > +
>> > +   if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
>> > +           return;
>> > +
>> > +   spin_lock_irqsave(&list_lock, irqflags);
>> > +   blocker->flags |= SB_ACTIVE;
>> > +   list_del(&blocker->link);
>> > +
>> > +   if (debug_mask & DEBUG_SUSPEND_BLOCKER)
>> > +           pr_info("suspend_block: %s\n", blocker->name);
>> > +
>> > +   list_add(&blocker->link, &active_blockers);
>>
>> Here and in suspend_unblock(), you can use list_move() in place of
>> list_del() followed by list_add().
>

OK.

> Indeed.  And the debug statement might be moved out of the critical section IMHO.
>

If I move the debug statements out of the critical section you could
end entering suspend while the debug log claims a suspend blocker was
active, but I can move the debug statement to the start of the
critical section.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28 21:13     ` Rafael J. Wysocki
@ 2010-04-28 23:35       ` Arve Hjønnevåg
  2010-04-28 23:35       ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-04-28 23:35 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Len Brown, linux-doc, Kernel development list, Jesse Barnes,
	Oleg Nesterov, Tejun Heo, Linux-pm mailing list, Wu Fengguang,
	Andrew Morton

2010/4/28 Rafael J. Wysocki <rjw@sisk.pl>:
> On Wednesday 28 April 2010, Alan Stern wrote:
>> On Tue, 27 Apr 2010, [UTF-8] Arve Hjønnevåg wrote:
>>
>> > +For example, in cell phones or other embedded systems, where powering the screen
>> > +is a significant drain on the battery, suspend blockers can be used to allow
>> > +user-space to decide whether a keystroke received while the system is suspended
>> > +should cause the screen to be turned back on or allow the system to go back into
>> > +suspend. Use set_irq_wake or a platform specific api to make sure the keypad
>> > +interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
>> > +events can look like this:
>> > +
>> > +- The Keypad driver gets an interrupt. It then calls suspend_block on the
>> > +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
>> > +- The keypad-scan code detects a key change and reports it to the input-event
>> > +  driver.
>> > +- The input-event driver sees the key change, enqueues an event, and calls
>> > +  suspend_block on the input-event-queue suspend_blocker.
>> > +- The keypad-scan code detects that no keys are held and calls suspend_unblock
>> > +  on the keypad-scan suspend_blocker.
>> > +- The user-space input-event thread returns from select/poll, calls
>> > +  suspend_block on the process-input-events suspend_blocker and then calls read
>> > +  on the input-event device.
>> > +- The input-event driver dequeues the key-event and, since the queue is now
>> > +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
>> > +- The user-space input-event thread returns from read. If it determines that
>> > +  the key should leave the screen off, it calls suspend_unblock on the
>> > +  process_input_events suspend_blocker and then calls select or poll. The
>> > +  system will automatically suspend again, since now no suspend blockers are
>> > +  active.
>> > +
>> > +                 Key pressed   Key released
>> > +                     |             |
>> > +keypad-scan          ++++++++++++++++++
>> > +input-event-queue        +++          +++
>> > +process-input-events       +++          +++
>>
>> This is better than before, but it still isn't ideal.  Here's what I
>> mean:
>>
>> >  suspend blockers can be used to allow
>> > +user-space to decide whether a keystroke received while the system is suspended
>> > +should cause the screen to be turned back on or allow the system to go back into
>> > +suspend.
>>
>> That's not right.  Handling the screen doesn't need suspend blockers:
>> The program decides what to do and then either turns on the screen or
>> else writes "mem" to /sys/power/state.

That does not work though. Unless every key turns the screen on you
will have a race every time the user presses a key you want to ignore.

>>  What suspend blockers add is
>> the ability to resolve races and satisfy multiple constraints when
>> going into suspend -- which has nothing to do with operating the
>> screen.

I'm not sure I agree with this. You cannot reliably turn the screen on
from user space when the user presses a wakeup-key without suspend
blockers.

>>
>> I _think_ what you're trying to get at can be expressed this way:
>>
>>       Here's an example showing how a cell phone or other embedded
>>       system can handle keystrokes (or other input events) in the
>>       presence of suspend blockers.  Use set_irq_wake...

OK, but the last version was what you (Alan) suggested last year.

>>
>>       ...
>>
>>       - The user-space input-event thread returns from read.  It
>>       carries out whatever activities are appropriate (for example,
>>       powering up the display screen, running other programs, and so
>>       on).  When it is finished, it calls suspend_unblock on the
>>       process_input_events suspend_blocker and then calls select or
>>       poll.  The system will automatically suspend again when it is
>>       idle and no suspend blockers remain active.
>
> Yeah, that sounds better.  Arve, what do you think?
>

Idle is irrelevant and needs to be removed. This new last step is also
no longer a concrete example, but if you really think is it better I
can change it.

>> > +/**
>> > + * suspend_block() - Block suspend
>> > + * @blocker:       The suspend blocker to use
>> > + *
>> > + * It is safe to call this function from interrupt context.
>> > + */
>> > +void suspend_block(struct suspend_blocker *blocker)
>> > +{
>> > +   unsigned long irqflags;
>> > +
>> > +   if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
>> > +           return;
>> > +
>> > +   spin_lock_irqsave(&list_lock, irqflags);
>> > +   blocker->flags |= SB_ACTIVE;
>> > +   list_del(&blocker->link);
>> > +
>> > +   if (debug_mask & DEBUG_SUSPEND_BLOCKER)
>> > +           pr_info("suspend_block: %s\n", blocker->name);
>> > +
>> > +   list_add(&blocker->link, &active_blockers);
>>
>> Here and in suspend_unblock(), you can use list_move() in place of
>> list_del() followed by list_add().
>

OK.

> Indeed.  And the debug statement might be moved out of the critical section IMHO.
>

If I move the debug statements out of the critical section you could
end entering suspend while the debug log claims a suspend blocker was
active, but I can move the debug statement to the start of the
critical section.

-- 
Arve Hjønnevåg

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28 19:13   ` Alan Stern
  2010-04-28 21:13     ` Rafael J. Wysocki
@ 2010-04-28 21:13     ` Rafael J. Wysocki
  2010-04-28 23:35       ` Arve Hjønnevåg
  2010-04-28 23:35       ` Arve Hjønnevåg
  1 sibling, 2 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-04-28 21:13 UTC (permalink / raw)
  To: Alan Stern, Arve Hjønnevåg
  Cc: Linux-pm mailing list, Kernel development list, Tejun Heo,
	Oleg Nesterov, Len Brown, Pavel Machek, Randy Dunlap,
	Jesse Barnes, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

On Wednesday 28 April 2010, Alan Stern wrote:
> On Tue, 27 Apr 2010, [UTF-8] Arve Hjønnevåg wrote:
> 
> > +For example, in cell phones or other embedded systems, where powering the screen
> > +is a significant drain on the battery, suspend blockers can be used to allow
> > +user-space to decide whether a keystroke received while the system is suspended
> > +should cause the screen to be turned back on or allow the system to go back into
> > +suspend. Use set_irq_wake or a platform specific api to make sure the keypad
> > +interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
> > +events can look like this:
> > +
> > +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> > +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> > +- The keypad-scan code detects a key change and reports it to the input-event
> > +  driver.
> > +- The input-event driver sees the key change, enqueues an event, and calls
> > +  suspend_block on the input-event-queue suspend_blocker.
> > +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> > +  on the keypad-scan suspend_blocker.
> > +- The user-space input-event thread returns from select/poll, calls
> > +  suspend_block on the process-input-events suspend_blocker and then calls read
> > +  on the input-event device.
> > +- The input-event driver dequeues the key-event and, since the queue is now
> > +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> > +- The user-space input-event thread returns from read. If it determines that
> > +  the key should leave the screen off, it calls suspend_unblock on the
> > +  process_input_events suspend_blocker and then calls select or poll. The
> > +  system will automatically suspend again, since now no suspend blockers are
> > +  active.
> > +
> > +                 Key pressed   Key released
> > +                     |             |
> > +keypad-scan          ++++++++++++++++++
> > +input-event-queue        +++          +++
> > +process-input-events       +++          +++
> 
> This is better than before, but it still isn't ideal.  Here's what I 
> mean:
> 
> >  suspend blockers can be used to allow
> > +user-space to decide whether a keystroke received while the system is suspended
> > +should cause the screen to be turned back on or allow the system to go back into
> > +suspend.
> 
> That's not right.  Handling the screen doesn't need suspend blockers:
> The program decides what to do and then either turns on the screen or
> else writes "mem" to /sys/power/state.  What suspend blockers add is
> the ability to resolve races and satisfy multiple constraints when
> going into suspend -- which has nothing to do with operating the
> screen.
> 
> I _think_ what you're trying to get at can be expressed this way:
> 
> 	Here's an example showing how a cell phone or other embedded
> 	system can handle keystrokes (or other input events) in the 
> 	presence of suspend blockers.  Use set_irq_wake...
> 
> 	...
> 
>       - The user-space input-event thread returns from read.  It 
> 	carries out whatever activities are appropriate (for example,
> 	powering up the display screen, running other programs, and so 
> 	on).  When it is finished, it calls suspend_unblock on the
> 	process_input_events suspend_blocker and then calls select or
> 	poll.  The system will automatically suspend again when it is
> 	idle and no suspend blockers remain active.

Yeah, that sounds better.  Arve, what do you think?

> > +/**
> > + * suspend_block() - Block suspend
> > + * @blocker:	The suspend blocker to use
> > + *
> > + * It is safe to call this function from interrupt context.
> > + */
> > +void suspend_block(struct suspend_blocker *blocker)
> > +{
> > +	unsigned long irqflags;
> > +
> > +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> > +		return;
> > +
> > +	spin_lock_irqsave(&list_lock, irqflags);
> > +	blocker->flags |= SB_ACTIVE;
> > +	list_del(&blocker->link);
> > +
> > +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> > +		pr_info("suspend_block: %s\n", blocker->name);
> > +
> > +	list_add(&blocker->link, &active_blockers);
> 
> Here and in suspend_unblock(), you can use list_move() in place of 
> list_del() followed by list_add().

Indeed.  And the debug statement might be moved out of the critical section IMHO.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28 19:13   ` Alan Stern
@ 2010-04-28 21:13     ` Rafael J. Wysocki
  2010-04-28 21:13     ` Rafael J. Wysocki
  1 sibling, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-04-28 21:13 UTC (permalink / raw)
  To: Alan Stern, Arve Hjønnevåg
  Cc: Len Brown, linux-doc, Kernel development list, Jesse Barnes,
	Oleg Nesterov, Tejun Heo, Linux-pm mailing list, Wu Fengguang,
	Andrew Morton

On Wednesday 28 April 2010, Alan Stern wrote:
> On Tue, 27 Apr 2010, [UTF-8] Arve Hjønnevåg wrote:
> 
> > +For example, in cell phones or other embedded systems, where powering the screen
> > +is a significant drain on the battery, suspend blockers can be used to allow
> > +user-space to decide whether a keystroke received while the system is suspended
> > +should cause the screen to be turned back on or allow the system to go back into
> > +suspend. Use set_irq_wake or a platform specific api to make sure the keypad
> > +interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
> > +events can look like this:
> > +
> > +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> > +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> > +- The keypad-scan code detects a key change and reports it to the input-event
> > +  driver.
> > +- The input-event driver sees the key change, enqueues an event, and calls
> > +  suspend_block on the input-event-queue suspend_blocker.
> > +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> > +  on the keypad-scan suspend_blocker.
> > +- The user-space input-event thread returns from select/poll, calls
> > +  suspend_block on the process-input-events suspend_blocker and then calls read
> > +  on the input-event device.
> > +- The input-event driver dequeues the key-event and, since the queue is now
> > +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> > +- The user-space input-event thread returns from read. If it determines that
> > +  the key should leave the screen off, it calls suspend_unblock on the
> > +  process_input_events suspend_blocker and then calls select or poll. The
> > +  system will automatically suspend again, since now no suspend blockers are
> > +  active.
> > +
> > +                 Key pressed   Key released
> > +                     |             |
> > +keypad-scan          ++++++++++++++++++
> > +input-event-queue        +++          +++
> > +process-input-events       +++          +++
> 
> This is better than before, but it still isn't ideal.  Here's what I 
> mean:
> 
> >  suspend blockers can be used to allow
> > +user-space to decide whether a keystroke received while the system is suspended
> > +should cause the screen to be turned back on or allow the system to go back into
> > +suspend.
> 
> That's not right.  Handling the screen doesn't need suspend blockers:
> The program decides what to do and then either turns on the screen or
> else writes "mem" to /sys/power/state.  What suspend blockers add is
> the ability to resolve races and satisfy multiple constraints when
> going into suspend -- which has nothing to do with operating the
> screen.
> 
> I _think_ what you're trying to get at can be expressed this way:
> 
> 	Here's an example showing how a cell phone or other embedded
> 	system can handle keystrokes (or other input events) in the 
> 	presence of suspend blockers.  Use set_irq_wake...
> 
> 	...
> 
>       - The user-space input-event thread returns from read.  It 
> 	carries out whatever activities are appropriate (for example,
> 	powering up the display screen, running other programs, and so 
> 	on).  When it is finished, it calls suspend_unblock on the
> 	process_input_events suspend_blocker and then calls select or
> 	poll.  The system will automatically suspend again when it is
> 	idle and no suspend blockers remain active.

Yeah, that sounds better.  Arve, what do you think?

> > +/**
> > + * suspend_block() - Block suspend
> > + * @blocker:	The suspend blocker to use
> > + *
> > + * It is safe to call this function from interrupt context.
> > + */
> > +void suspend_block(struct suspend_blocker *blocker)
> > +{
> > +	unsigned long irqflags;
> > +
> > +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> > +		return;
> > +
> > +	spin_lock_irqsave(&list_lock, irqflags);
> > +	blocker->flags |= SB_ACTIVE;
> > +	list_del(&blocker->link);
> > +
> > +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> > +		pr_info("suspend_block: %s\n", blocker->name);
> > +
> > +	list_add(&blocker->link, &active_blockers);
> 
> Here and in suspend_unblock(), you can use list_move() in place of 
> list_del() followed by list_add().

Indeed.  And the debug statement might be moved out of the critical section IMHO.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28  4:31 ` Arve Hjønnevåg
                     ` (3 preceding siblings ...)
  2010-04-28 20:50   ` Rafael J. Wysocki
@ 2010-04-28 20:50   ` Rafael J. Wysocki
  2010-04-29  3:37     ` Arve Hjønnevåg
  2010-04-29  3:37     ` Arve Hjønnevåg
  2010-05-06 15:18   ` Alan Stern
  2010-05-06 15:18   ` Alan Stern
  6 siblings, 2 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-04-28 20:50 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: linux-pm, linux-kernel, Alan Stern, Tejun Heo, Oleg Nesterov,
	Len Brown, Pavel Machek, Randy Dunlap, Jesse Barnes, Magnus Damm,
	Nigel Cunningham, Cornelia Huck, Ming Lei, Wu Fengguang,
	Andrew Morton, Maxim Levitsky, linux-doc

On Wednesday 28 April 2010, Arve Hjønnevåg wrote:
> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.
> 
> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> ---
...
> @@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
>  unsigned int pm_flags;
>  EXPORT_SYMBOL(pm_flags);
>  
> +struct policy {
> +	const char *name;
> +	bool (*valid_state)(suspend_state_t state);
> +	int (*set_state)(suspend_state_t state);
> +};
> +static struct policy policies[] = {
> +	{
> +		.name		= "forced",
> +		.valid_state	= valid_state,
> +		.set_state	= enter_state,
> +	},
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	{
> +		.name		= "opportunistic",
> +		.valid_state	= request_suspend_valid_state,
> +		.set_state	= request_suspend_state,
> +	},
> +#endif
> +};
> +static int policy;
> +
>  #ifdef CONFIG_PM_SLEEP
>  
>  /* Routines for PM-transition notifications */
> @@ -146,6 +168,12 @@ struct kobject *power_kobj;
>   *
>   *	store() accepts one of those strings, translates it into the 
>   *	proper enumerated value, and initiates a suspend transition.
> + *
> + *	If policy is set to opportunistic, store() does not block until the
> + *	system resumes, and it will try to re-enter the state until another
> + *	state is requested. Suspend blockers are respected and the requested
> + *	state will only be entered when no suspend blockers are active.
> + *	Write "on" to cancel.
>   */
>  static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  			  char *buf)
> @@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  	int i;
>  
>  	for (i = 0; i < PM_SUSPEND_MAX; i++) {
> -		if (pm_states[i] && valid_state(i))
> +		if (pm_states[i] && policies[policy].valid_state(i))
>  			s += sprintf(s,"%s ", pm_states[i]);
>  	}
>  #endif
>  #ifdef CONFIG_HIBERNATION
> -	s += sprintf(s, "%s\n", "disk");
> +	if (!policy)
> +		s += sprintf(s, "%s\n", "disk");
>  #else
>  	if (s != buf)
>  		/* convert the last space to a newline */
> @@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			   const char *buf, size_t n)
>  {
>  #ifdef CONFIG_SUSPEND
> -	suspend_state_t state = PM_SUSPEND_STANDBY;
> +	suspend_state_t state = PM_SUSPEND_ON;
>  	const char * const *s;
>  #endif
>  	char *p;
> @@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  	len = p ? p - buf : n;
>  
>  	/* First, check if we are requested to hibernate */
> -	if (len == 4 && !strncmp(buf, "disk", len)) {
> +	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
>  		error = hibernate();
>    goto Exit;
>  	}
> @@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			break;
>  	}
>  	if (state < PM_SUSPEND_MAX && *s)
> -		error = enter_state(state);
> +		error = policies[policy].set_state(state);
>  #endif
>  
>   Exit:
> @@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  
>  power_attr(state);
>  
> +/**
> + *	policy - set policy for state
> + */
> +
> +static ssize_t policy_show(struct kobject *kobj,
> +			   struct kobj_attribute *attr, char *buf)
> +{
> +	char *s = buf;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		if (i == policy)
> +			s += sprintf(s, "[%s] ", policies[i].name);
> +		else
> +			s += sprintf(s, "%s ", policies[i].name);
> +	}
> +	if (s != buf)
> +		/* convert the last space to a newline */
> +		*(s-1) = '\n';
> +	return (s - buf);
> +}
> +
> +static ssize_t policy_store(struct kobject *kobj,
> +			    struct kobj_attribute *attr,
> +			    const char *buf, size_t n)
> +{
> +	const char *s;
> +	char *p;
> +	int len;
> +	int i;
> +
> +	p = memchr(buf, '\n', n);
> +	len = p ? p - buf : n;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		s = policies[i].name;
> +		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
> +			mutex_lock(&pm_mutex);
> +			policies[policy].set_state(PM_SUSPEND_ON);
> +			policy = i;
> +			mutex_unlock(&pm_mutex);
> +			return n;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +power_attr(policy);
> +
>  #ifdef CONFIG_PM_TRACE
>  int pm_trace_enabled;
>  

Would you mind if I changed the above so that "policy" doesn't even show up
if CONFIG_OPPORTUNISTIC_SUSPEND is unset?

...
> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = true;
> +	while (!suspend_is_blocked()) {
> +		entry_event_num = current_event_num;
> +
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: enter suspend\n");
> +
> +		ret = pm_suspend(requested_suspend_state);
> +
> +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> +
> +		if (current_event_num == entry_event_num)
> +			pr_info("suspend: pm_suspend returned with no event\n");

Hmm, what exactly is this for?  It looks like a debug thing to me.  I'd use
pr_debug() here and in both debug printk()s above.  Would you agree?

> +	}
> +	enable_suspend_blockers = false;
> +}
> +static DECLARE_WORK(suspend_work, suspend_worker);
> +
> +/**
> + * suspend_blocker_init() - Initialize a suspend blocker
> + * @blocker:	The suspend blocker to initialize.
> + * @name:	The name of the suspend blocker to show in debug messages.
> + *
> + * The suspend blocker struct and name must not be freed before calling
> + * suspend_blocker_destroy.
> + */
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
> +{
> +	unsigned long irqflags = 0;
> +
> +	WARN_ON(!name);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_init name=%s\n", name);
> +
> +	blocker->name = name;
> +	blocker->flags = SB_INITIALIZED;
> +	INIT_LIST_HEAD(&blocker->link);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	list_add(&blocker->link, &inactive_blockers);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_init);

Is there a strong objection to changing that (and the other instances below) to
EXPORT_SYMBOL_GPL?

> +
> +/**
> + * suspend_blocker_destroy() - Destroy a suspend blocker
> + * @blocker:	The suspend blocker to destroy.
> + */
> +void suspend_blocker_destroy(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags &= ~SB_INITIALIZED;
> +	list_del(&blocker->link);
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(suspend_work_queue, &suspend_work);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_destroy);
> +
> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags |= SB_ACTIVE;
> +	list_del(&blocker->link);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +
> +	list_add(&blocker->link, &active_blockers);
> +
> +	current_event_num++;
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_block);
> +
> +/**
> + * suspend_unblock() - Unblock suspend
> + * @blocker:	The suspend blocker to unblock.
> + *
> + * If no other suspend blockers block suspend, the system will suspend.
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_unblock(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_unblock: %s\n", blocker->name);
> +
> +	list_del(&blocker->link);
> +	list_add(&blocker->link, &inactive_blockers);
> +
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(suspend_work_queue, &suspend_work);
> +	blocker->flags &= ~(SB_ACTIVE);
> +	if (blocker == &main_suspend_blocker) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			print_active_blockers_locked();
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_unblock);
> +
> +/**
> + * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
> + * @blocker:	The suspend blocker to check.
> + *
> + * Returns true if the suspend_blocker is currently active.
> + */
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker)
> +{
> +	WARN_ON(!(blocker->flags & SB_INITIALIZED));
> +
> +	return !!(blocker->flags & SB_ACTIVE);
> +}
> +EXPORT_SYMBOL(suspend_blocker_is_active);
> +
> +bool request_suspend_valid_state(suspend_state_t state)
> +{
> +	return (state == PM_SUSPEND_ON) || valid_state(state);
> +}
> +
> +int request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +
> +	if (!request_suspend_valid_state(state))
> +		return -ENODEV;
> +
> +	spin_lock_irqsave(&state_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_USER_STATE)
> +		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
> +			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> +			     requested_suspend_state, state,
> +			     ktime_to_ns(ktime_get()));
> +
> +	requested_suspend_state = state;
> +	if (state == PM_SUSPEND_ON)
> +		suspend_block(&main_suspend_blocker);
> +	else
> +		suspend_unblock(&main_suspend_blocker);
> +	spin_unlock_irqrestore(&state_lock, irqflags);
> +	return 0;
> +}

I think the two functions above should be static, shouldn't they?

> +static int __init suspend_block_init(void)
> +{
> +	suspend_work_queue = create_singlethread_workqueue("suspend");
> +	if (!suspend_work_queue)
> +		return -ENOMEM;
> +
> +	suspend_blocker_init(&main_suspend_blocker, "main");
> +	suspend_block(&main_suspend_blocker);
> +	return 0;
> +}
> +
> +core_initcall(suspend_block_init);

Hmm.  Why don't you want to put that initialization into pm_init() (in
kernel/power/main.c)?

Also, we already have one PM workqueue.  It is used for runtime PM, but I guess
it may be used just as well for the opportunistic suspend.  It is freezable,
but would it hurt?

Rafael

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28  4:31 ` Arve Hjønnevåg
                     ` (2 preceding siblings ...)
  2010-04-28 19:13   ` Alan Stern
@ 2010-04-28 20:50   ` Rafael J. Wysocki
  2010-04-28 20:50   ` Rafael J. Wysocki
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 195+ messages in thread
From: Rafael J. Wysocki @ 2010-04-28 20:50 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, linux-doc, linux-kernel, Jesse Barnes, Oleg Nesterov,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

On Wednesday 28 April 2010, Arve Hjønnevåg wrote:
> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.
> 
> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> ---
...
> @@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
>  unsigned int pm_flags;
>  EXPORT_SYMBOL(pm_flags);
>  
> +struct policy {
> +	const char *name;
> +	bool (*valid_state)(suspend_state_t state);
> +	int (*set_state)(suspend_state_t state);
> +};
> +static struct policy policies[] = {
> +	{
> +		.name		= "forced",
> +		.valid_state	= valid_state,
> +		.set_state	= enter_state,
> +	},
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	{
> +		.name		= "opportunistic",
> +		.valid_state	= request_suspend_valid_state,
> +		.set_state	= request_suspend_state,
> +	},
> +#endif
> +};
> +static int policy;
> +
>  #ifdef CONFIG_PM_SLEEP
>  
>  /* Routines for PM-transition notifications */
> @@ -146,6 +168,12 @@ struct kobject *power_kobj;
>   *
>   *	store() accepts one of those strings, translates it into the 
>   *	proper enumerated value, and initiates a suspend transition.
> + *
> + *	If policy is set to opportunistic, store() does not block until the
> + *	system resumes, and it will try to re-enter the state until another
> + *	state is requested. Suspend blockers are respected and the requested
> + *	state will only be entered when no suspend blockers are active.
> + *	Write "on" to cancel.
>   */
>  static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  			  char *buf)
> @@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  	int i;
>  
>  	for (i = 0; i < PM_SUSPEND_MAX; i++) {
> -		if (pm_states[i] && valid_state(i))
> +		if (pm_states[i] && policies[policy].valid_state(i))
>  			s += sprintf(s,"%s ", pm_states[i]);
>  	}
>  #endif
>  #ifdef CONFIG_HIBERNATION
> -	s += sprintf(s, "%s\n", "disk");
> +	if (!policy)
> +		s += sprintf(s, "%s\n", "disk");
>  #else
>  	if (s != buf)
>  		/* convert the last space to a newline */
> @@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			   const char *buf, size_t n)
>  {
>  #ifdef CONFIG_SUSPEND
> -	suspend_state_t state = PM_SUSPEND_STANDBY;
> +	suspend_state_t state = PM_SUSPEND_ON;
>  	const char * const *s;
>  #endif
>  	char *p;
> @@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  	len = p ? p - buf : n;
>  
>  	/* First, check if we are requested to hibernate */
> -	if (len == 4 && !strncmp(buf, "disk", len)) {
> +	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
>  		error = hibernate();
>    goto Exit;
>  	}
> @@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			break;
>  	}
>  	if (state < PM_SUSPEND_MAX && *s)
> -		error = enter_state(state);
> +		error = policies[policy].set_state(state);
>  #endif
>  
>   Exit:
> @@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  
>  power_attr(state);
>  
> +/**
> + *	policy - set policy for state
> + */
> +
> +static ssize_t policy_show(struct kobject *kobj,
> +			   struct kobj_attribute *attr, char *buf)
> +{
> +	char *s = buf;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		if (i == policy)
> +			s += sprintf(s, "[%s] ", policies[i].name);
> +		else
> +			s += sprintf(s, "%s ", policies[i].name);
> +	}
> +	if (s != buf)
> +		/* convert the last space to a newline */
> +		*(s-1) = '\n';
> +	return (s - buf);
> +}
> +
> +static ssize_t policy_store(struct kobject *kobj,
> +			    struct kobj_attribute *attr,
> +			    const char *buf, size_t n)
> +{
> +	const char *s;
> +	char *p;
> +	int len;
> +	int i;
> +
> +	p = memchr(buf, '\n', n);
> +	len = p ? p - buf : n;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		s = policies[i].name;
> +		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
> +			mutex_lock(&pm_mutex);
> +			policies[policy].set_state(PM_SUSPEND_ON);
> +			policy = i;
> +			mutex_unlock(&pm_mutex);
> +			return n;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +power_attr(policy);
> +
>  #ifdef CONFIG_PM_TRACE
>  int pm_trace_enabled;
>  

Would you mind if I changed the above so that "policy" doesn't even show up
if CONFIG_OPPORTUNISTIC_SUSPEND is unset?

...
> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = true;
> +	while (!suspend_is_blocked()) {
> +		entry_event_num = current_event_num;
> +
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: enter suspend\n");
> +
> +		ret = pm_suspend(requested_suspend_state);
> +
> +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> +
> +		if (current_event_num == entry_event_num)
> +			pr_info("suspend: pm_suspend returned with no event\n");

Hmm, what exactly is this for?  It looks like a debug thing to me.  I'd use
pr_debug() here and in both debug printk()s above.  Would you agree?

> +	}
> +	enable_suspend_blockers = false;
> +}
> +static DECLARE_WORK(suspend_work, suspend_worker);
> +
> +/**
> + * suspend_blocker_init() - Initialize a suspend blocker
> + * @blocker:	The suspend blocker to initialize.
> + * @name:	The name of the suspend blocker to show in debug messages.
> + *
> + * The suspend blocker struct and name must not be freed before calling
> + * suspend_blocker_destroy.
> + */
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
> +{
> +	unsigned long irqflags = 0;
> +
> +	WARN_ON(!name);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_init name=%s\n", name);
> +
> +	blocker->name = name;
> +	blocker->flags = SB_INITIALIZED;
> +	INIT_LIST_HEAD(&blocker->link);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	list_add(&blocker->link, &inactive_blockers);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_init);

Is there a strong objection to changing that (and the other instances below) to
EXPORT_SYMBOL_GPL?

> +
> +/**
> + * suspend_blocker_destroy() - Destroy a suspend blocker
> + * @blocker:	The suspend blocker to destroy.
> + */
> +void suspend_blocker_destroy(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags &= ~SB_INITIALIZED;
> +	list_del(&blocker->link);
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(suspend_work_queue, &suspend_work);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_destroy);
> +
> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags |= SB_ACTIVE;
> +	list_del(&blocker->link);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +
> +	list_add(&blocker->link, &active_blockers);
> +
> +	current_event_num++;
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_block);
> +
> +/**
> + * suspend_unblock() - Unblock suspend
> + * @blocker:	The suspend blocker to unblock.
> + *
> + * If no other suspend blockers block suspend, the system will suspend.
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_unblock(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_unblock: %s\n", blocker->name);
> +
> +	list_del(&blocker->link);
> +	list_add(&blocker->link, &inactive_blockers);
> +
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(suspend_work_queue, &suspend_work);
> +	blocker->flags &= ~(SB_ACTIVE);
> +	if (blocker == &main_suspend_blocker) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			print_active_blockers_locked();
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_unblock);
> +
> +/**
> + * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
> + * @blocker:	The suspend blocker to check.
> + *
> + * Returns true if the suspend_blocker is currently active.
> + */
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker)
> +{
> +	WARN_ON(!(blocker->flags & SB_INITIALIZED));
> +
> +	return !!(blocker->flags & SB_ACTIVE);
> +}
> +EXPORT_SYMBOL(suspend_blocker_is_active);
> +
> +bool request_suspend_valid_state(suspend_state_t state)
> +{
> +	return (state == PM_SUSPEND_ON) || valid_state(state);
> +}
> +
> +int request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +
> +	if (!request_suspend_valid_state(state))
> +		return -ENODEV;
> +
> +	spin_lock_irqsave(&state_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_USER_STATE)
> +		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
> +			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> +			     requested_suspend_state, state,
> +			     ktime_to_ns(ktime_get()));
> +
> +	requested_suspend_state = state;
> +	if (state == PM_SUSPEND_ON)
> +		suspend_block(&main_suspend_blocker);
> +	else
> +		suspend_unblock(&main_suspend_blocker);
> +	spin_unlock_irqrestore(&state_lock, irqflags);
> +	return 0;
> +}

I think the two functions above should be static, shouldn't they?

> +static int __init suspend_block_init(void)
> +{
> +	suspend_work_queue = create_singlethread_workqueue("suspend");
> +	if (!suspend_work_queue)
> +		return -ENOMEM;
> +
> +	suspend_blocker_init(&main_suspend_blocker, "main");
> +	suspend_block(&main_suspend_blocker);
> +	return 0;
> +}
> +
> +core_initcall(suspend_block_init);

Hmm.  Why don't you want to put that initialization into pm_init() (in
kernel/power/main.c)?

Also, we already have one PM workqueue.  It is used for runtime PM, but I guess
it may be used just as well for the opportunistic suspend.  It is freezable,
but would it hurt?

Rafael
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28  4:31 ` Arve Hjønnevåg
  2010-04-28  6:07     ` Pavel Machek
@ 2010-04-28 19:13   ` Alan Stern
  2010-04-28 21:13     ` Rafael J. Wysocki
  2010-04-28 21:13     ` Rafael J. Wysocki
  2010-04-28 19:13   ` Alan Stern
                     ` (4 subsequent siblings)
  6 siblings, 2 replies; 195+ messages in thread
From: Alan Stern @ 2010-04-28 19:13 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Linux-pm mailing list, Kernel development list,
	Rafael J. Wysocki, Tejun Heo, Oleg Nesterov, Len Brown,
	Pavel Machek, Randy Dunlap, Jesse Barnes, Nigel Cunningham,
	Cornelia Huck, Ming Lei, Wu Fengguang, Andrew Morton,
	Maxim Levitsky, linux-doc

On Tue, 27 Apr 2010, [UTF-8] Arve Hjønnevåg wrote:

> +For example, in cell phones or other embedded systems, where powering the screen
> +is a significant drain on the battery, suspend blockers can be used to allow
> +user-space to decide whether a keystroke received while the system is suspended
> +should cause the screen to be turned back on or allow the system to go back into
> +suspend. Use set_irq_wake or a platform specific api to make sure the keypad
> +interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
> +events can look like this:
> +
> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> +- The keypad-scan code detects a key change and reports it to the input-event
> +  driver.
> +- The input-event driver sees the key change, enqueues an event, and calls
> +  suspend_block on the input-event-queue suspend_blocker.
> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> +  on the keypad-scan suspend_blocker.
> +- The user-space input-event thread returns from select/poll, calls
> +  suspend_block on the process-input-events suspend_blocker and then calls read
> +  on the input-event device.
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> +- The user-space input-event thread returns from read. If it determines that
> +  the key should leave the screen off, it calls suspend_unblock on the
> +  process_input_events suspend_blocker and then calls select or poll. The
> +  system will automatically suspend again, since now no suspend blockers are
> +  active.
> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++

This is better than before, but it still isn't ideal.  Here's what I 
mean:

>  suspend blockers can be used to allow
> +user-space to decide whether a keystroke received while the system is suspended
> +should cause the screen to be turned back on or allow the system to go back into
> +suspend.

That's not right.  Handling the screen doesn't need suspend blockers:
The program decides what to do and then either turns on the screen or
else writes "mem" to /sys/power/state.  What suspend blockers add is
the ability to resolve races and satisfy multiple constraints when
going into suspend -- which has nothing to do with operating the
screen.

I _think_ what you're trying to get at can be expressed this way:

	Here's an example showing how a cell phone or other embedded
	system can handle keystrokes (or other input events) in the 
	presence of suspend blockers.  Use set_irq_wake...

	...

      - The user-space input-event thread returns from read.  It 
	carries out whatever activities are appropriate (for example,
	powering up the display screen, running other programs, and so 
	on).  When it is finished, it calls suspend_unblock on the
	process_input_events suspend_blocker and then calls select or
	poll.  The system will automatically suspend again when it is
	idle and no suspend blockers remain active.


> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags |= SB_ACTIVE;
> +	list_del(&blocker->link);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +
> +	list_add(&blocker->link, &active_blockers);

Here and in suspend_unblock(), you can use list_move() in place of 
list_del() followed by list_add().

Alan Stern



^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28  4:31 ` Arve Hjønnevåg
  2010-04-28  6:07     ` Pavel Machek
  2010-04-28 19:13   ` Alan Stern
@ 2010-04-28 19:13   ` Alan Stern
  2010-04-28 20:50   ` Rafael J. Wysocki
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 195+ messages in thread
From: Alan Stern @ 2010-04-28 19:13 UTC (permalink / raw)
  To: Arve Hjønnevåg
  Cc: Len Brown, linux-doc, Kernel development list, Jesse Barnes,
	Oleg Nesterov, Tejun Heo, Linux-pm mailing list, Wu Fengguang,
	Andrew Morton

On Tue, 27 Apr 2010, [UTF-8] Arve Hjønnevåg wrote:

> +For example, in cell phones or other embedded systems, where powering the screen
> +is a significant drain on the battery, suspend blockers can be used to allow
> +user-space to decide whether a keystroke received while the system is suspended
> +should cause the screen to be turned back on or allow the system to go back into
> +suspend. Use set_irq_wake or a platform specific api to make sure the keypad
> +interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
> +events can look like this:
> +
> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> +- The keypad-scan code detects a key change and reports it to the input-event
> +  driver.
> +- The input-event driver sees the key change, enqueues an event, and calls
> +  suspend_block on the input-event-queue suspend_blocker.
> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> +  on the keypad-scan suspend_blocker.
> +- The user-space input-event thread returns from select/poll, calls
> +  suspend_block on the process-input-events suspend_blocker and then calls read
> +  on the input-event device.
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> +- The user-space input-event thread returns from read. If it determines that
> +  the key should leave the screen off, it calls suspend_unblock on the
> +  process_input_events suspend_blocker and then calls select or poll. The
> +  system will automatically suspend again, since now no suspend blockers are
> +  active.
> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++

This is better than before, but it still isn't ideal.  Here's what I 
mean:

>  suspend blockers can be used to allow
> +user-space to decide whether a keystroke received while the system is suspended
> +should cause the screen to be turned back on or allow the system to go back into
> +suspend.

That's not right.  Handling the screen doesn't need suspend blockers:
The program decides what to do and then either turns on the screen or
else writes "mem" to /sys/power/state.  What suspend blockers add is
the ability to resolve races and satisfy multiple constraints when
going into suspend -- which has nothing to do with operating the
screen.

I _think_ what you're trying to get at can be expressed this way:

	Here's an example showing how a cell phone or other embedded
	system can handle keystrokes (or other input events) in the 
	presence of suspend blockers.  Use set_irq_wake...

	...

      - The user-space input-event thread returns from read.  It 
	carries out whatever activities are appropriate (for example,
	powering up the display screen, running other programs, and so 
	on).  When it is finished, it calls suspend_unblock on the
	process_input_events suspend_blocker and then calls select or
	poll.  The system will automatically suspend again when it is
	idle and no suspend blockers remain active.


> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags |= SB_ACTIVE;
> +	list_del(&blocker->link);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +
> +	list_add(&blocker->link, &active_blockers);

Here and in suspend_unblock(), you can use list_move() in place of 
list_del() followed by list_add().

Alan Stern

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
  2010-04-28  4:31 ` Arve Hjønnevåg
@ 2010-04-28  6:07     ` Pavel Machek
  2010-04-28 19:13   ` Alan Stern
                       ` (5 subsequent siblings)
  6 siblings, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-04-28  6:07 UTC (permalink / raw)
  To: Arve Hj??nnev??g
  Cc: linux-pm, linux-kernel, Rafael J. Wysocki, Alan Stern, Tejun Heo,
	Oleg Nesterov, Len Brown, Randy Dunlap, Jesse Barnes,
	Magnus Damm, Nigel Cunningham, Cornelia Huck, Ming Lei,
	Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

Hi!

> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.

I really don't like how this changes semantics of 'state'. I guess I'd
prefer leaving state as is -- forced transition to hibernation while
system is set to opportunistically suspend seems sane -- and adding
something like

/sys/power/autosleep

with 'off' or 'suspend' values?


> 
> Signed-off-by: Arve Hj??nnev??g <arve@android.com>
> ---
>  Documentation/power/opportunistic-suspend.txt |  114 +++++++++++
>  include/linux/suspend_blocker.h               |   64 ++++++
>  kernel/power/Kconfig                          |   16 ++
>  kernel/power/Makefile                         |    1 +
>  kernel/power/main.c                           |   89 ++++++++-
>  kernel/power/power.h                          |    5 +
>  kernel/power/suspend.c                        |    4 +-
>  kernel/power/suspend_blocker.c                |  269 +++++++++++++++++++++++++
>  8 files changed, 556 insertions(+), 6 deletions(-)
>  create mode 100644 Documentation/power/opportunistic-suspend.txt
>  create mode 100755 include/linux/suspend_blocker.h
>  create mode 100644 kernel/power/suspend_blocker.c
> 
> diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
> new file mode 100644
> index 0000000..1a29d10
> --- /dev/null
> +++ b/Documentation/power/opportunistic-suspend.txt
> @@ -0,0 +1,114 @@
> +Opportunistic Suspend
> +=====================
> +
> +Opportunistic suspend is a feature allowing the system to be suspended (ie. put
> +into one of the available sleep states) automatically whenever it is regarded
> +as idle.  The suspend blockers framework described below is used to determine
> +when that happens.
> +
> +The /sys/power/policy sysfs attribute is used to switch the system between the
> +opportunistic and "forced" suspend behavior, where in the latter case the
> +system is only suspended if a specific value, corresponding to one of the
> +available system sleep states, is written into /sys/power/state.  However, in
> +the former, opportunistic, case the system is put into the sleep state
> +corresponding to the value written to /sys/power/state whenever there are no
> +active suspend blockers. The default policy is "forced". Also, suspend blockers
> +do not affect sleep states entered from idle.
> +
> +When the policy is "opportunisic", there is a special value, "on", that can be
> +written to /sys/power/state. This will block the automatic sleep request, as if
> +a suspend blocker was used by a device driver. This way the opportunistic
> +suspend may be blocked by user space whithout switching back to the "forced"
> +mode.
> +
> +A suspend blocker is an object used to inform the PM subsystem when the system
> +can or cannot be suspended in the "opportunistic" mode (the "forced" mode
> +ignores suspend blockers).  To use it, a device driver creates a struct
> +suspend_blocker that must be initialized with suspend_blocker_init(). Before
> +freeing the suspend_blocker structure or its name, suspend_blocker_destroy()
> +must be called on it.
> +
> +A suspend blocker is activated using suspend_block(), which prevents the PM
> +subsystem from putting the system into the requested sleep state in the
> +"opportunistic" mode until the suspend blocker is deactivated with
> +suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
> +the system will not suspend as long as at least one of them is active.
> +
> +If opportunistic suspend is already in progress when suspend_block() is called,
> +it will abort the suspend, unless suspend_ops->enter has already been
> +executed. If suspend is aborted this way, the system is usually not fully
> +operational at that point. The suspend callbacks of some drivers may still be
> +running and it usually takes time to restore the system to the fully operational
> +state.
> +
> +For example, in cell phones or other embedded systems, where powering the screen
> +is a significant drain on the battery, suspend blockers can be used to allow
> +user-space to decide whether a keystroke received while the system is suspended
> +should cause the screen to be turned back on or allow the system to go back into
> +suspend. Use set_irq_wake or a platform specific api to make sure the keypad
> +interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
> +events can look like this:
> +
> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> +- The keypad-scan code detects a key change and reports it to the input-event
> +  driver.
> +- The input-event driver sees the key change, enqueues an event, and calls
> +  suspend_block on the input-event-queue suspend_blocker.
> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> +  on the keypad-scan suspend_blocker.
> +- The user-space input-event thread returns from select/poll, calls
> +  suspend_block on the process-input-events suspend_blocker and then calls read
> +  on the input-event device.
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> +- The user-space input-event thread returns from read. If it determines that
> +  the key should leave the screen off, it calls suspend_unblock on the
> +  process_input_events suspend_blocker and then calls select or poll. The
> +  system will automatically suspend again, since now no suspend blockers are
> +  active.
> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++
> +
> +
> +Driver API
> +==========
> +
> +A driver can use the suspend block api by adding a suspend_blocker variable to
> +its state and calling suspend_blocker_init. For instance:
> +struct state {
> +	struct suspend_blocker suspend_blocker;
> +}
> +
> +init() {
> +	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
> +}
> +
> +Before freeing the memory, suspend_blocker_destroy must be called:
> +
> +uninit() {
> +	suspend_blocker_destroy(&state->suspend_blocker);
> +}
> +
> +When the driver determines that it needs to run (usually in an interrupt
> +handler) it calls suspend_block:
> +	suspend_block(&state->suspend_blocker);
> +
> +When it no longer needs to run it calls suspend_unblock:
> +	suspend_unblock(&state->suspend_blocker);
> +
> +Calling suspend_block when the suspend blocker is active or suspend_unblock when
> +it is not active has no effect (i.e., these functions don't nest). This allows
> +drivers to update their state and call suspend suspend_block or suspend_unblock
> +based on the result.
> +For instance:
> +
> +if (list_empty(&state->pending_work))
> +	suspend_unblock(&state->suspend_blocker);
> +else
> +	suspend_block(&state->suspend_blocker);
> +
> diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
> new file mode 100755
> index 0000000..f9928cc
> --- /dev/null
> +++ b/include/linux/suspend_blocker.h
> @@ -0,0 +1,64 @@
> +/* include/linux/suspend_blocker.h
> + *
> + * Copyright (C) 2007-2009 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _LINUX_SUSPEND_BLOCKER_H
> +#define _LINUX_SUSPEND_BLOCKER_H
> +
> +#include <linux/list.h>
> +
> +/**
> + * struct suspend_blocker - the basic suspend_blocker structure
> + * @link:	List entry for active or inactive list.
> + * @flags:	Tracks initialized and active state.
> + * @name:	Name used for debugging.
> + *
> + * When a suspend_blocker is active it prevents the system from entering
> + * opportunistic suspend.
> + *
> + * The suspend_blocker structure must be initialized by suspend_blocker_init()
> + */
> +
> +struct suspend_blocker {
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	struct list_head    link;
> +	int                 flags;
> +	const char         *name;
> +#endif
> +};
> +
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
> +void suspend_blocker_destroy(struct suspend_blocker *blocker);
> +void suspend_block(struct suspend_blocker *blocker);
> +void suspend_unblock(struct suspend_blocker *blocker);
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker);
> +bool suspend_is_blocked(void);
> +
> +#else
> +
> +static inline void suspend_blocker_init(struct suspend_blocker *blocker,
> +					const char *name) {}
> +static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
> +static inline void suspend_block(struct suspend_blocker *blocker) {}
> +static inline void suspend_unblock(struct suspend_blocker *blocker) {}
> +static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
> +								{ return 0; }
> +static inline bool suspend_is_blocked(void) { return 0; }
> +
> +#endif
> +
> +#endif
> +
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 5c36ea9..55a06a1 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -130,6 +130,22 @@ config SUSPEND_FREEZER
>  
>  	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
>  
> +config OPPORTUNISTIC_SUSPEND
> +	bool "Suspend blockers"
> +	depends on PM_SLEEP
> +	select RTC_LIB
> +	default n
> +	---help---
> +	  Opportunistic sleep support.  Allows the system to be put into a sleep
> +	  state opportunistically, if it doesn't do any useful work at the
> +	  moment. The PM subsystem is switched into this mode of operation by
> +	  writing "opportunistic" into /sys/power/policy, while writing
> +	  "forced" to this file turns the opportunistic suspend feature off.
> +	  In the "opportunistic" mode suspend blockers are used to determine
> +	  when to suspend the system and the value written to /sys/power/state
> +	  determines the sleep state the system will be put into when there are
> +	  no active suspend blockers.
> +
>  config HIBERNATION_NVS
>  	bool
>  
> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
> index 4319181..ee5276d 100644
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
>  obj-$(CONFIG_PM_SLEEP)		+= console.o
>  obj-$(CONFIG_FREEZER)		+= process.o
>  obj-$(CONFIG_SUSPEND)		+= suspend.o
> +obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= suspend_blocker.o
>  obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
>  obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
>  obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
> diff --git a/kernel/power/main.c b/kernel/power/main.c
> index b58800b..5f0af6c 100644
> --- a/kernel/power/main.c
> +++ b/kernel/power/main.c
> @@ -12,6 +12,7 @@
>  #include <linux/string.h>
>  #include <linux/resume-trace.h>
>  #include <linux/workqueue.h>
> +#include <linux/suspend_blocker.h>
>  
>  #include "power.h"
>  
> @@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
>  unsigned int pm_flags;
>  EXPORT_SYMBOL(pm_flags);
>  
> +struct policy {
> +	const char *name;
> +	bool (*valid_state)(suspend_state_t state);
> +	int (*set_state)(suspend_state_t state);
> +};
> +static struct policy policies[] = {
> +	{
> +		.name		= "forced",
> +		.valid_state	= valid_state,
> +		.set_state	= enter_state,
> +	},
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	{
> +		.name		= "opportunistic",
> +		.valid_state	= request_suspend_valid_state,
> +		.set_state	= request_suspend_state,
> +	},
> +#endif
> +};
> +static int policy;
> +
>  #ifdef CONFIG_PM_SLEEP
>  
>  /* Routines for PM-transition notifications */
> @@ -146,6 +168,12 @@ struct kobject *power_kobj;
>   *
>   *	store() accepts one of those strings, translates it into the 
>   *	proper enumerated value, and initiates a suspend transition.
> + *
> + *	If policy is set to opportunistic, store() does not block until the
> + *	system resumes, and it will try to re-enter the state until another
> + *	state is requested. Suspend blockers are respected and the requested
> + *	state will only be entered when no suspend blockers are active.
> + *	Write "on" to cancel.
>   */
>  static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  			  char *buf)
> @@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  	int i;
>  
>  	for (i = 0; i < PM_SUSPEND_MAX; i++) {
> -		if (pm_states[i] && valid_state(i))
> +		if (pm_states[i] && policies[policy].valid_state(i))
>  			s += sprintf(s,"%s ", pm_states[i]);
>  	}
>  #endif
>  #ifdef CONFIG_HIBERNATION
> -	s += sprintf(s, "%s\n", "disk");
> +	if (!policy)
> +		s += sprintf(s, "%s\n", "disk");
>  #else
>  	if (s != buf)
>  		/* convert the last space to a newline */
> @@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			   const char *buf, size_t n)
>  {
>  #ifdef CONFIG_SUSPEND
> -	suspend_state_t state = PM_SUSPEND_STANDBY;
> +	suspend_state_t state = PM_SUSPEND_ON;
>  	const char * const *s;
>  #endif
>  	char *p;
> @@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  	len = p ? p - buf : n;
>  
>  	/* First, check if we are requested to hibernate */
> -	if (len == 4 && !strncmp(buf, "disk", len)) {
> +	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
>  		error = hibernate();
>    goto Exit;
>  	}
> @@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			break;
>  	}
>  	if (state < PM_SUSPEND_MAX && *s)
> -		error = enter_state(state);
> +		error = policies[policy].set_state(state);
>  #endif
>  
>   Exit:
> @@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  
>  power_attr(state);
>  
> +/**
> + *	policy - set policy for state
> + */
> +
> +static ssize_t policy_show(struct kobject *kobj,
> +			   struct kobj_attribute *attr, char *buf)
> +{
> +	char *s = buf;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		if (i == policy)
> +			s += sprintf(s, "[%s] ", policies[i].name);
> +		else
> +			s += sprintf(s, "%s ", policies[i].name);
> +	}
> +	if (s != buf)
> +		/* convert the last space to a newline */
> +		*(s-1) = '\n';
> +	return (s - buf);
> +}
> +
> +static ssize_t policy_store(struct kobject *kobj,
> +			    struct kobj_attribute *attr,
> +			    const char *buf, size_t n)
> +{
> +	const char *s;
> +	char *p;
> +	int len;
> +	int i;
> +
> +	p = memchr(buf, '\n', n);
> +	len = p ? p - buf : n;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		s = policies[i].name;
> +		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
> +			mutex_lock(&pm_mutex);
> +			policies[policy].set_state(PM_SUSPEND_ON);
> +			policy = i;
> +			mutex_unlock(&pm_mutex);
> +			return n;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +power_attr(policy);
> +
>  #ifdef CONFIG_PM_TRACE
>  int pm_trace_enabled;
>  
> @@ -231,6 +309,7 @@ power_attr(pm_trace);
>  
>  static struct attribute * g[] = {
>  	&state_attr.attr,
> +	&policy_attr.attr,
>  #ifdef CONFIG_PM_TRACE
>  	&pm_trace_attr.attr,
>  #endif
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index 46c5a26..9b468d7 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -236,3 +236,8 @@ static inline void suspend_thaw_processes(void)
>  {
>  }
>  #endif
> +
> +/* kernel/power/suspend_block.c */
> +extern int request_suspend_state(suspend_state_t state);
> +extern bool request_suspend_valid_state(suspend_state_t state);
> +
> diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
> index 56e7dbb..dc42006 100644
> --- a/kernel/power/suspend.c
> +++ b/kernel/power/suspend.c
> @@ -16,10 +16,12 @@
>  #include <linux/cpu.h>
>  #include <linux/syscalls.h>
>  #include <linux/gfp.h>
> +#include <linux/suspend_blocker.h>
>  
>  #include "power.h"
>  
>  const char *const pm_states[PM_SUSPEND_MAX] = {
> +	[PM_SUSPEND_ON]		= "on",
>  	[PM_SUSPEND_STANDBY]	= "standby",
>  	[PM_SUSPEND_MEM]	= "mem",
>  };
> @@ -157,7 +159,7 @@ static int suspend_enter(suspend_state_t state)
>  
>  	error = sysdev_suspend(PMSG_SUSPEND);
>  	if (!error) {
> -		if (!suspend_test(TEST_CORE))
> +		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
>  			error = suspend_ops->enter(state);
>  		sysdev_resume();
>  	}
> diff --git a/kernel/power/suspend_blocker.c b/kernel/power/suspend_blocker.c
> new file mode 100644
> index 0000000..9459361
> --- /dev/null
> +++ b/kernel/power/suspend_blocker.c
> @@ -0,0 +1,269 @@
> +/* kernel/power/suspend_blocker.c
> + *
> + * Copyright (C) 2005-2010 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/suspend.h>
> +#include <linux/suspend_blocker.h>
> +#include "power.h"
> +
> +enum {
> +	DEBUG_EXIT_SUSPEND = 1U << 0,
> +	DEBUG_WAKEUP = 1U << 1,
> +	DEBUG_USER_STATE = 1U << 2,
> +	DEBUG_SUSPEND = 1U << 3,
> +	DEBUG_SUSPEND_BLOCKER = 1U << 4,
> +};
> +static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
> +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
> +
> +#define SB_INITIALIZED            (1U << 8)
> +#define SB_ACTIVE                 (1U << 9)
> +
> +static DEFINE_SPINLOCK(list_lock);
> +static DEFINE_SPINLOCK(state_lock);
> +static LIST_HEAD(inactive_blockers);
> +static LIST_HEAD(active_blockers);
> +static int current_event_num;
> +struct workqueue_struct *suspend_work_queue;
> +struct suspend_blocker main_suspend_blocker;
> +static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
> +static bool enable_suspend_blockers;
> +
> +#define pr_info_time(fmt, args...) \
> +	do { \
> +		struct timespec ts; \
> +		struct rtc_time tm; \
> +		getnstimeofday(&ts); \
> +		rtc_time_to_tm(ts.tv_sec, &tm); \
> +		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
> +			args, \
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
> +	} while (0);
> +
> +static void print_active_blockers_locked(void)
> +{
> +	struct suspend_blocker *blocker;
> +
> +	list_for_each_entry(blocker, &active_blockers, link)
> +		pr_info("active suspend blocker %s\n", blocker->name);
> +}
> +
> +/**
> + * suspend_is_blocked() - Check if suspend should be blocked
> + *
> + * suspend_is_blocked can be used by generic power management code to abort
> + * suspend.
> + *
> + * To preserve backward compatibility suspend_is_blocked returns 0 unless it
> + * is called during suspend initiated from the suspend_block code.
> + */
> +bool suspend_is_blocked(void)
> +{
> +	if (!enable_suspend_blockers)
> +		return 0;
> +	return !list_empty(&active_blockers);
> +}
> +
> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = true;
> +	while (!suspend_is_blocked()) {
> +		entry_event_num = current_event_num;
> +
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: enter suspend\n");
> +
> +		ret = pm_suspend(requested_suspend_state);
> +
> +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> +
> +		if (current_event_num == entry_event_num)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +	}
> +	enable_suspend_blockers = false;
> +}
> +static DECLARE_WORK(suspend_work, suspend_worker);
> +
> +/**
> + * suspend_blocker_init() - Initialize a suspend blocker
> + * @blocker:	The suspend blocker to initialize.
> + * @name:	The name of the suspend blocker to show in debug messages.
> + *
> + * The suspend blocker struct and name must not be freed before calling
> + * suspend_blocker_destroy.
> + */
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
> +{
> +	unsigned long irqflags = 0;
> +
> +	WARN_ON(!name);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_init name=%s\n", name);
> +
> +	blocker->name = name;
> +	blocker->flags = SB_INITIALIZED;
> +	INIT_LIST_HEAD(&blocker->link);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	list_add(&blocker->link, &inactive_blockers);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_init);
> +
> +/**
> + * suspend_blocker_destroy() - Destroy a suspend blocker
> + * @blocker:	The suspend blocker to destroy.
> + */
> +void suspend_blocker_destroy(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags &= ~SB_INITIALIZED;
> +	list_del(&blocker->link);
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(suspend_work_queue, &suspend_work);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_destroy);
> +
> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags |= SB_ACTIVE;
> +	list_del(&blocker->link);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +
> +	list_add(&blocker->link, &active_blockers);
> +
> +	current_event_num++;
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_block);
> +
> +/**
> + * suspend_unblock() - Unblock suspend
> + * @blocker:	The suspend blocker to unblock.
> + *
> + * If no other suspend blockers block suspend, the system will suspend.
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_unblock(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_unblock: %s\n", blocker->name);
> +
> +	list_del(&blocker->link);
> +	list_add(&blocker->link, &inactive_blockers);
> +
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(suspend_work_queue, &suspend_work);
> +	blocker->flags &= ~(SB_ACTIVE);
> +	if (blocker == &main_suspend_blocker) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			print_active_blockers_locked();
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_unblock);
> +
> +/**
> + * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
> + * @blocker:	The suspend blocker to check.
> + *
> + * Returns true if the suspend_blocker is currently active.
> + */
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker)
> +{
> +	WARN_ON(!(blocker->flags & SB_INITIALIZED));
> +
> +	return !!(blocker->flags & SB_ACTIVE);
> +}
> +EXPORT_SYMBOL(suspend_blocker_is_active);
> +
> +bool request_suspend_valid_state(suspend_state_t state)
> +{
> +	return (state == PM_SUSPEND_ON) || valid_state(state);
> +}
> +
> +int request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +
> +	if (!request_suspend_valid_state(state))
> +		return -ENODEV;
> +
> +	spin_lock_irqsave(&state_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_USER_STATE)
> +		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
> +			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> +			     requested_suspend_state, state,
> +			     ktime_to_ns(ktime_get()));
> +
> +	requested_suspend_state = state;
> +	if (state == PM_SUSPEND_ON)
> +		suspend_block(&main_suspend_blocker);
> +	else
> +		suspend_unblock(&main_suspend_blocker);
> +	spin_unlock_irqrestore(&state_lock, irqflags);
> +	return 0;
> +}
> +
> +static int __init suspend_block_init(void)
> +{
> +	suspend_work_queue = create_singlethread_workqueue("suspend");
> +	if (!suspend_work_queue)
> +		return -ENOMEM;
> +
> +	suspend_blocker_init(&main_suspend_blocker, "main");
> +	suspend_block(&main_suspend_blocker);
> +	return 0;
> +}
> +
> +core_initcall(suspend_block_init);

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api.
@ 2010-04-28  6:07     ` Pavel Machek
  0 siblings, 0 replies; 195+ messages in thread
From: Pavel Machek @ 2010-04-28  6:07 UTC (permalink / raw)
  To: Arve Hj??nnev??g
  Cc: Len Brown, linux-doc, linux-kernel, Jesse Barnes, Oleg Nesterov,
	Tejun Heo, Magnus Damm, linux-pm, Wu Fengguang, Andrew Morton

Hi!

> Adds /sys/power/policy that selects the behaviour of /sys/power/state.
> After setting the policy to opportunistic, writes to /sys/power/state
> become non-blocking requests that specify which suspend state to enter
> when no suspend blockers are active. A special state, "on", stops the
> process by activating the "main" suspend blocker.

I really don't like how this changes semantics of 'state'. I guess I'd
prefer leaving state as is -- forced transition to hibernation while
system is set to opportunistically suspend seems sane -- and adding
something like

/sys/power/autosleep

with 'off' or 'suspend' values?


> 
> Signed-off-by: Arve Hj??nnev??g <arve@android.com>
> ---
>  Documentation/power/opportunistic-suspend.txt |  114 +++++++++++
>  include/linux/suspend_blocker.h               |   64 ++++++
>  kernel/power/Kconfig                          |   16 ++
>  kernel/power/Makefile                         |    1 +
>  kernel/power/main.c                           |   89 ++++++++-
>  kernel/power/power.h                          |    5 +
>  kernel/power/suspend.c                        |    4 +-
>  kernel/power/suspend_blocker.c                |  269 +++++++++++++++++++++++++
>  8 files changed, 556 insertions(+), 6 deletions(-)
>  create mode 100644 Documentation/power/opportunistic-suspend.txt
>  create mode 100755 include/linux/suspend_blocker.h
>  create mode 100644 kernel/power/suspend_blocker.c
> 
> diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
> new file mode 100644
> index 0000000..1a29d10
> --- /dev/null
> +++ b/Documentation/power/opportunistic-suspend.txt
> @@ -0,0 +1,114 @@
> +Opportunistic Suspend
> +=====================
> +
> +Opportunistic suspend is a feature allowing the system to be suspended (ie. put
> +into one of the available sleep states) automatically whenever it is regarded
> +as idle.  The suspend blockers framework described below is used to determine
> +when that happens.
> +
> +The /sys/power/policy sysfs attribute is used to switch the system between the
> +opportunistic and "forced" suspend behavior, where in the latter case the
> +system is only suspended if a specific value, corresponding to one of the
> +available system sleep states, is written into /sys/power/state.  However, in
> +the former, opportunistic, case the system is put into the sleep state
> +corresponding to the value written to /sys/power/state whenever there are no
> +active suspend blockers. The default policy is "forced". Also, suspend blockers
> +do not affect sleep states entered from idle.
> +
> +When the policy is "opportunisic", there is a special value, "on", that can be
> +written to /sys/power/state. This will block the automatic sleep request, as if
> +a suspend blocker was used by a device driver. This way the opportunistic
> +suspend may be blocked by user space whithout switching back to the "forced"
> +mode.
> +
> +A suspend blocker is an object used to inform the PM subsystem when the system
> +can or cannot be suspended in the "opportunistic" mode (the "forced" mode
> +ignores suspend blockers).  To use it, a device driver creates a struct
> +suspend_blocker that must be initialized with suspend_blocker_init(). Before
> +freeing the suspend_blocker structure or its name, suspend_blocker_destroy()
> +must be called on it.
> +
> +A suspend blocker is activated using suspend_block(), which prevents the PM
> +subsystem from putting the system into the requested sleep state in the
> +"opportunistic" mode until the suspend blocker is deactivated with
> +suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
> +the system will not suspend as long as at least one of them is active.
> +
> +If opportunistic suspend is already in progress when suspend_block() is called,
> +it will abort the suspend, unless suspend_ops->enter has already been
> +executed. If suspend is aborted this way, the system is usually not fully
> +operational at that point. The suspend callbacks of some drivers may still be
> +running and it usually takes time to restore the system to the fully operational
> +state.
> +
> +For example, in cell phones or other embedded systems, where powering the screen
> +is a significant drain on the battery, suspend blockers can be used to allow
> +user-space to decide whether a keystroke received while the system is suspended
> +should cause the screen to be turned back on or allow the system to go back into
> +suspend. Use set_irq_wake or a platform specific api to make sure the keypad
> +interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
> +events can look like this:
> +
> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
> +- The keypad-scan code detects a key change and reports it to the input-event
> +  driver.
> +- The input-event driver sees the key change, enqueues an event, and calls
> +  suspend_block on the input-event-queue suspend_blocker.
> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
> +  on the keypad-scan suspend_blocker.
> +- The user-space input-event thread returns from select/poll, calls
> +  suspend_block on the process-input-events suspend_blocker and then calls read
> +  on the input-event device.
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
> +- The user-space input-event thread returns from read. If it determines that
> +  the key should leave the screen off, it calls suspend_unblock on the
> +  process_input_events suspend_blocker and then calls select or poll. The
> +  system will automatically suspend again, since now no suspend blockers are
> +  active.
> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++
> +
> +
> +Driver API
> +==========
> +
> +A driver can use the suspend block api by adding a suspend_blocker variable to
> +its state and calling suspend_blocker_init. For instance:
> +struct state {
> +	struct suspend_blocker suspend_blocker;
> +}
> +
> +init() {
> +	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
> +}
> +
> +Before freeing the memory, suspend_blocker_destroy must be called:
> +
> +uninit() {
> +	suspend_blocker_destroy(&state->suspend_blocker);
> +}
> +
> +When the driver determines that it needs to run (usually in an interrupt
> +handler) it calls suspend_block:
> +	suspend_block(&state->suspend_blocker);
> +
> +When it no longer needs to run it calls suspend_unblock:
> +	suspend_unblock(&state->suspend_blocker);
> +
> +Calling suspend_block when the suspend blocker is active or suspend_unblock when
> +it is not active has no effect (i.e., these functions don't nest). This allows
> +drivers to update their state and call suspend suspend_block or suspend_unblock
> +based on the result.
> +For instance:
> +
> +if (list_empty(&state->pending_work))
> +	suspend_unblock(&state->suspend_blocker);
> +else
> +	suspend_block(&state->suspend_blocker);
> +
> diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
> new file mode 100755
> index 0000000..f9928cc
> --- /dev/null
> +++ b/include/linux/suspend_blocker.h
> @@ -0,0 +1,64 @@
> +/* include/linux/suspend_blocker.h
> + *
> + * Copyright (C) 2007-2009 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _LINUX_SUSPEND_BLOCKER_H
> +#define _LINUX_SUSPEND_BLOCKER_H
> +
> +#include <linux/list.h>
> +
> +/**
> + * struct suspend_blocker - the basic suspend_blocker structure
> + * @link:	List entry for active or inactive list.
> + * @flags:	Tracks initialized and active state.
> + * @name:	Name used for debugging.
> + *
> + * When a suspend_blocker is active it prevents the system from entering
> + * opportunistic suspend.
> + *
> + * The suspend_blocker structure must be initialized by suspend_blocker_init()
> + */
> +
> +struct suspend_blocker {
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	struct list_head    link;
> +	int                 flags;
> +	const char         *name;
> +#endif
> +};
> +
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
> +void suspend_blocker_destroy(struct suspend_blocker *blocker);
> +void suspend_block(struct suspend_blocker *blocker);
> +void suspend_unblock(struct suspend_blocker *blocker);
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker);
> +bool suspend_is_blocked(void);
> +
> +#else
> +
> +static inline void suspend_blocker_init(struct suspend_blocker *blocker,
> +					const char *name) {}
> +static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
> +static inline void suspend_block(struct suspend_blocker *blocker) {}
> +static inline void suspend_unblock(struct suspend_blocker *blocker) {}
> +static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
> +								{ return 0; }
> +static inline bool suspend_is_blocked(void) { return 0; }
> +
> +#endif
> +
> +#endif
> +
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 5c36ea9..55a06a1 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -130,6 +130,22 @@ config SUSPEND_FREEZER
>  
>  	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
>  
> +config OPPORTUNISTIC_SUSPEND
> +	bool "Suspend blockers"
> +	depends on PM_SLEEP
> +	select RTC_LIB
> +	default n
> +	---help---
> +	  Opportunistic sleep support.  Allows the system to be put into a sleep
> +	  state opportunistically, if it doesn't do any useful work at the
> +	  moment. The PM subsystem is switched into this mode of operation by
> +	  writing "opportunistic" into /sys/power/policy, while writing
> +	  "forced" to this file turns the opportunistic suspend feature off.
> +	  In the "opportunistic" mode suspend blockers are used to determine
> +	  when to suspend the system and the value written to /sys/power/state
> +	  determines the sleep state the system will be put into when there are
> +	  no active suspend blockers.
> +
>  config HIBERNATION_NVS
>  	bool
>  
> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
> index 4319181..ee5276d 100644
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
>  obj-$(CONFIG_PM_SLEEP)		+= console.o
>  obj-$(CONFIG_FREEZER)		+= process.o
>  obj-$(CONFIG_SUSPEND)		+= suspend.o
> +obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= suspend_blocker.o
>  obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
>  obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
>  obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
> diff --git a/kernel/power/main.c b/kernel/power/main.c
> index b58800b..5f0af6c 100644
> --- a/kernel/power/main.c
> +++ b/kernel/power/main.c
> @@ -12,6 +12,7 @@
>  #include <linux/string.h>
>  #include <linux/resume-trace.h>
>  #include <linux/workqueue.h>
> +#include <linux/suspend_blocker.h>
>  
>  #include "power.h"
>  
> @@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
>  unsigned int pm_flags;
>  EXPORT_SYMBOL(pm_flags);
>  
> +struct policy {
> +	const char *name;
> +	bool (*valid_state)(suspend_state_t state);
> +	int (*set_state)(suspend_state_t state);
> +};
> +static struct policy policies[] = {
> +	{
> +		.name		= "forced",
> +		.valid_state	= valid_state,
> +		.set_state	= enter_state,
> +	},
> +#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
> +	{
> +		.name		= "opportunistic",
> +		.valid_state	= request_suspend_valid_state,
> +		.set_state	= request_suspend_state,
> +	},
> +#endif
> +};
> +static int policy;
> +
>  #ifdef CONFIG_PM_SLEEP
>  
>  /* Routines for PM-transition notifications */
> @@ -146,6 +168,12 @@ struct kobject *power_kobj;
>   *
>   *	store() accepts one of those strings, translates it into the 
>   *	proper enumerated value, and initiates a suspend transition.
> + *
> + *	If policy is set to opportunistic, store() does not block until the
> + *	system resumes, and it will try to re-enter the state until another
> + *	state is requested. Suspend blockers are respected and the requested
> + *	state will only be entered when no suspend blockers are active.
> + *	Write "on" to cancel.
>   */
>  static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  			  char *buf)
> @@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
>  	int i;
>  
>  	for (i = 0; i < PM_SUSPEND_MAX; i++) {
> -		if (pm_states[i] && valid_state(i))
> +		if (pm_states[i] && policies[policy].valid_state(i))
>  			s += sprintf(s,"%s ", pm_states[i]);
>  	}
>  #endif
>  #ifdef CONFIG_HIBERNATION
> -	s += sprintf(s, "%s\n", "disk");
> +	if (!policy)
> +		s += sprintf(s, "%s\n", "disk");
>  #else
>  	if (s != buf)
>  		/* convert the last space to a newline */
> @@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			   const char *buf, size_t n)
>  {
>  #ifdef CONFIG_SUSPEND
> -	suspend_state_t state = PM_SUSPEND_STANDBY;
> +	suspend_state_t state = PM_SUSPEND_ON;
>  	const char * const *s;
>  #endif
>  	char *p;
> @@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  	len = p ? p - buf : n;
>  
>  	/* First, check if we are requested to hibernate */
> -	if (len == 4 && !strncmp(buf, "disk", len)) {
> +	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
>  		error = hibernate();
>    goto Exit;
>  	}
> @@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  			break;
>  	}
>  	if (state < PM_SUSPEND_MAX && *s)
> -		error = enter_state(state);
> +		error = policies[policy].set_state(state);
>  #endif
>  
>   Exit:
> @@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
>  
>  power_attr(state);
>  
> +/**
> + *	policy - set policy for state
> + */
> +
> +static ssize_t policy_show(struct kobject *kobj,
> +			   struct kobj_attribute *attr, char *buf)
> +{
> +	char *s = buf;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		if (i == policy)
> +			s += sprintf(s, "[%s] ", policies[i].name);
> +		else
> +			s += sprintf(s, "%s ", policies[i].name);
> +	}
> +	if (s != buf)
> +		/* convert the last space to a newline */
> +		*(s-1) = '\n';
> +	return (s - buf);
> +}
> +
> +static ssize_t policy_store(struct kobject *kobj,
> +			    struct kobj_attribute *attr,
> +			    const char *buf, size_t n)
> +{
> +	const char *s;
> +	char *p;
> +	int len;
> +	int i;
> +
> +	p = memchr(buf, '\n', n);
> +	len = p ? p - buf : n;
> +
> +	for (i = 0; i < ARRAY_SIZE(policies); i++) {
> +		s = policies[i].name;
> +		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
> +			mutex_lock(&pm_mutex);
> +			policies[policy].set_state(PM_SUSPEND_ON);
> +			policy = i;
> +			mutex_unlock(&pm_mutex);
> +			return n;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +power_attr(policy);
> +
>  #ifdef CONFIG_PM_TRACE
>  int pm_trace_enabled;
>  
> @@ -231,6 +309,7 @@ power_attr(pm_trace);
>  
>  static struct attribute * g[] = {
>  	&state_attr.attr,
> +	&policy_attr.attr,
>  #ifdef CONFIG_PM_TRACE
>  	&pm_trace_attr.attr,
>  #endif
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index 46c5a26..9b468d7 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -236,3 +236,8 @@ static inline void suspend_thaw_processes(void)
>  {
>  }
>  #endif
> +
> +/* kernel/power/suspend_block.c */
> +extern int request_suspend_state(suspend_state_t state);
> +extern bool request_suspend_valid_state(suspend_state_t state);
> +
> diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
> index 56e7dbb..dc42006 100644
> --- a/kernel/power/suspend.c
> +++ b/kernel/power/suspend.c
> @@ -16,10 +16,12 @@
>  #include <linux/cpu.h>
>  #include <linux/syscalls.h>
>  #include <linux/gfp.h>
> +#include <linux/suspend_blocker.h>
>  
>  #include "power.h"
>  
>  const char *const pm_states[PM_SUSPEND_MAX] = {
> +	[PM_SUSPEND_ON]		= "on",
>  	[PM_SUSPEND_STANDBY]	= "standby",
>  	[PM_SUSPEND_MEM]	= "mem",
>  };
> @@ -157,7 +159,7 @@ static int suspend_enter(suspend_state_t state)
>  
>  	error = sysdev_suspend(PMSG_SUSPEND);
>  	if (!error) {
> -		if (!suspend_test(TEST_CORE))
> +		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
>  			error = suspend_ops->enter(state);
>  		sysdev_resume();
>  	}
> diff --git a/kernel/power/suspend_blocker.c b/kernel/power/suspend_blocker.c
> new file mode 100644
> index 0000000..9459361
> --- /dev/null
> +++ b/kernel/power/suspend_blocker.c
> @@ -0,0 +1,269 @@
> +/* kernel/power/suspend_blocker.c
> + *
> + * Copyright (C) 2005-2010 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/suspend.h>
> +#include <linux/suspend_blocker.h>
> +#include "power.h"
> +
> +enum {
> +	DEBUG_EXIT_SUSPEND = 1U << 0,
> +	DEBUG_WAKEUP = 1U << 1,
> +	DEBUG_USER_STATE = 1U << 2,
> +	DEBUG_SUSPEND = 1U << 3,
> +	DEBUG_SUSPEND_BLOCKER = 1U << 4,
> +};
> +static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
> +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
> +
> +#define SB_INITIALIZED            (1U << 8)
> +#define SB_ACTIVE                 (1U << 9)
> +
> +static DEFINE_SPINLOCK(list_lock);
> +static DEFINE_SPINLOCK(state_lock);
> +static LIST_HEAD(inactive_blockers);
> +static LIST_HEAD(active_blockers);
> +static int current_event_num;
> +struct workqueue_struct *suspend_work_queue;
> +struct suspend_blocker main_suspend_blocker;
> +static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
> +static bool enable_suspend_blockers;
> +
> +#define pr_info_time(fmt, args...) \
> +	do { \
> +		struct timespec ts; \
> +		struct rtc_time tm; \
> +		getnstimeofday(&ts); \
> +		rtc_time_to_tm(ts.tv_sec, &tm); \
> +		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
> +			args, \
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
> +	} while (0);
> +
> +static void print_active_blockers_locked(void)
> +{
> +	struct suspend_blocker *blocker;
> +
> +	list_for_each_entry(blocker, &active_blockers, link)
> +		pr_info("active suspend blocker %s\n", blocker->name);
> +}
> +
> +/**
> + * suspend_is_blocked() - Check if suspend should be blocked
> + *
> + * suspend_is_blocked can be used by generic power management code to abort
> + * suspend.
> + *
> + * To preserve backward compatibility suspend_is_blocked returns 0 unless it
> + * is called during suspend initiated from the suspend_block code.
> + */
> +bool suspend_is_blocked(void)
> +{
> +	if (!enable_suspend_blockers)
> +		return 0;
> +	return !list_empty(&active_blockers);
> +}
> +
> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	enable_suspend_blockers = true;
> +	while (!suspend_is_blocked()) {
> +		entry_event_num = current_event_num;
> +
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: enter suspend\n");
> +
> +		ret = pm_suspend(requested_suspend_state);
> +
> +		if (debug_mask & DEBUG_EXIT_SUSPEND)
> +			pr_info_time("suspend: exit suspend, ret = %d ", ret);
> +
> +		if (current_event_num == entry_event_num)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +	}
> +	enable_suspend_blockers = false;
> +}
> +static DECLARE_WORK(suspend_work, suspend_worker);
> +
> +/**
> + * suspend_blocker_init() - Initialize a suspend blocker
> + * @blocker:	The suspend blocker to initialize.
> + * @name:	The name of the suspend blocker to show in debug messages.
> + *
> + * The suspend blocker struct and name must not be freed before calling
> + * suspend_blocker_destroy.
> + */
> +void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
> +{
> +	unsigned long irqflags = 0;
> +
> +	WARN_ON(!name);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_init name=%s\n", name);
> +
> +	blocker->name = name;
> +	blocker->flags = SB_INITIALIZED;
> +	INIT_LIST_HEAD(&blocker->link);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	list_add(&blocker->link, &inactive_blockers);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_init);
> +
> +/**
> + * suspend_blocker_destroy() - Destroy a suspend blocker
> + * @blocker:	The suspend blocker to destroy.
> + */
> +void suspend_blocker_destroy(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags &= ~SB_INITIALIZED;
> +	list_del(&blocker->link);
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(suspend_work_queue, &suspend_work);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_blocker_destroy);
> +
> +/**
> + * suspend_block() - Block suspend
> + * @blocker:	The suspend blocker to use
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_block(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	blocker->flags |= SB_ACTIVE;
> +	list_del(&blocker->link);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_block: %s\n", blocker->name);
> +
> +	list_add(&blocker->link, &active_blockers);
> +
> +	current_event_num++;
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_block);
> +
> +/**
> + * suspend_unblock() - Unblock suspend
> + * @blocker:	The suspend blocker to unblock.
> + *
> + * If no other suspend blockers block suspend, the system will suspend.
> + *
> + * It is safe to call this function from interrupt context.
> + */
> +void suspend_unblock(struct suspend_blocker *blocker)
> +{
> +	unsigned long irqflags;
> +
> +	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
> +		return;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
> +		pr_info("suspend_unblock: %s\n", blocker->name);
> +
> +	list_del(&blocker->link);
> +	list_add(&blocker->link, &inactive_blockers);
> +
> +	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
> +		queue_work(suspend_work_queue, &suspend_work);
> +	blocker->flags &= ~(SB_ACTIVE);
> +	if (blocker == &main_suspend_blocker) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			print_active_blockers_locked();
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(suspend_unblock);
> +
> +/**
> + * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
> + * @blocker:	The suspend blocker to check.
> + *
> + * Returns true if the suspend_blocker is currently active.
> + */
> +bool suspend_blocker_is_active(struct suspend_blocker *blocker)
> +{
> +	WARN_ON(!(blocker->flags & SB_INITIALIZED));
> +
> +	return !!(blocker->flags & SB_ACTIVE);
> +}
> +EXPORT_SYMBOL(suspend_blocker_is_active);
> +
> +bool request_suspend_valid_state(suspend_state_t state)
> +{
> +	return (state == PM_SUSPEND_ON) || valid_state(state);
> +}
> +
> +int request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +
> +	if (!request_suspend_valid_state(state))
> +		return -ENODEV;
> +
> +	spin_lock_irqsave(&state_lock, irqflags);
> +
> +	if (debug_mask & DEBUG_USER_STATE)
> +		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
> +			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
> +			     requested_suspend_state, state,
> +			     ktime_to_ns(ktime_get()));
> +
> +	requested_suspend_state = state;
> +	if (state == PM_SUSPEND_ON)
> +		suspend_block(&main_suspend_blocker);
> +	else
> +		suspend_unblock(&main_suspend_blocker);
> +	spin_unlock_irqrestore(&state_lock, irqflags);
> +	return 0;
> +}
> +
> +static int __init suspend_block_init(void)
> +{
> +	suspend_work_queue = create_singlethread_workqueue("suspend");
> +	if (!suspend_work_queue)
> +		return -ENOMEM;
> +
> +	suspend_blocker_init(&main_suspend_blocker, "main");
> +	suspend_block(&main_suspend_blocker);
> +	return 0;
> +}
> +
> +core_initcall(suspend_block_init);

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 195+ messages in thread

* [PATCH 1/8] PM: Add suspend block api.
  2010-04-28  4:31 [PATCH 0/9] Suspend block api (version 5) Arve Hjønnevåg
  2010-04-28  4:31 ` [PATCH 1/8] PM: Add suspend block api Arve Hjønnevåg
@ 2010-04-28  4:31 ` Arve Hjønnevåg
  2010-04-28  6:07     ` Pavel Machek
                     ` (6 more replies)
  1 sibling, 7 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-04-28  4:31 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: Rafael J. Wysocki, Alan Stern, Tejun Heo, Oleg Nesterov,
	Arve Hjønnevåg, Len Brown, Pavel Machek, Randy Dunlap,
	Jesse Barnes, Magnus Damm, Nigel Cunningham, Cornelia Huck,
	Ming Lei, Wu Fengguang, Andrew Morton, Maxim Levitsky, linux-doc

Adds /sys/power/policy that selects the behaviour of /sys/power/state.
After setting the policy to opportunistic, writes to /sys/power/state
become non-blocking requests that specify which suspend state to enter
when no suspend blockers are active. A special state, "on", stops the
process by activating the "main" suspend blocker.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/opportunistic-suspend.txt |  114 +++++++++++
 include/linux/suspend_blocker.h               |   64 ++++++
 kernel/power/Kconfig                          |   16 ++
 kernel/power/Makefile                         |    1 +
 kernel/power/main.c                           |   89 ++++++++-
 kernel/power/power.h                          |    5 +
 kernel/power/suspend.c                        |    4 +-
 kernel/power/suspend_blocker.c                |  269 +++++++++++++++++++++++++
 8 files changed, 556 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/power/opportunistic-suspend.txt
 create mode 100755 include/linux/suspend_blocker.h
 create mode 100644 kernel/power/suspend_blocker.c

diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
new file mode 100644
index 0000000..1a29d10
--- /dev/null
+++ b/Documentation/power/opportunistic-suspend.txt
@@ -0,0 +1,114 @@
+Opportunistic Suspend
+=====================
+
+Opportunistic suspend is a feature allowing the system to be suspended (ie. put
+into one of the available sleep states) automatically whenever it is regarded
+as idle.  The suspend blockers framework described below is used to determine
+when that happens.
+
+The /sys/power/policy sysfs attribute is used to switch the system between the
+opportunistic and "forced" suspend behavior, where in the latter case the
+system is only suspended if a specific value, corresponding to one of the
+available system sleep states, is written into /sys/power/state.  However, in
+the former, opportunistic, case the system is put into the sleep state
+corresponding to the value written to /sys/power/state whenever there are no
+active suspend blockers. The default policy is "forced". Also, suspend blockers
+do not affect sleep states entered from idle.
+
+When the policy is "opportunisic", there is a special value, "on", that can be
+written to /sys/power/state. This will block the automatic sleep request, as if
+a suspend blocker was used by a device driver. This way the opportunistic
+suspend may be blocked by user space whithout switching back to the "forced"
+mode.
+
+A suspend blocker is an object used to inform the PM subsystem when the system
+can or cannot be suspended in the "opportunistic" mode (the "forced" mode
+ignores suspend blockers).  To use it, a device driver creates a struct
+suspend_blocker that must be initialized with suspend_blocker_init(). Before
+freeing the suspend_blocker structure or its name, suspend_blocker_destroy()
+must be called on it.
+
+A suspend blocker is activated using suspend_block(), which prevents the PM
+subsystem from putting the system into the requested sleep state in the
+"opportunistic" mode until the suspend blocker is deactivated with
+suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
+the system will not suspend as long as at least one of them is active.
+
+If opportunistic suspend is already in progress when suspend_block() is called,
+it will abort the suspend, unless suspend_ops->enter has already been
+executed. If suspend is aborted this way, the system is usually not fully
+operational at that point. The suspend callbacks of some drivers may still be
+running and it usually takes time to restore the system to the fully operational
+state.
+
+For example, in cell phones or other embedded systems, where powering the screen
+is a significant drain on the battery, suspend blockers can be used to allow
+user-space to decide whether a keystroke received while the system is suspended
+should cause the screen to be turned back on or allow the system to go back into
+suspend. Use set_irq_wake or a platform specific api to make sure the keypad
+interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
+events can look like this:
+
+- The Keypad driver gets an interrupt. It then calls suspend_block on the
+  keypad-scan suspend_blocker and starts scanning the keypad matrix.
+- The keypad-scan code detects a key change and reports it to the input-event
+  driver.
+- The input-event driver sees the key change, enqueues an event, and calls
+  suspend_block on the input-event-queue suspend_blocker.
+- The keypad-scan code detects that no keys are held and calls suspend_unblock
+  on the keypad-scan suspend_blocker.
+- The user-space input-event thread returns from select/poll, calls
+  suspend_block on the process-input-events suspend_blocker and then calls read
+  on the input-event device.
+- The input-event driver dequeues the key-event and, since the queue is now
+  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
+- The user-space input-event thread returns from read. If it determines that
+  the key should leave the screen off, it calls suspend_unblock on the
+  process_input_events suspend_blocker and then calls select or poll. The
+  system will automatically suspend again, since now no suspend blockers are
+  active.
+
+                 Key pressed   Key released
+                     |             |
+keypad-scan          ++++++++++++++++++
+input-event-queue        +++          +++
+process-input-events       +++          +++
+
+
+Driver API
+==========
+
+A driver can use the suspend block api by adding a suspend_blocker variable to
+its state and calling suspend_blocker_init. For instance:
+struct state {
+	struct suspend_blocker suspend_blocker;
+}
+
+init() {
+	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
+}
+
+Before freeing the memory, suspend_blocker_destroy must be called:
+
+uninit() {
+	suspend_blocker_destroy(&state->suspend_blocker);
+}
+
+When the driver determines that it needs to run (usually in an interrupt
+handler) it calls suspend_block:
+	suspend_block(&state->suspend_blocker);
+
+When it no longer needs to run it calls suspend_unblock:
+	suspend_unblock(&state->suspend_blocker);
+
+Calling suspend_block when the suspend blocker is active or suspend_unblock when
+it is not active has no effect (i.e., these functions don't nest). This allows
+drivers to update their state and call suspend suspend_block or suspend_unblock
+based on the result.
+For instance:
+
+if (list_empty(&state->pending_work))
+	suspend_unblock(&state->suspend_blocker);
+else
+	suspend_block(&state->suspend_blocker);
+
diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
new file mode 100755
index 0000000..f9928cc
--- /dev/null
+++ b/include/linux/suspend_blocker.h
@@ -0,0 +1,64 @@
+/* include/linux/suspend_blocker.h
+ *
+ * Copyright (C) 2007-2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SUSPEND_BLOCKER_H
+#define _LINUX_SUSPEND_BLOCKER_H
+
+#include <linux/list.h>
+
+/**
+ * struct suspend_blocker - the basic suspend_blocker structure
+ * @link:	List entry for active or inactive list.
+ * @flags:	Tracks initialized and active state.
+ * @name:	Name used for debugging.
+ *
+ * When a suspend_blocker is active it prevents the system from entering
+ * opportunistic suspend.
+ *
+ * The suspend_blocker structure must be initialized by suspend_blocker_init()
+ */
+
+struct suspend_blocker {
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	struct list_head    link;
+	int                 flags;
+	const char         *name;
+#endif
+};
+
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
+void suspend_blocker_destroy(struct suspend_blocker *blocker);
+void suspend_block(struct suspend_blocker *blocker);
+void suspend_unblock(struct suspend_blocker *blocker);
+bool suspend_blocker_is_active(struct suspend_blocker *blocker);
+bool suspend_is_blocked(void);
+
+#else
+
+static inline void suspend_blocker_init(struct suspend_blocker *blocker,
+					const char *name) {}
+static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
+static inline void suspend_block(struct suspend_blocker *blocker) {}
+static inline void suspend_unblock(struct suspend_blocker *blocker) {}
+static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
+								{ return 0; }
+static inline bool suspend_is_blocked(void) { return 0; }
+
+#endif
+
+#endif
+
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 5c36ea9..55a06a1 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -130,6 +130,22 @@ config SUSPEND_FREEZER
 
 	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
 
+config OPPORTUNISTIC_SUSPEND
+	bool "Suspend blockers"
+	depends on PM_SLEEP
+	select RTC_LIB
+	default n
+	---help---
+	  Opportunistic sleep support.  Allows the system to be put into a sleep
+	  state opportunistically, if it doesn't do any useful work at the
+	  moment. The PM subsystem is switched into this mode of operation by
+	  writing "opportunistic" into /sys/power/policy, while writing
+	  "forced" to this file turns the opportunistic suspend feature off.
+	  In the "opportunistic" mode suspend blockers are used to determine
+	  when to suspend the system and the value written to /sys/power/state
+	  determines the sleep state the system will be put into when there are
+	  no active suspend blockers.
+
 config HIBERNATION_NVS
 	bool
 
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 4319181..ee5276d 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
+obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= suspend_blocker.o
 obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
 obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
 obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
diff --git a/kernel/power/main.c b/kernel/power/main.c
index b58800b..5f0af6c 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -12,6 +12,7 @@
 #include <linux/string.h>
 #include <linux/resume-trace.h>
 #include <linux/workqueue.h>
+#include <linux/suspend_blocker.h>
 
 #include "power.h"
 
@@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
 unsigned int pm_flags;
 EXPORT_SYMBOL(pm_flags);
 
+struct policy {
+	const char *name;
+	bool (*valid_state)(suspend_state_t state);
+	int (*set_state)(suspend_state_t state);
+};
+static struct policy policies[] = {
+	{
+		.name		= "forced",
+		.valid_state	= valid_state,
+		.set_state	= enter_state,
+	},
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	{
+		.name		= "opportunistic",
+		.valid_state	= request_suspend_valid_state,
+		.set_state	= request_suspend_state,
+	},
+#endif
+};
+static int policy;
+
 #ifdef CONFIG_PM_SLEEP
 
 /* Routines for PM-transition notifications */
@@ -146,6 +168,12 @@ struct kobject *power_kobj;
  *
  *	store() accepts one of those strings, translates it into the 
  *	proper enumerated value, and initiates a suspend transition.
+ *
+ *	If policy is set to opportunistic, store() does not block until the
+ *	system resumes, and it will try to re-enter the state until another
+ *	state is requested. Suspend blockers are respected and the requested
+ *	state will only be entered when no suspend blockers are active.
+ *	Write "on" to cancel.
  */
 static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 			  char *buf)
@@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 	int i;
 
 	for (i = 0; i < PM_SUSPEND_MAX; i++) {
-		if (pm_states[i] && valid_state(i))
+		if (pm_states[i] && policies[policy].valid_state(i))
 			s += sprintf(s,"%s ", pm_states[i]);
 	}
 #endif
 #ifdef CONFIG_HIBERNATION
-	s += sprintf(s, "%s\n", "disk");
+	if (!policy)
+		s += sprintf(s, "%s\n", "disk");
 #else
 	if (s != buf)
 		/* convert the last space to a newline */
@@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			   const char *buf, size_t n)
 {
 #ifdef CONFIG_SUSPEND
-	suspend_state_t state = PM_SUSPEND_STANDBY;
+	suspend_state_t state = PM_SUSPEND_ON;
 	const char * const *s;
 #endif
 	char *p;
@@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 	len = p ? p - buf : n;
 
 	/* First, check if we are requested to hibernate */
-	if (len == 4 && !strncmp(buf, "disk", len)) {
+	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
 		error = hibernate();
   goto Exit;
 	}
@@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			break;
 	}
 	if (state < PM_SUSPEND_MAX && *s)
-		error = enter_state(state);
+		error = policies[policy].set_state(state);
 #endif
 
  Exit:
@@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 
 power_attr(state);
 
+/**
+ *	policy - set policy for state
+ */
+
+static ssize_t policy_show(struct kobject *kobj,
+			   struct kobj_attribute *attr, char *buf)
+{
+	char *s = buf;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		if (i == policy)
+			s += sprintf(s, "[%s] ", policies[i].name);
+		else
+			s += sprintf(s, "%s ", policies[i].name);
+	}
+	if (s != buf)
+		/* convert the last space to a newline */
+		*(s-1) = '\n';
+	return (s - buf);
+}
+
+static ssize_t policy_store(struct kobject *kobj,
+			    struct kobj_attribute *attr,
+			    const char *buf, size_t n)
+{
+	const char *s;
+	char *p;
+	int len;
+	int i;
+
+	p = memchr(buf, '\n', n);
+	len = p ? p - buf : n;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		s = policies[i].name;
+		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
+			mutex_lock(&pm_mutex);
+			policies[policy].set_state(PM_SUSPEND_ON);
+			policy = i;
+			mutex_unlock(&pm_mutex);
+			return n;
+		}
+	}
+	return -EINVAL;
+}
+
+power_attr(policy);
+
 #ifdef CONFIG_PM_TRACE
 int pm_trace_enabled;
 
@@ -231,6 +309,7 @@ power_attr(pm_trace);
 
 static struct attribute * g[] = {
 	&state_attr.attr,
+	&policy_attr.attr,
 #ifdef CONFIG_PM_TRACE
 	&pm_trace_attr.attr,
 #endif
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 46c5a26..9b468d7 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -236,3 +236,8 @@ static inline void suspend_thaw_processes(void)
 {
 }
 #endif
+
+/* kernel/power/suspend_block.c */
+extern int request_suspend_state(suspend_state_t state);
+extern bool request_suspend_valid_state(suspend_state_t state);
+
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 56e7dbb..dc42006 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -16,10 +16,12 @@
 #include <linux/cpu.h>
 #include <linux/syscalls.h>
 #include <linux/gfp.h>
+#include <linux/suspend_blocker.h>
 
 #include "power.h"
 
 const char *const pm_states[PM_SUSPEND_MAX] = {
+	[PM_SUSPEND_ON]		= "on",
 	[PM_SUSPEND_STANDBY]	= "standby",
 	[PM_SUSPEND_MEM]	= "mem",
 };
@@ -157,7 +159,7 @@ static int suspend_enter(suspend_state_t state)
 
 	error = sysdev_suspend(PMSG_SUSPEND);
 	if (!error) {
-		if (!suspend_test(TEST_CORE))
+		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
 			error = suspend_ops->enter(state);
 		sysdev_resume();
 	}
diff --git a/kernel/power/suspend_blocker.c b/kernel/power/suspend_blocker.c
new file mode 100644
index 0000000..9459361
--- /dev/null
+++ b/kernel/power/suspend_blocker.c
@@ -0,0 +1,269 @@
+/* kernel/power/suspend_blocker.c
+ *
+ * Copyright (C) 2005-2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/suspend.h>
+#include <linux/suspend_blocker.h>
+#include "power.h"
+
+enum {
+	DEBUG_EXIT_SUSPEND = 1U << 0,
+	DEBUG_WAKEUP = 1U << 1,
+	DEBUG_USER_STATE = 1U << 2,
+	DEBUG_SUSPEND = 1U << 3,
+	DEBUG_SUSPEND_BLOCKER = 1U << 4,
+};
+static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SB_INITIALIZED            (1U << 8)
+#define SB_ACTIVE                 (1U << 9)
+
+static DEFINE_SPINLOCK(list_lock);
+static DEFINE_SPINLOCK(state_lock);
+static LIST_HEAD(inactive_blockers);
+static LIST_HEAD(active_blockers);
+static int current_event_num;
+struct workqueue_struct *suspend_work_queue;
+struct suspend_blocker main_suspend_blocker;
+static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
+static bool enable_suspend_blockers;
+
+#define pr_info_time(fmt, args...) \
+	do { \
+		struct timespec ts; \
+		struct rtc_time tm; \
+		getnstimeofday(&ts); \
+		rtc_time_to_tm(ts.tv_sec, &tm); \
+		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
+			args, \
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
+			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
+	} while (0);
+
+static void print_active_blockers_locked(void)
+{
+	struct suspend_blocker *blocker;
+
+	list_for_each_entry(blocker, &active_blockers, link)
+		pr_info("active suspend blocker %s\n", blocker->name);
+}
+
+/**
+ * suspend_is_blocked() - Check if suspend should be blocked
+ *
+ * suspend_is_blocked can be used by generic power management code to abort
+ * suspend.
+ *
+ * To preserve backward compatibility suspend_is_blocked returns 0 unless it
+ * is called during suspend initiated from the suspend_block code.
+ */
+bool suspend_is_blocked(void)
+{
+	if (!enable_suspend_blockers)
+		return 0;
+	return !list_empty(&active_blockers);
+}
+
+static void suspend_worker(struct work_struct *work)
+{
+	int ret;
+	int entry_event_num;
+
+	enable_suspend_blockers = true;
+	while (!suspend_is_blocked()) {
+		entry_event_num = current_event_num;
+
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("suspend: enter suspend\n");
+
+		ret = pm_suspend(requested_suspend_state);
+
+		if (debug_mask & DEBUG_EXIT_SUSPEND)
+			pr_info_time("suspend: exit suspend, ret = %d ", ret);
+
+		if (current_event_num == entry_event_num)
+			pr_info("suspend: pm_suspend returned with no event\n");
+	}
+	enable_suspend_blockers = false;
+}
+static DECLARE_WORK(suspend_work, suspend_worker);
+
+/**
+ * suspend_blocker_init() - Initialize a suspend blocker
+ * @blocker:	The suspend blocker to initialize.
+ * @name:	The name of the suspend blocker to show in debug messages.
+ *
+ * The suspend blocker struct and name must not be freed before calling
+ * suspend_blocker_destroy.
+ */
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
+{
+	unsigned long irqflags = 0;
+
+	WARN_ON(!name);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_blocker_init name=%s\n", name);
+
+	blocker->name = name;
+	blocker->flags = SB_INITIALIZED;
+	INIT_LIST_HEAD(&blocker->link);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	list_add(&blocker->link, &inactive_blockers);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_blocker_init);
+
+/**
+ * suspend_blocker_destroy() - Destroy a suspend blocker
+ * @blocker:	The suspend blocker to destroy.
+ */
+void suspend_blocker_destroy(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	blocker->flags &= ~SB_INITIALIZED;
+	list_del(&blocker->link);
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(suspend_work_queue, &suspend_work);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_blocker_destroy);
+
+/**
+ * suspend_block() - Block suspend
+ * @blocker:	The suspend blocker to use
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_block(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	blocker->flags |= SB_ACTIVE;
+	list_del(&blocker->link);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_block: %s\n", blocker->name);
+
+	list_add(&blocker->link, &active_blockers);
+
+	current_event_num++;
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_block);
+
+/**
+ * suspend_unblock() - Unblock suspend
+ * @blocker:	The suspend blocker to unblock.
+ *
+ * If no other suspend blockers block suspend, the system will suspend.
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_unblock(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_unblock: %s\n", blocker->name);
+
+	list_del(&blocker->link);
+	list_add(&blocker->link, &inactive_blockers);
+
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(suspend_work_queue, &suspend_work);
+	blocker->flags &= ~(SB_ACTIVE);
+	if (blocker == &main_suspend_blocker) {
+		if (debug_mask & DEBUG_SUSPEND)
+			print_active_blockers_locked();
+	}
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_unblock);
+
+/**
+ * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
+ * @blocker:	The suspend blocker to check.
+ *
+ * Returns true if the suspend_blocker is currently active.
+ */
+bool suspend_blocker_is_active(struct suspend_blocker *blocker)
+{
+	WARN_ON(!(blocker->flags & SB_INITIALIZED));
+
+	return !!(blocker->flags & SB_ACTIVE);
+}
+EXPORT_SYMBOL(suspend_blocker_is_active);
+
+bool request_suspend_valid_state(suspend_state_t state)
+{
+	return (state == PM_SUSPEND_ON) || valid_state(state);
+}
+
+int request_suspend_state(suspend_state_t state)
+{
+	unsigned long irqflags;
+
+	if (!request_suspend_valid_state(state))
+		return -ENODEV;
+
+	spin_lock_irqsave(&state_lock, irqflags);
+
+	if (debug_mask & DEBUG_USER_STATE)
+		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
+			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
+			     requested_suspend_state, state,
+			     ktime_to_ns(ktime_get()));
+
+	requested_suspend_state = state;
+	if (state == PM_SUSPEND_ON)
+		suspend_block(&main_suspend_blocker);
+	else
+		suspend_unblock(&main_suspend_blocker);
+	spin_unlock_irqrestore(&state_lock, irqflags);
+	return 0;
+}
+
+static int __init suspend_block_init(void)
+{
+	suspend_work_queue = create_singlethread_workqueue("suspend");
+	if (!suspend_work_queue)
+		return -ENOMEM;
+
+	suspend_blocker_init(&main_suspend_blocker, "main");
+	suspend_block(&main_suspend_blocker);
+	return 0;
+}
+
+core_initcall(suspend_block_init);
-- 
1.6.5.1


^ permalink raw reply related	[flat|nested] 195+ messages in thread

* [PATCH 1/8] PM: Add suspend block api.
  2010-04-28  4:31 [PATCH 0/9] Suspend block api (version 5) Arve Hjønnevåg
@ 2010-04-28  4:31 ` Arve Hjønnevåg
  2010-04-28  4:31 ` Arve Hjønnevåg
  1 sibling, 0 replies; 195+ messages in thread
From: Arve Hjønnevåg @ 2010-04-28  4:31 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: Len Brown, linux-doc, Oleg Nesterov, Jesse Barnes, Tejun Heo,
	Magnus Damm, Andrew Morton, Wu Fengguang

Adds /sys/power/policy that selects the behaviour of /sys/power/state.
After setting the policy to opportunistic, writes to /sys/power/state
become non-blocking requests that specify which suspend state to enter
when no suspend blockers are active. A special state, "on", stops the
process by activating the "main" suspend blocker.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/opportunistic-suspend.txt |  114 +++++++++++
 include/linux/suspend_blocker.h               |   64 ++++++
 kernel/power/Kconfig                          |   16 ++
 kernel/power/Makefile                         |    1 +
 kernel/power/main.c                           |   89 ++++++++-
 kernel/power/power.h                          |    5 +
 kernel/power/suspend.c                        |    4 +-
 kernel/power/suspend_blocker.c                |  269 +++++++++++++++++++++++++
 8 files changed, 556 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/power/opportunistic-suspend.txt
 create mode 100755 include/linux/suspend_blocker.h
 create mode 100644 kernel/power/suspend_blocker.c

diff --git a/Documentation/power/opportunistic-suspend.txt b/Documentation/power/opportunistic-suspend.txt
new file mode 100644
index 0000000..1a29d10
--- /dev/null
+++ b/Documentation/power/opportunistic-suspend.txt
@@ -0,0 +1,114 @@
+Opportunistic Suspend
+=====================
+
+Opportunistic suspend is a feature allowing the system to be suspended (ie. put
+into one of the available sleep states) automatically whenever it is regarded
+as idle.  The suspend blockers framework described below is used to determine
+when that happens.
+
+The /sys/power/policy sysfs attribute is used to switch the system between the
+opportunistic and "forced" suspend behavior, where in the latter case the
+system is only suspended if a specific value, corresponding to one of the
+available system sleep states, is written into /sys/power/state.  However, in
+the former, opportunistic, case the system is put into the sleep state
+corresponding to the value written to /sys/power/state whenever there are no
+active suspend blockers. The default policy is "forced". Also, suspend blockers
+do not affect sleep states entered from idle.
+
+When the policy is "opportunisic", there is a special value, "on", that can be
+written to /sys/power/state. This will block the automatic sleep request, as if
+a suspend blocker was used by a device driver. This way the opportunistic
+suspend may be blocked by user space whithout switching back to the "forced"
+mode.
+
+A suspend blocker is an object used to inform the PM subsystem when the system
+can or cannot be suspended in the "opportunistic" mode (the "forced" mode
+ignores suspend blockers).  To use it, a device driver creates a struct
+suspend_blocker that must be initialized with suspend_blocker_init(). Before
+freeing the suspend_blocker structure or its name, suspend_blocker_destroy()
+must be called on it.
+
+A suspend blocker is activated using suspend_block(), which prevents the PM
+subsystem from putting the system into the requested sleep state in the
+"opportunistic" mode until the suspend blocker is deactivated with
+suspend_unblock(). Multiple suspend blockers may be active simultaneously, and
+the system will not suspend as long as at least one of them is active.
+
+If opportunistic suspend is already in progress when suspend_block() is called,
+it will abort the suspend, unless suspend_ops->enter has already been
+executed. If suspend is aborted this way, the system is usually not fully
+operational at that point. The suspend callbacks of some drivers may still be
+running and it usually takes time to restore the system to the fully operational
+state.
+
+For example, in cell phones or other embedded systems, where powering the screen
+is a significant drain on the battery, suspend blockers can be used to allow
+user-space to decide whether a keystroke received while the system is suspended
+should cause the screen to be turned back on or allow the system to go back into
+suspend. Use set_irq_wake or a platform specific api to make sure the keypad
+interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
+events can look like this:
+
+- The Keypad driver gets an interrupt. It then calls suspend_block on the
+  keypad-scan suspend_blocker and starts scanning the keypad matrix.
+- The keypad-scan code detects a key change and reports it to the input-event
+  driver.
+- The input-event driver sees the key change, enqueues an event, and calls
+  suspend_block on the input-event-queue suspend_blocker.
+- The keypad-scan code detects that no keys are held and calls suspend_unblock
+  on the keypad-scan suspend_blocker.
+- The user-space input-event thread returns from select/poll, calls
+  suspend_block on the process-input-events suspend_blocker and then calls read
+  on the input-event device.
+- The input-event driver dequeues the key-event and, since the queue is now
+  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
+- The user-space input-event thread returns from read. If it determines that
+  the key should leave the screen off, it calls suspend_unblock on the
+  process_input_events suspend_blocker and then calls select or poll. The
+  system will automatically suspend again, since now no suspend blockers are
+  active.
+
+                 Key pressed   Key released
+                     |             |
+keypad-scan          ++++++++++++++++++
+input-event-queue        +++          +++
+process-input-events       +++          +++
+
+
+Driver API
+==========
+
+A driver can use the suspend block api by adding a suspend_blocker variable to
+its state and calling suspend_blocker_init. For instance:
+struct state {
+	struct suspend_blocker suspend_blocker;
+}
+
+init() {
+	suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
+}
+
+Before freeing the memory, suspend_blocker_destroy must be called:
+
+uninit() {
+	suspend_blocker_destroy(&state->suspend_blocker);
+}
+
+When the driver determines that it needs to run (usually in an interrupt
+handler) it calls suspend_block:
+	suspend_block(&state->suspend_blocker);
+
+When it no longer needs to run it calls suspend_unblock:
+	suspend_unblock(&state->suspend_blocker);
+
+Calling suspend_block when the suspend blocker is active or suspend_unblock when
+it is not active has no effect (i.e., these functions don't nest). This allows
+drivers to update their state and call suspend suspend_block or suspend_unblock
+based on the result.
+For instance:
+
+if (list_empty(&state->pending_work))
+	suspend_unblock(&state->suspend_blocker);
+else
+	suspend_block(&state->suspend_blocker);
+
diff --git a/include/linux/suspend_blocker.h b/include/linux/suspend_blocker.h
new file mode 100755
index 0000000..f9928cc
--- /dev/null
+++ b/include/linux/suspend_blocker.h
@@ -0,0 +1,64 @@
+/* include/linux/suspend_blocker.h
+ *
+ * Copyright (C) 2007-2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SUSPEND_BLOCKER_H
+#define _LINUX_SUSPEND_BLOCKER_H
+
+#include <linux/list.h>
+
+/**
+ * struct suspend_blocker - the basic suspend_blocker structure
+ * @link:	List entry for active or inactive list.
+ * @flags:	Tracks initialized and active state.
+ * @name:	Name used for debugging.
+ *
+ * When a suspend_blocker is active it prevents the system from entering
+ * opportunistic suspend.
+ *
+ * The suspend_blocker structure must be initialized by suspend_blocker_init()
+ */
+
+struct suspend_blocker {
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	struct list_head    link;
+	int                 flags;
+	const char         *name;
+#endif
+};
+
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name);
+void suspend_blocker_destroy(struct suspend_blocker *blocker);
+void suspend_block(struct suspend_blocker *blocker);
+void suspend_unblock(struct suspend_blocker *blocker);
+bool suspend_blocker_is_active(struct suspend_blocker *blocker);
+bool suspend_is_blocked(void);
+
+#else
+
+static inline void suspend_blocker_init(struct suspend_blocker *blocker,
+					const char *name) {}
+static inline void suspend_blocker_destroy(struct suspend_blocker *blocker) {}
+static inline void suspend_block(struct suspend_blocker *blocker) {}
+static inline void suspend_unblock(struct suspend_blocker *blocker) {}
+static inline bool suspend_blocker_is_active(struct suspend_blocker *bl)
+								{ return 0; }
+static inline bool suspend_is_blocked(void) { return 0; }
+
+#endif
+
+#endif
+
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 5c36ea9..55a06a1 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -130,6 +130,22 @@ config SUSPEND_FREEZER
 
 	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
 
+config OPPORTUNISTIC_SUSPEND
+	bool "Suspend blockers"
+	depends on PM_SLEEP
+	select RTC_LIB
+	default n
+	---help---
+	  Opportunistic sleep support.  Allows the system to be put into a sleep
+	  state opportunistically, if it doesn't do any useful work at the
+	  moment. The PM subsystem is switched into this mode of operation by
+	  writing "opportunistic" into /sys/power/policy, while writing
+	  "forced" to this file turns the opportunistic suspend feature off.
+	  In the "opportunistic" mode suspend blockers are used to determine
+	  when to suspend the system and the value written to /sys/power/state
+	  determines the sleep state the system will be put into when there are
+	  no active suspend blockers.
+
 config HIBERNATION_NVS
 	bool
 
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 4319181..ee5276d 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
+obj-$(CONFIG_OPPORTUNISTIC_SUSPEND)	+= suspend_blocker.o
 obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
 obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o
 obj-$(CONFIG_HIBERNATION_NVS)	+= hibernate_nvs.o
diff --git a/kernel/power/main.c b/kernel/power/main.c
index b58800b..5f0af6c 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -12,6 +12,7 @@
 #include <linux/string.h>
 #include <linux/resume-trace.h>
 #include <linux/workqueue.h>
+#include <linux/suspend_blocker.h>
 
 #include "power.h"
 
@@ -20,6 +21,27 @@ DEFINE_MUTEX(pm_mutex);
 unsigned int pm_flags;
 EXPORT_SYMBOL(pm_flags);
 
+struct policy {
+	const char *name;
+	bool (*valid_state)(suspend_state_t state);
+	int (*set_state)(suspend_state_t state);
+};
+static struct policy policies[] = {
+	{
+		.name		= "forced",
+		.valid_state	= valid_state,
+		.set_state	= enter_state,
+	},
+#ifdef CONFIG_OPPORTUNISTIC_SUSPEND
+	{
+		.name		= "opportunistic",
+		.valid_state	= request_suspend_valid_state,
+		.set_state	= request_suspend_state,
+	},
+#endif
+};
+static int policy;
+
 #ifdef CONFIG_PM_SLEEP
 
 /* Routines for PM-transition notifications */
@@ -146,6 +168,12 @@ struct kobject *power_kobj;
  *
  *	store() accepts one of those strings, translates it into the 
  *	proper enumerated value, and initiates a suspend transition.
+ *
+ *	If policy is set to opportunistic, store() does not block until the
+ *	system resumes, and it will try to re-enter the state until another
+ *	state is requested. Suspend blockers are respected and the requested
+ *	state will only be entered when no suspend blockers are active.
+ *	Write "on" to cancel.
  */
 static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 			  char *buf)
@@ -155,12 +183,13 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 	int i;
 
 	for (i = 0; i < PM_SUSPEND_MAX; i++) {
-		if (pm_states[i] && valid_state(i))
+		if (pm_states[i] && policies[policy].valid_state(i))
 			s += sprintf(s,"%s ", pm_states[i]);
 	}
 #endif
 #ifdef CONFIG_HIBERNATION
-	s += sprintf(s, "%s\n", "disk");
+	if (!policy)
+		s += sprintf(s, "%s\n", "disk");
 #else
 	if (s != buf)
 		/* convert the last space to a newline */
@@ -173,7 +202,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			   const char *buf, size_t n)
 {
 #ifdef CONFIG_SUSPEND
-	suspend_state_t state = PM_SUSPEND_STANDBY;
+	suspend_state_t state = PM_SUSPEND_ON;
 	const char * const *s;
 #endif
 	char *p;
@@ -184,7 +213,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 	len = p ? p - buf : n;
 
 	/* First, check if we are requested to hibernate */
-	if (len == 4 && !strncmp(buf, "disk", len)) {
+	if (len == 4 && !strncmp(buf, "disk", len) && !policy) {
 		error = hibernate();
   goto Exit;
 	}
@@ -195,7 +224,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 			break;
 	}
 	if (state < PM_SUSPEND_MAX && *s)
-		error = enter_state(state);
+		error = policies[policy].set_state(state);
 #endif
 
  Exit:
@@ -204,6 +233,55 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 
 power_attr(state);
 
+/**
+ *	policy - set policy for state
+ */
+
+static ssize_t policy_show(struct kobject *kobj,
+			   struct kobj_attribute *attr, char *buf)
+{
+	char *s = buf;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		if (i == policy)
+			s += sprintf(s, "[%s] ", policies[i].name);
+		else
+			s += sprintf(s, "%s ", policies[i].name);
+	}
+	if (s != buf)
+		/* convert the last space to a newline */
+		*(s-1) = '\n';
+	return (s - buf);
+}
+
+static ssize_t policy_store(struct kobject *kobj,
+			    struct kobj_attribute *attr,
+			    const char *buf, size_t n)
+{
+	const char *s;
+	char *p;
+	int len;
+	int i;
+
+	p = memchr(buf, '\n', n);
+	len = p ? p - buf : n;
+
+	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+		s = policies[i].name;
+		if (s && len == strlen(s) && !strncmp(buf, s, len)) {
+			mutex_lock(&pm_mutex);
+			policies[policy].set_state(PM_SUSPEND_ON);
+			policy = i;
+			mutex_unlock(&pm_mutex);
+			return n;
+		}
+	}
+	return -EINVAL;
+}
+
+power_attr(policy);
+
 #ifdef CONFIG_PM_TRACE
 int pm_trace_enabled;
 
@@ -231,6 +309,7 @@ power_attr(pm_trace);
 
 static struct attribute * g[] = {
 	&state_attr.attr,
+	&policy_attr.attr,
 #ifdef CONFIG_PM_TRACE
 	&pm_trace_attr.attr,
 #endif
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 46c5a26..9b468d7 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -236,3 +236,8 @@ static inline void suspend_thaw_processes(void)
 {
 }
 #endif
+
+/* kernel/power/suspend_block.c */
+extern int request_suspend_state(suspend_state_t state);
+extern bool request_suspend_valid_state(suspend_state_t state);
+
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 56e7dbb..dc42006 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -16,10 +16,12 @@
 #include <linux/cpu.h>
 #include <linux/syscalls.h>
 #include <linux/gfp.h>
+#include <linux/suspend_blocker.h>
 
 #include "power.h"
 
 const char *const pm_states[PM_SUSPEND_MAX] = {
+	[PM_SUSPEND_ON]		= "on",
 	[PM_SUSPEND_STANDBY]	= "standby",
 	[PM_SUSPEND_MEM]	= "mem",
 };
@@ -157,7 +159,7 @@ static int suspend_enter(suspend_state_t state)
 
 	error = sysdev_suspend(PMSG_SUSPEND);
 	if (!error) {
-		if (!suspend_test(TEST_CORE))
+		if (!suspend_is_blocked() && !suspend_test(TEST_CORE))
 			error = suspend_ops->enter(state);
 		sysdev_resume();
 	}
diff --git a/kernel/power/suspend_blocker.c b/kernel/power/suspend_blocker.c
new file mode 100644
index 0000000..9459361
--- /dev/null
+++ b/kernel/power/suspend_blocker.c
@@ -0,0 +1,269 @@
+/* kernel/power/suspend_blocker.c
+ *
+ * Copyright (C) 2005-2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/suspend.h>
+#include <linux/suspend_blocker.h>
+#include "power.h"
+
+enum {
+	DEBUG_EXIT_SUSPEND = 1U << 0,
+	DEBUG_WAKEUP = 1U << 1,
+	DEBUG_USER_STATE = 1U << 2,
+	DEBUG_SUSPEND = 1U << 3,
+	DEBUG_SUSPEND_BLOCKER = 1U << 4,
+};
+static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP | DEBUG_USER_STATE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SB_INITIALIZED            (1U << 8)
+#define SB_ACTIVE                 (1U << 9)
+
+static DEFINE_SPINLOCK(list_lock);
+static DEFINE_SPINLOCK(state_lock);
+static LIST_HEAD(inactive_blockers);
+static LIST_HEAD(active_blockers);
+static int current_event_num;
+struct workqueue_struct *suspend_work_queue;
+struct suspend_blocker main_suspend_blocker;
+static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
+static bool enable_suspend_blockers;
+
+#define pr_info_time(fmt, args...) \
+	do { \
+		struct timespec ts; \
+		struct rtc_time tm; \
+		getnstimeofday(&ts); \
+		rtc_time_to_tm(ts.tv_sec, &tm); \
+		pr_info(fmt "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n" , \
+			args, \
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
+			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
+	} while (0);
+
+static void print_active_blockers_locked(void)
+{
+	struct suspend_blocker *blocker;
+
+	list_for_each_entry(blocker, &active_blockers, link)
+		pr_info("active suspend blocker %s\n", blocker->name);
+}
+
+/**
+ * suspend_is_blocked() - Check if suspend should be blocked
+ *
+ * suspend_is_blocked can be used by generic power management code to abort
+ * suspend.
+ *
+ * To preserve backward compatibility suspend_is_blocked returns 0 unless it
+ * is called during suspend initiated from the suspend_block code.
+ */
+bool suspend_is_blocked(void)
+{
+	if (!enable_suspend_blockers)
+		return 0;
+	return !list_empty(&active_blockers);
+}
+
+static void suspend_worker(struct work_struct *work)
+{
+	int ret;
+	int entry_event_num;
+
+	enable_suspend_blockers = true;
+	while (!suspend_is_blocked()) {
+		entry_event_num = current_event_num;
+
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("suspend: enter suspend\n");
+
+		ret = pm_suspend(requested_suspend_state);
+
+		if (debug_mask & DEBUG_EXIT_SUSPEND)
+			pr_info_time("suspend: exit suspend, ret = %d ", ret);
+
+		if (current_event_num == entry_event_num)
+			pr_info("suspend: pm_suspend returned with no event\n");
+	}
+	enable_suspend_blockers = false;
+}
+static DECLARE_WORK(suspend_work, suspend_worker);
+
+/**
+ * suspend_blocker_init() - Initialize a suspend blocker
+ * @blocker:	The suspend blocker to initialize.
+ * @name:	The name of the suspend blocker to show in debug messages.
+ *
+ * The suspend blocker struct and name must not be freed before calling
+ * suspend_blocker_destroy.
+ */
+void suspend_blocker_init(struct suspend_blocker *blocker, const char *name)
+{
+	unsigned long irqflags = 0;
+
+	WARN_ON(!name);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_blocker_init name=%s\n", name);
+
+	blocker->name = name;
+	blocker->flags = SB_INITIALIZED;
+	INIT_LIST_HEAD(&blocker->link);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	list_add(&blocker->link, &inactive_blockers);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_blocker_init);
+
+/**
+ * suspend_blocker_destroy() - Destroy a suspend blocker
+ * @blocker:	The suspend blocker to destroy.
+ */
+void suspend_blocker_destroy(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_blocker_destroy name=%s\n", blocker->name);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	blocker->flags &= ~SB_INITIALIZED;
+	list_del(&blocker->link);
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(suspend_work_queue, &suspend_work);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_blocker_destroy);
+
+/**
+ * suspend_block() - Block suspend
+ * @blocker:	The suspend blocker to use
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_block(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	blocker->flags |= SB_ACTIVE;
+	list_del(&blocker->link);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_block: %s\n", blocker->name);
+
+	list_add(&blocker->link, &active_blockers);
+
+	current_event_num++;
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_block);
+
+/**
+ * suspend_unblock() - Unblock suspend
+ * @blocker:	The suspend blocker to unblock.
+ *
+ * If no other suspend blockers block suspend, the system will suspend.
+ *
+ * It is safe to call this function from interrupt context.
+ */
+void suspend_unblock(struct suspend_blocker *blocker)
+{
+	unsigned long irqflags;
+
+	if (WARN_ON(!(blocker->flags & SB_INITIALIZED)))
+		return;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	if (debug_mask & DEBUG_SUSPEND_BLOCKER)
+		pr_info("suspend_unblock: %s\n", blocker->name);
+
+	list_del(&blocker->link);
+	list_add(&blocker->link, &inactive_blockers);
+
+	if ((blocker->flags & SB_ACTIVE) && list_empty(&active_blockers))
+		queue_work(suspend_work_queue, &suspend_work);
+	blocker->flags &= ~(SB_ACTIVE);
+	if (blocker == &main_suspend_blocker) {
+		if (debug_mask & DEBUG_SUSPEND)
+			print_active_blockers_locked();
+	}
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(suspend_unblock);
+
+/**
+ * suspend_blocker_is_active() - Test if a suspend blocker is blocking suspend
+ * @blocker:	The suspend blocker to check.
+ *
+ * Returns true if the suspend_blocker is currently active.
+ */
+bool suspend_blocker_is_active(struct suspend_blocker *blocker)
+{
+	WARN_ON(!(blocker->flags & SB_INITIALIZED));
+
+	return !!(blocker->flags & SB_ACTIVE);
+}
+EXPORT_SYMBOL(suspend_blocker_is_active);
+
+bool request_suspend_valid_state(suspend_state_t state)
+{
+	return (state == PM_SUSPEND_ON) || valid_state(state);
+}
+
+int request_suspend_state(suspend_state_t state)
+{
+	unsigned long irqflags;
+
+	if (!request_suspend_valid_state(state))
+		return -ENODEV;
+
+	spin_lock_irqsave(&state_lock, irqflags);
+
+	if (debug_mask & DEBUG_USER_STATE)
+		pr_info_time("request_suspend_state: %s (%d->%d) at %lld ",
+			     state != PM_SUSPEND_ON ? "sleep" : "wakeup",
+			     requested_suspend_state, state,
+			     ktime_to_ns(ktime_get()));
+
+	requested_suspend_state = state;
+	if (state == PM_SUSPEND_ON)
+		suspend_block(&main_suspend_blocker);
+	else
+		suspend_unblock(&main_suspend_blocker);
+	spin_unlock_irqrestore(&state_lock, irqflags);
+	return 0;
+}
+
+static int __init suspend_block_init(void)
+{
+	suspend_work_queue = create_singlethread_workqueue("suspend");
+	if (!suspend_work_queue)
+		return -ENOMEM;
+
+	suspend_blocker_init(&main_suspend_blocker, "main");
+	suspend_block(&main_suspend_blocker);
+	return 0;
+}
+
+core_initcall(suspend_block_init);
-- 
1.6.5.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply related	[flat|nested] 195+ messages in thread

* Re: [PATCH 1/8] PM: Add suspend block api
@ 2009-04-16  6:00 Sam Shang
  0 siblings, 0 replies; 195+ messages in thread
From: Sam Shang @ 2009-04-16  6:00 UTC (permalink / raw)
  To: linux-pm


[-- Attachment #1.1: Type: text/plain, Size: 1843 bytes --]


Arve,
 
I can see the usage of suspend blockers from the example you described below. I understand how applications can make use of suspend blockers to save power. Still, my first rule of their usage is don't use them unless necessary, as their misuse might cause increase of power usage.
 
I'm wondering whether you have a recommended rule of suspend blocker usage for both applications and drivers. From application and driver development point of view, I don't want to add suspend blockers everywhere.
 
Sam Shang
 
*******************
Date: Wed, 15 Apr 2009 17:34:43 -0700
From: Arve Hj?nnev?g <arve@android.com>
Subject: Re: [linux-pm] [PATCH 1/8] PM: Add suspend block api.
To: Alan Stern <stern@rowland.harvard.edu>
Cc: ncunningham@crca.org.au, u.luckas@road.de, swetland@google.com,
        Linux-pm mailing list <linux-pm@lists.linux-foundation.org>
Message-ID:
        <d6200be20904151734o738c7a5dx52a800b97ebc2708@mail.gmail.com>
Content-Type: text/plain; charset=ISO-8859-1
 
2009/4/15 Alan Stern <stern@rowland.harvard.edu>:
> On Tue, 14 Apr 2009, [utf-8] Arve Hj?nnev?g wrote:
> 
>> +Suspend blockers can be used to allow user-space to decide which keys should
> 
> Then in the last section, say this:
> 
> ? ? ?- The user-space input-event thread returns from read. If it
> ? ? ? ?determines that the key should leave the screen off, it
> ? ? ? ?calls suspend_unblock on the process_input_events
> ? ? ? ?suspend_blocker and then calls select or poll. ?The system
> ? ? ? ?will automatically suspend again, since now no suspend blockers
> ? ? ? ?are active.
> 
 
This sounds reasonable too me.
 
-- 
Arve Hj?nnev?g
_________________________________________________________________
More than messages–check out the rest of the Windows Live™.
http://www.microsoft.com/windows/windowslive/

[-- Attachment #1.2: Type: text/html, Size: 7670 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 195+ messages in thread

end of thread, other threads:[~2010-05-28 13:42 UTC | newest]

Thread overview: 195+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-04-15  1:41 [RFC][PATCH 0/8] Suspend block api Arve Hjønnevåg
2009-04-15  1:41 ` [PATCH 1/8] PM: Add suspend " Arve Hjønnevåg
2009-04-15  1:41   ` [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space Arve Hjønnevåg
2009-04-15  1:41     ` [PATCH 3/8] PM: suspend_block: Abort task freezing if a suspend_blocker is active Arve Hjønnevåg
2009-04-15  1:41       ` [PATCH 4/8] Input: Block suspend while event queue is not empty Arve Hjønnevåg
2009-04-15  1:41         ` [PATCH 5/8] PM: suspend_block: Switch to list of active and inactive suspend blockers Arve Hjønnevåg
2009-04-15  1:41           ` [PATCH 6/8] PM: suspend_block: Add suspend_blocker stats Arve Hjønnevåg
2009-04-15  1:41             ` [PATCH 7/8] PM: suspend_block: Add timeout support Arve Hjønnevåg
2009-04-15  1:41               ` [PATCH 8/8] PM: suspend_block: Add timeout support to user-space suspend_blockers Arve Hjønnevåg
2009-04-29 22:56       ` [PATCH 3/8] PM: suspend_block: Abort task freezing if a suspend_blocker is active Rafael J. Wysocki
2009-04-29 22:52     ` [PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space Rafael J. Wysocki
2009-04-15 15:29   ` [PATCH 1/8] PM: Add suspend block api Alan Stern
2009-04-15 19:08     ` mark gross
2009-04-16  0:40       ` Arve Hjønnevåg
2009-04-16  0:34     ` Arve Hjønnevåg
2009-04-15 22:31   ` mark gross
2009-04-16  1:45     ` Arve Hjønnevåg
2009-04-16 17:49       ` mark gross
2009-04-20  9:29   ` Pavel Machek
2009-04-21  4:44     ` Arve Hjønnevåg
2009-04-24 20:59       ` Pavel Machek
2009-04-29 21:24         ` Rafael J. Wysocki
2009-04-29 22:52           ` Arve Hjønnevåg
2009-04-29 22:34   ` Rafael J. Wysocki
2009-04-29 23:45     ` Arve Hjønnevåg
2009-04-30  0:49     ` Arve Hjønnevåg
2009-04-26  9:42       ` Pavel Machek
2009-05-02 12:17         ` Rafael J. Wysocki
2009-05-02 12:14       ` Rafael J. Wysocki
2009-05-02 20:51         ` Pavel Machek
2009-05-05  3:48         ` Arve Hjønnevåg
2009-04-15 23:04 ` [RFC][PATCH 0/8] Suspend " Rafael J. Wysocki
2009-04-16  6:00 [PATCH 1/8] PM: Add suspend " Sam Shang
2010-04-28  4:31 [PATCH 0/9] Suspend block api (version 5) Arve Hjønnevåg
2010-04-28  4:31 ` [PATCH 1/8] PM: Add suspend block api Arve Hjønnevåg
2010-04-28  4:31 ` Arve Hjønnevåg
2010-04-28  6:07   ` Pavel Machek
2010-04-28  6:07     ` Pavel Machek
2010-04-28 19:13   ` Alan Stern
2010-04-28 21:13     ` Rafael J. Wysocki
2010-04-28 21:13     ` Rafael J. Wysocki
2010-04-28 23:35       ` Arve Hjønnevåg
2010-04-28 23:35       ` Arve Hjønnevåg
2010-04-29 15:41         ` Alan Stern
2010-04-29 15:41           ` Alan Stern
2010-04-29 23:39           ` Arve Hjønnevåg
2010-04-29 23:39             ` Arve Hjønnevåg
2010-04-30 14:41             ` Alan Stern
2010-04-30 14:41               ` Alan Stern
2010-04-28 19:13   ` Alan Stern
2010-04-28 20:50   ` Rafael J. Wysocki
2010-04-28 20:50   ` Rafael J. Wysocki
2010-04-29  3:37     ` Arve Hjønnevåg
2010-04-29 21:16       ` Rafael J. Wysocki
2010-04-29 21:16         ` Rafael J. Wysocki
2010-04-30  4:24         ` Tejun Heo
2010-04-30  4:24           ` Tejun Heo
2010-04-30 17:26           ` Oleg Nesterov
2010-04-30 17:26           ` Oleg Nesterov
2010-05-20  8:30             ` Tejun Heo
2010-05-20 22:27               ` Rafael J. Wysocki
2010-05-21  6:35                 ` Tejun Heo
2010-05-21  6:35                   ` Tejun Heo
2010-05-20 22:27               ` Rafael J. Wysocki
2010-05-20  8:30             ` Tejun Heo
2010-04-29  3:37     ` Arve Hjønnevåg
2010-05-06 15:18   ` Alan Stern
2010-05-06 15:18   ` Alan Stern
2010-05-06 19:28     ` Rafael J. Wysocki
2010-05-06 19:28     ` Rafael J. Wysocki
2010-05-06 19:40       ` Alan Stern
2010-05-06 19:40       ` Alan Stern
2010-05-06 23:48         ` Arve Hjønnevåg
2010-05-06 23:48         ` Arve Hjønnevåg
2010-05-07 14:22           ` Alan Stern
2010-05-07 14:22           ` Alan Stern
2010-04-30 22:36 [PATCH 0/8] Suspend block api (version 6) Arve Hjønnevåg
2010-04-30 22:36 ` [PATCH 1/8] PM: Add suspend block api Arve Hjønnevåg
2010-04-30 22:36 ` Arve Hjønnevåg
2010-05-02  6:56   ` Pavel Machek
2010-05-02  6:56   ` Pavel Machek
2010-05-02 20:10     ` Rafael J. Wysocki
2010-05-02 20:52       ` Pavel Machek
2010-05-02 21:29         ` Rafael J. Wysocki
2010-05-02 21:29         ` Rafael J. Wysocki
2010-05-03 19:01           ` Pavel Machek
2010-05-03 21:38             ` Rafael J. Wysocki
2010-05-03 21:38             ` Rafael J. Wysocki
2010-05-03 22:11               ` Alan Stern
2010-05-03 22:11               ` Alan Stern
2010-05-03 22:24                 ` Arve Hjønnevåg
2010-05-03 22:24                 ` Arve Hjønnevåg
2010-05-03 19:01           ` Pavel Machek
2010-05-02 20:52       ` Pavel Machek
2010-05-02 20:10     ` Rafael J. Wysocki
2010-05-02  7:01   ` Pavel Machek
2010-05-02  7:01   ` Pavel Machek
2010-05-04  5:12   ` [linux-pm] " mark gross
2010-05-04 13:59     ` Alan Stern
2010-05-04 13:59     ` [linux-pm] " Alan Stern
2010-05-04 16:03       ` mark gross
2010-05-04 20:40     ` Arve Hjønnevåg
2010-05-04  5:12   ` mark gross
2010-05-13 19:01   ` Paul Walmsley
2010-05-14 20:05     ` Paul Walmsley
2010-05-04 16:03 [linux-pm] " mark gross
2010-05-04 17:16 ` Alan Stern
2010-05-05  1:50   ` mark gross
2010-05-05 13:31     ` Matthew Garrett
2010-05-05 20:09       ` mark gross
2010-05-05 20:21         ` Matthew Garrett
2010-05-05 20:09       ` mark gross
2010-05-05 13:31     ` Matthew Garrett
2010-05-05 15:44     ` Alan Stern
2010-05-05 15:44     ` [linux-pm] " Alan Stern
2010-05-05 20:28       ` mark gross
2010-05-05  1:50   ` mark gross
2010-05-04 17:16 ` Alan Stern
2010-05-05 20:28 [linux-pm] " mark gross
2010-05-05 21:12 ` Alan Stern
2010-05-05 21:37   ` Brian Swetland
2010-05-05 23:47     ` Tony Lindgren
2010-05-05 23:47     ` [linux-pm] " Tony Lindgren
2010-05-05 23:56       ` Brian Swetland
2010-05-06  0:05         ` Tony Lindgren
2010-05-06  4:16           ` Arve Hjønnevåg
2010-05-06 17:04             ` Tony Lindgren
2010-05-07  0:10               ` Arve Hjønnevåg
2010-05-07 15:54                 ` Tony Lindgren
2010-05-28  6:43                 ` Pavel Machek
2010-05-28  6:43                 ` [linux-pm] " Pavel Machek
2010-05-28  7:01                   ` Arve Hjønnevåg
2010-05-07  0:10               ` Arve Hjønnevåg
2010-05-06 17:04             ` Tony Lindgren
2010-05-06  4:16           ` Arve Hjønnevåg
2010-05-06  0:05         ` Tony Lindgren
2010-05-05 23:56       ` Brian Swetland
2010-05-06 13:40       ` Matthew Garrett
2010-05-06 13:40       ` [linux-pm] " Matthew Garrett
2010-05-06 17:01         ` Tony Lindgren
2010-05-06 17:01         ` [linux-pm] " Tony Lindgren
2010-05-06 17:09           ` Matthew Garrett
2010-05-06 17:09           ` [linux-pm] " Matthew Garrett
2010-05-06 17:14             ` Tony Lindgren
2010-05-06 17:22               ` Matthew Garrett
2010-05-06 17:38                 ` Tony Lindgren
2010-05-06 17:38                 ` [linux-pm] " Tony Lindgren
2010-05-06 17:43                   ` Matthew Garrett
2010-05-06 18:33                     ` Tony Lindgren
2010-05-06 18:33                     ` [linux-pm] " Tony Lindgren
2010-05-06 18:44                       ` Matthew Garrett
2010-05-07  2:05                         ` Tony Lindgren
2010-05-07  2:05                         ` [linux-pm] " Tony Lindgren
2010-05-07 17:12                           ` Matthew Garrett
2010-05-07 17:35                             ` Tony Lindgren
2010-05-07 17:35                             ` [linux-pm] " Tony Lindgren
2010-05-07 17:50                               ` Matthew Garrett
2010-05-07 18:01                                 ` Tony Lindgren
2010-05-07 18:01                                 ` [linux-pm] " Tony Lindgren
2010-05-07 18:28                                   ` Matthew Garrett
2010-05-07 18:43                                     ` Tony Lindgren
2010-05-07 18:46                                       ` Matthew Garrett
2010-05-07 18:46                                       ` [linux-pm] " Matthew Garrett
2010-05-07 19:06                                         ` Daniel Walker
2010-05-07 19:06                                         ` [linux-pm] " Daniel Walker
2010-05-07 19:28                                           ` Tony Lindgren
2010-05-07 19:33                                             ` Matthew Garrett
2010-05-07 19:33                                             ` [linux-pm] " Matthew Garrett
2010-05-07 19:55                                               ` Tony Lindgren
2010-05-07 20:28                                                 ` Matthew Garrett
2010-05-07 20:28                                                 ` [linux-pm] " Matthew Garrett
2010-05-07 20:53                                                   ` Tony Lindgren
2010-05-07 20:53                                                   ` [linux-pm] " Tony Lindgren
2010-05-07 21:03                                                     ` Matthew Garrett
2010-05-07 21:25                                                       ` Tony Lindgren
2010-05-07 21:25                                                       ` [linux-pm] " Tony Lindgren
2010-05-07 21:32                                                         ` Arve Hjønnevåg
2010-05-07 21:39                                                         ` Matthew Garrett
2010-05-07 21:39                                                         ` [linux-pm] " Matthew Garrett
2010-05-07 21:42                                                           ` Tony Lindgren
2010-05-07 21:42                                                           ` [linux-pm] " Tony Lindgren
2010-05-07 21:48                                                             ` Matthew Garrett
2010-05-07 22:00                                                               ` Tony Lindgren
2010-05-07 22:00                                                               ` [linux-pm] " Tony Lindgren
2010-05-07 22:28                                                                 ` Matthew Garrett
2010-05-07 21:48                                                             ` Matthew Garrett
2010-05-07 21:30                                                       ` Daniel Walker
2010-05-07 21:30                                                       ` [linux-pm] " Daniel Walker
2010-05-07 21:35                                                         ` Arve Hjønnevåg
2010-05-07 21:43                                                           ` Daniel Walker
2010-05-07 21:35                                                         ` Arve Hjønnevåg
2010-05-07 21:38                                                         ` Matthew Garrett
2010-05-07 21:03                                                     ` Matthew Garrett
2010-05-07 19:55                                               ` Tony Lindgren
2010-05-07 19:28                                           ` Tony Lindgren
2010-05-07 18:28                                   ` Matthew Garrett
2010-05-07 17:50                               ` Matthew Garrett
2010-05-07 17:12                           ` Matthew Garrett
2010-05-06 18:44                       ` Matthew Garrett
2010-05-06 18:47                       ` Alan Stern
2010-05-06 18:47                       ` [linux-pm] " Alan Stern
2010-05-07  2:20                         ` Tony Lindgren
2010-05-06 17:43                   ` Matthew Garrett
2010-05-28 13:29                   ` [linux-pm] " Pavel Machek
2010-05-28 13:42                     ` Brian Swetland
2010-05-28 13:29                   ` Pavel Machek
2010-05-06 17:22               ` Matthew Garrett
2010-05-06 17:35               ` [linux-pm] " Daniel Walker
2010-05-06 18:36                 ` Tony Lindgren
2010-05-06 19:11                   ` Daniel Walker
2010-05-07  2:00                     ` Tony Lindgren
2010-05-07  2:00                     ` [linux-pm] " Tony Lindgren
2010-05-07 17:20                       ` Daniel Walker
2010-05-07 17:36                         ` Matthew Garrett
2010-05-07 17:36                         ` [linux-pm] " Matthew Garrett
2010-05-07 17:40                           ` Daniel Walker
2010-05-07 17:51                             ` Matthew Garrett
2010-05-07 17:51                             ` [linux-pm] " Matthew Garrett
2010-05-07 18:00                               ` Daniel Walker
2010-05-07 18:17                                 ` Tony Lindgren
2010-05-07 18:00                               ` Daniel Walker
2010-05-07 17:40                           ` Daniel Walker
2010-05-07 17:50                         ` Tony Lindgren
2010-05-07 17:20                       ` Daniel Walker
2010-05-06 19:11                   ` Daniel Walker
2010-05-06 18:36                 ` Tony Lindgren
2010-05-06 17:35               ` Daniel Walker
2010-05-07  3:45               ` mgross
2010-05-06 17:14             ` Tony Lindgren
2010-05-07  3:45             ` [linux-pm] " mgross
2010-05-07  4:10               ` Arve Hjønnevåg
2010-05-07  3:45             ` mgross
2010-05-05 21:37   ` Brian Swetland
2010-05-05 21:12 ` Alan Stern
2010-05-14  4:11 [PATCH 0/8] Suspend block api (version 7) Arve Hjønnevåg
2010-05-14  4:11 ` [PATCH 1/8] PM: Add suspend block api Arve Hjønnevåg
2010-05-14  4:11   ` Arve Hjønnevåg
2010-05-14  6:13   ` Paul Walmsley
2010-05-14  6:27   ` Paul Walmsley
2010-05-14  7:14     ` Arve Hjønnevåg
2010-05-18  2:17       ` Paul Walmsley
2010-05-18  3:06         ` Arve Hjønnevåg
2010-05-18  3:34           ` Paul Walmsley
2010-05-18  3:51             ` Arve Hjønnevåg
2010-05-19 15:55               ` Paul Walmsley
2010-05-20  0:35                 ` Arve Hjønnevåg
2010-05-18 13:11   ` Pavel Machek
2010-05-18 13:11   ` Pavel Machek
2010-05-20  9:11     ` Florian Mickler
2010-05-20  9:26       ` Florian Mickler
2010-05-20  9:26       ` Florian Mickler
2010-05-20 22:18         ` Rafael J. Wysocki
2010-05-20 22:18         ` Rafael J. Wysocki
2010-05-21  6:04           ` Florian Mickler
2010-05-21  6:04           ` Florian Mickler
2010-05-27 15:41           ` Pavel Machek
2010-05-27 15:41           ` Pavel Machek
2010-05-20  9:11     ` Florian Mickler

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.