All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH 00/11] Android PM extensions (version 3)
@ 2009-02-11  1:49 Arve Hjønnevåg
  2009-02-11  1:49 ` [PATCH 01/10] PM: Add wake lock api Arve Hjønnevåg
  2009-02-17 21:05 ` [RFC][PATCH 00/11] Android PM extensions (version 3) Pavel Machek
  0 siblings, 2 replies; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland

The following patch series adds two apis, wakelock and earlysuspend.
The Android platform uses the earlysuspend api to turn the screen
and some input devices on and off. The wakelock code determines when
to enter the full suspend state.

These apis could also be useful to other platforms where the goal is
to enter full suspend whenever possible.

Changes since the last version:
- The separation between the api and implementation has been removed.
- The user space wakelock api moved from sysfs to a device.
- Moved more wakelock stats code out of the main functions.
- Some other requested cleanup.

--
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] 34+ messages in thread

* [PATCH 01/10] PM: Add wake lock api.
  2009-02-11  1:49 [RFC][PATCH 00/11] Android PM extensions (version 3) Arve Hjønnevåg
@ 2009-02-11  1:49 ` Arve Hjønnevåg
  2009-02-11  1:49   ` [PATCH 02/10] PM: wakelock: Override wakelocks when not using /sys/power/request_state Arve Hjønnevåg
  2009-02-12 22:00   ` [PATCH 01/10] PM: Add wake lock api mark gross
  2009-02-17 21:05 ` [RFC][PATCH 00/11] Android PM extensions (version 3) Pavel Machek
  1 sibling, 2 replies; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland

Adds /sys/power/request_state, a non-blocking interface that specifies
which suspend state to enter when no wakelocks are locked. A special
state, "on", stops the process by locking the "main" wakelock.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/wakelocks.txt |   80 +++++
 include/linux/wakelock.h          |   93 +++++
 kernel/power/Kconfig              |   17 +
 kernel/power/Makefile             |    1 +
 kernel/power/main.c               |   63 ++++
 kernel/power/power.h              |    7 +
 kernel/power/wakelock.c           |  664 +++++++++++++++++++++++++++++++++++++
 7 files changed, 925 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/power/wakelocks.txt
 create mode 100755 include/linux/wakelock.h
 create mode 100644 kernel/power/wakelock.c

diff --git a/Documentation/power/wakelocks.txt b/Documentation/power/wakelocks.txt
new file mode 100644
index 0000000..893a438
--- /dev/null
+++ b/Documentation/power/wakelocks.txt
@@ -0,0 +1,80 @@
+Wakelocks
+=========
+
+A locked wakelock, depending on its type, prevents the system from entering
+suspend or other low-power states. When creating a wakelock, you can select
+if it prevents suspend or low-power idle states.  If the type is set to
+WAKE_LOCK_SUSPEND, the wakelock prevents a full system suspend. If the type
+is WAKE_LOCK_IDLE, low-power states that cause large interrupt latencies, or
+that disable a set of interrupts, will not be entered from idle until the
+wakelocks are released. Unless the type is specified, this document refers
+to wakelocks with the type set to WAKE_LOCK_SUSPEND.
+
+If the suspend operation has already started when locking a wakelock, it will
+abort the suspend operation as long it has not already reached the suspend_late
+stage. This means that locking a wakelock from an interrupt handler or a
+freezeable thread always works, but if you lock a wakelock from a suspend_late
+handler you must also return an error from that handler to abort suspend.
+
+Wakelocks 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 locks the keypad-scan wakelock
+  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 locks
+  the input-event-queue wakelock.
+- The keypad-scan code detects that no keys are held and unlocks the
+  keypad-scan wakelock.
+- The user-space input-event thread returns from select/poll, locks the
+  process-input-events wakelock and then calls read in the input-event device.
+- The input-event driver dequeues the key-event and, since the queue is now
+  empty, it unlocks the input-event-queue wakelock.
+- The user-space input-event thread returns from read. It determines that the
+  key should not wake up the full system, releases the process-input-events
+  wakelock and calls select or poll.
+
+                 Key pressed   Key released
+                     |             |
+keypad-scan          ++++++++++++++++++
+input-event-queue        +++          +++
+process-input-events       +++          +++
+
+
+Driver API
+==========
+
+A driver can use the wakelock api by adding a wakelock variable to its state
+and calling wake_lock_init. For instance:
+struct state {
+	struct wakelock wakelock;
+}
+
+init() {
+	wake_lock_init(&state->wakelock, WAKE_LOCK_SUSPEND, "wakelockname");
+}
+
+Before freeing the memory, wake_lock_destroy must be called:
+
+uninit() {
+	wake_lock_destroy(&state->wakelock);
+}
+
+When the driver determines that it needs to run (usually in an interrupt
+handler) it calls wake_lock:
+	wake_lock(&state->wakelock);
+
+When it no longer needs to run it calls wake_unlock:
+	wake_unlock(&state->wakelock);
+
+It can also call wake_lock_timeout to release the wakelock after a delay:
+	wake_lock_timeout(&state->wakelock, HZ);
+
+This works whether the wakelock is already held or not. It is useful if the
+driver woke up other parts of the system that do not use wakelocks 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.
+
diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h
new file mode 100755
index 0000000..ec878d2
--- /dev/null
+++ b/include/linux/wakelock.h
@@ -0,0 +1,93 @@
+/* include/linux/wakelock.h
+ *
+ * Copyright (C) 2007-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.
+ *
+ */
+
+#ifndef _LINUX_WAKELOCK_H
+#define _LINUX_WAKELOCK_H
+
+#include <linux/list.h>
+#include <linux/ktime.h>
+
+/* A wake_lock prevents the system from entering suspend or other low power
+ * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock
+ * prevents a full system suspend. If the type is WAKE_LOCK_IDLE, low power
+ * states that cause large interrupt latencies or that disable a set of
+ * interrupts will not entered from idle until the wake_locks are released.
+ */
+
+enum {
+	WAKE_LOCK_SUSPEND, /* Prevent suspend */
+	WAKE_LOCK_IDLE,    /* Prevent low power idle */
+	WAKE_LOCK_TYPE_COUNT
+};
+
+struct wake_lock {
+#ifdef CONFIG_WAKELOCK
+	struct list_head    link;
+	int                 flags;
+	const char         *name;
+	unsigned long       expires;
+#ifdef CONFIG_WAKELOCK_STAT
+	struct {
+		int             count;
+		int             expire_count;
+		int             wakeup_count;
+		ktime_t         total_time;
+		ktime_t         prevent_suspend_time;
+		ktime_t         max_time;
+		ktime_t         last_time;
+	} stat;
+#endif
+#endif
+};
+
+#ifdef CONFIG_WAKELOCK
+
+void wake_lock_init(struct wake_lock *lock, int type, const char *name);
+void wake_lock_destroy(struct wake_lock *lock);
+void wake_lock(struct wake_lock *lock);
+void wake_lock_timeout(struct wake_lock *lock, long timeout);
+void wake_unlock(struct wake_lock *lock);
+
+/* wake_lock_active returns a non-zero value if the wake_lock is currently
+ * locked. If the wake_lock 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 0.
+ */
+int wake_lock_active(struct wake_lock *lock);
+
+/* has_wake_lock can be used by generic power management code to abort suspend.
+ * has_wake_lock returns 0 if no wake locks of the specified type are active,
+ * and non-zero if one or more wake locks are held. Specifically it returns
+ * -1 if one or more wake locks with no timeout are active or the
+ * number of jiffies until all active wake locks time out.
+ */
+long has_wake_lock(int type);
+
+#else
+
+static inline void wake_lock_init(struct wake_lock *lock, int type,
+					const char *name) {}
+static inline void wake_lock_destroy(struct wake_lock *lock) {}
+static inline void wake_lock(struct wake_lock *lock) {}
+static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) {}
+static inline void wake_unlock(struct wake_lock *lock) {}
+
+static inline int wake_lock_active(struct wake_lock *lock) { return 0; }
+static inline long has_wake_lock(int type) { return 0; }
+
+#endif
+
+#endif
+
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 23bd4da..9abd97e 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -116,6 +116,23 @@ config SUSPEND_FREEZER
 
 	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
 
+config WAKELOCK
+	bool "Wake lock"
+	depends on PM
+	select RTC_LIB
+	default n
+	---help---
+	  Enable wakelocks. When user space requests a sleep state through
+	  /sys/power/request_state, the requested sleep state will be entered
+	  when no wake locks are held.
+
+config WAKELOCK_STAT
+	bool "Wake lock stats"
+	depends on WAKELOCK
+	default y
+	---help---
+	  Report wake lock stats in /proc/wakelocks
+
 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 d7a1016..8d8672b 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -6,6 +6,7 @@ endif
 obj-y				:= main.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
+obj-$(CONFIG_WAKELOCK)		+= wakelock.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 2399888..8bdd15a 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/wakelock.h>
 
 #include "power.h"
 
@@ -388,6 +389,9 @@ static void suspend_finish(void)
 
 
 static const char * const pm_states[PM_SUSPEND_MAX] = {
+#ifdef CONFIG_WAKELOCK
+	[PM_SUSPEND_ON]		= "on",
+#endif
 	[PM_SUSPEND_STANDBY]	= "standby",
 	[PM_SUSPEND_MEM]	= "mem",
 };
@@ -536,6 +540,62 @@ 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
+ *	requsted. Wakelocks are respected and the requested state will only
+ *	be entered when no wakelocks are held. Write "on" to cancel.
+ *
+ *	If CONFIG_EARLYSUSPEND is set, early_suspend hooks are called when
+ *	the requested state changes to or from "on"
+ */
+
+#ifdef CONFIG_WAKELOCK
+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_WAKELOCK */
+
 #ifdef CONFIG_PM_TRACE
 int pm_trace_enabled;
 
@@ -563,6 +623,9 @@ power_attr(pm_trace);
 
 static struct attribute * g[] = {
 	&state_attr.attr,
+#ifdef CONFIG_WAKELOCK
+	&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..9468679 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -223,3 +223,10 @@ static inline void suspend_thaw_processes(void)
 {
 }
 #endif
+
+#ifdef CONFIG_WAKELOCK
+/* kernel/power/wakelock.c */
+extern struct workqueue_struct *suspend_work_queue;
+extern struct wake_lock main_wake_lock;
+void request_suspend_state(suspend_state_t state);
+#endif
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
new file mode 100644
index 0000000..13f5d03
--- /dev/null
+++ b/kernel/power/wakelock.c
@@ -0,0 +1,664 @@
+/* kernel/power/wakelock.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/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/suspend.h>
+#include <linux/syscalls.h> /* sys_sync */
+#include <linux/wakelock.h>
+#ifdef CONFIG_WAKELOCK_STAT
+#include <linux/proc_fs.h>
+#endif
+#include "power.h"
+
+enum {
+	DEBUG_EXIT_SUSPEND = 1U << 0,
+	DEBUG_WAKEUP = 1U << 1,
+	DEBUG_USER_STATE = 1U << 2,
+	DEBUG_SUSPEND = 1U << 3,
+	DEBUG_EXPIRE = 1U << 4,
+	DEBUG_WAKE_LOCK = 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 WAKE_LOCK_TYPE_MASK              (0x0f)
+#define WAKE_LOCK_INITIALIZED            (1U << 8)
+#define WAKE_LOCK_ACTIVE                 (1U << 9)
+#define WAKE_LOCK_AUTO_EXPIRE            (1U << 10)
+#define WAKE_LOCK_PREVENTING_SUSPEND     (1U << 11)
+
+static DEFINE_SPINLOCK(list_lock);
+static DEFINE_SPINLOCK(state_lock);
+static LIST_HEAD(inactive_locks);
+static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];
+static int current_event_num;
+struct workqueue_struct *suspend_work_queue;
+struct wake_lock main_wake_lock;
+static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
+static struct wake_lock unknown_wakeup;
+
+#ifdef CONFIG_WAKELOCK_STAT
+static struct wake_lock deleted_wake_locks;
+static ktime_t last_sleep_time_update;
+static bool wait_for_wakeup;
+
+bool stats_get_expired_time(struct wake_lock *lock, ktime_t *expire_time)
+{
+	struct timespec ts;
+	struct timespec kt;
+	struct timespec tomono;
+	struct timespec delta;
+	unsigned long seq;
+	long timeout;
+
+	if (!(lock->flags & WAKE_LOCK_AUTO_EXPIRE))
+		return false;
+	do {
+		seq = read_seqbegin(&xtime_lock);
+		timeout = lock->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_lock_stat(char *buf, struct wake_lock *lock)
+{
+	int lock_count = lock->stat.count;
+	int expire_count = lock->stat.expire_count;
+	ktime_t active_time = ktime_set(0, 0);
+	ktime_t total_time = lock->stat.total_time;
+	ktime_t max_time = lock->stat.max_time;
+	ktime_t prevent_suspend_time = lock->stat.prevent_suspend_time;
+	if (lock->flags & WAKE_LOCK_ACTIVE) {
+		ktime_t now, add_time;
+		bool expired = stats_get_expired_time(lock, &now);
+		if (!expired)
+			now = ktime_get();
+		add_time = ktime_sub(now, lock->stat.last_time);
+		lock_count++;
+		if (!expired)
+			active_time = add_time;
+		else
+			expire_count++;
+		total_time = ktime_add(total_time, add_time);
+		if (lock->flags & WAKE_LOCK_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%d\t%lld\t%lld\t%lld\t%lld\t"
+		       "%lld\n", lock->name, lock_count, expire_count,
+		       lock->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(lock->stat.last_time));
+}
+
+
+static int wakelock_stats_read_proc(char *page, char **start, off_t off,
+			       int count, int *eof, void *data)
+{
+	unsigned long irqflags;
+	struct wake_lock *lock;
+	int len = 0;
+	char *p = page;
+	int type;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	p += sprintf(p, "name\tcount\texpire_count\twake_count\tactive_since"
+		     "\ttotal_time\tsleep_time\tmax_time\tlast_change\n");
+	list_for_each_entry(lock, &inactive_locks, link) {
+		p += print_lock_stat(p, lock);
+	}
+	for (type = 0; type < WAKE_LOCK_TYPE_COUNT; type++) {
+		list_for_each_entry(lock, &active_wake_locks[type], link)
+			p += print_lock_stat(p, lock);
+	}
+	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 wake_lock_stat_init_locked(struct wake_lock *lock)
+{
+	lock->stat.count = 0;
+	lock->stat.expire_count = 0;
+	lock->stat.wakeup_count = 0;
+	lock->stat.total_time = ktime_set(0, 0);
+	lock->stat.prevent_suspend_time = ktime_set(0, 0);
+	lock->stat.max_time = ktime_set(0, 0);
+	lock->stat.last_time = ktime_set(0, 0);
+}
+
+static void wake_lock_stat_destroy_locked(struct wake_lock *lock)
+{
+	if (lock->stat.count) {
+		deleted_wake_locks.stat.count += lock->stat.count;
+		deleted_wake_locks.stat.expire_count += lock->stat.expire_count;
+		deleted_wake_locks.stat.total_time =
+			ktime_add(deleted_wake_locks.stat.total_time,
+				  lock->stat.total_time);
+		deleted_wake_locks.stat.prevent_suspend_time =
+			ktime_add(deleted_wake_locks.stat.prevent_suspend_time,
+				  lock->stat.prevent_suspend_time);
+		deleted_wake_locks.stat.max_time =
+			ktime_add(deleted_wake_locks.stat.max_time,
+				  lock->stat.max_time);
+	}
+}
+
+static void wake_unlock_stat_locked(struct wake_lock *lock, bool expired)
+{
+	ktime_t duration;
+	ktime_t now;
+	if (!(lock->flags & WAKE_LOCK_ACTIVE))
+		return;
+	if (stats_get_expired_time(lock, &now))
+		expired = true;
+	else
+		now = ktime_get();
+	lock->stat.count++;
+	if (expired)
+		lock->stat.expire_count++;
+	duration = ktime_sub(now, lock->stat.last_time);
+	lock->stat.total_time = ktime_add(lock->stat.total_time, duration);
+	if (ktime_to_ns(duration) > ktime_to_ns(lock->stat.max_time))
+		lock->stat.max_time = duration;
+	lock->stat.last_time = ktime_get();
+	if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
+		duration = ktime_sub(now, last_sleep_time_update);
+		lock->stat.prevent_suspend_time = ktime_add(
+			lock->stat.prevent_suspend_time, duration);
+		lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
+	}
+}
+
+static void wake_lock_stat_locked(struct wake_lock *lock)
+{
+	int type = lock->flags & WAKE_LOCK_TYPE_MASK;
+	if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {
+		if (debug_mask & DEBUG_WAKEUP)
+			pr_info("wakeup wake lock: %s\n", lock->name);
+		wait_for_wakeup = false;
+		lock->stat.wakeup_count++;
+	}
+	if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
+	    time_is_before_eq_jiffies(lock->expires)) {
+		wake_unlock_stat_locked(lock, false);
+		lock->stat.last_time = ktime_get();
+	}
+
+	if (!(lock->flags & WAKE_LOCK_ACTIVE))
+		lock->stat.last_time = ktime_get();
+}
+
+static void update_sleep_wait_stats_locked(bool done)
+{
+	struct wake_lock *lock;
+	ktime_t now, etime, elapsed, add;
+	bool expired;
+
+	now = ktime_get();
+	elapsed = ktime_sub(now, last_sleep_time_update);
+	list_for_each_entry(lock, &active_wake_locks[WAKE_LOCK_SUSPEND], link) {
+		expired = stats_get_expired_time(lock, &etime);
+		if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
+			if (expired)
+				add = ktime_sub(etime, last_sleep_time_update);
+			else
+				add = elapsed;
+			lock->stat.prevent_suspend_time = ktime_add(
+				lock->stat.prevent_suspend_time, add);
+		}
+		if (done || expired)
+			lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
+		else
+			lock->flags |= WAKE_LOCK_PREVENTING_SUSPEND;
+	}
+	last_sleep_time_update = now;
+}
+
+#else
+
+static inline void wake_lock_stat_init_locked(struct wake_lock *lock) {}
+static inline void wake_lock_stat_destroy_locked(struct wake_lock *lock) {}
+static inline void wake_lock_stat_locked(struct wake_lock *lock) {}
+static inline void wake_unlock_stat_locked(struct wake_lock *lock,
+					   bool expired) {}
+static inline void update_sleep_wait_stats_locked(bool done) {}
+
+#endif
+
+
+static void expire_wake_lock(struct wake_lock *lock)
+{
+	wake_unlock_stat_locked(lock, true);
+	lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
+	list_del(&lock->link);
+	list_add(&lock->link, &inactive_locks);
+	if (debug_mask & (DEBUG_WAKE_LOCK | DEBUG_EXPIRE))
+		pr_info("expired wake lock %s\n", lock->name);
+}
+
+static void print_active_locks(int type)
+{
+	unsigned long irqflags;
+	struct wake_lock *lock;
+
+	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
+	spin_lock_irqsave(&list_lock, irqflags);
+	list_for_each_entry(lock, &active_wake_locks[type], link) {
+		if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
+			long timeout = lock->expires - jiffies;
+			if (timeout <= 0)
+				pr_info("wake lock %s, expired\n", lock->name);
+			else
+				pr_info("active wake lock %s, time left %ld\n",
+					lock->name, timeout);
+		} else
+			pr_info("active wake lock %s\n", lock->name);
+	}
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+
+static long has_wake_lock_locked(int type)
+{
+	struct wake_lock *lock, *n;
+	long max_timeout = 0;
+
+	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
+	list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
+		if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
+			long timeout = lock->expires - jiffies;
+			if (timeout <= 0)
+				expire_wake_lock(lock);
+			else if (timeout > max_timeout)
+				max_timeout = timeout;
+		} else
+			return -1;
+	}
+	return max_timeout;
+}
+
+long has_wake_lock(int type)
+{
+	long ret;
+	unsigned long irqflags;
+	spin_lock_irqsave(&list_lock, irqflags);
+	ret = has_wake_lock_locked(type);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+	return ret;
+}
+
+static void suspend_worker(struct work_struct *work)
+{
+	int ret;
+	int entry_event_num;
+
+	if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("suspend: abort suspend\n");
+		return;
+	}
+
+	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) {
+		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 (current_event_num == entry_event_num) {
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("suspend: pm_suspend returned with no event\n");
+		wake_lock_timeout(&unknown_wakeup, HZ / 2);
+	}
+}
+static DECLARE_WORK(suspend_work, suspend_worker);
+
+static void expire_wake_locks(unsigned long data)
+{
+	long has_lock;
+	unsigned long irqflags;
+	if (debug_mask & DEBUG_EXPIRE)
+		pr_info("expire_wake_locks: start\n");
+	if (debug_mask & DEBUG_SUSPEND)
+		print_active_locks(WAKE_LOCK_SUSPEND);
+	spin_lock_irqsave(&list_lock, irqflags);
+	has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);
+	if (debug_mask & DEBUG_EXPIRE)
+		pr_info("expire_wake_locks: done, has_lock %ld\n", has_lock);
+	if (has_lock == 0)
+		queue_work(suspend_work_queue, &suspend_work);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
+
+static int power_suspend_late(struct platform_device *pdev, pm_message_t state)
+{
+	int ret = has_wake_lock(WAKE_LOCK_SUSPEND) ? -EAGAIN : 0;
+#ifdef CONFIG_WAKELOCK_STAT
+	wait_for_wakeup = true;
+#endif
+	if (debug_mask & DEBUG_SUSPEND)
+		pr_info("power_suspend_late return %d\n", ret);
+	return ret;
+}
+
+static struct platform_driver power_driver = {
+	.driver.name = "power",
+	.suspend_late = power_suspend_late,
+};
+static struct platform_device power_device = {
+	.name = "power",
+};
+
+/**
+ * wake_lock_init() - Initialize a wakelock
+ * @lock:	The wakelock to initialize.
+ * @type:	Type of wakelock, e.g. WAKE_LOCK_SUSPEND
+ * @name:	The name of the wakelock to show in /proc/wakelocks
+ */
+void wake_lock_init(struct wake_lock *lock, int type, const char *name)
+{
+	unsigned long irqflags = 0;
+
+	if (name)
+		lock->name = name;
+	BUG_ON(!lock->name);
+
+	if (debug_mask & DEBUG_WAKE_LOCK)
+		pr_info("wake_lock_init name=%s\n", lock->name);
+
+	lock->flags = (type & WAKE_LOCK_TYPE_MASK) | WAKE_LOCK_INITIALIZED;
+	INIT_LIST_HEAD(&lock->link);
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	wake_lock_stat_init_locked(lock);
+	list_add(&lock->link, &inactive_locks);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(wake_lock_init);
+
+/**
+ * wake_lock_destroy() - Destroy a wakelock
+ * @lock:       The wakelock to destroy.
+ */
+void wake_lock_destroy(struct wake_lock *lock)
+{
+	unsigned long irqflags;
+	if (debug_mask & DEBUG_WAKE_LOCK)
+		pr_info("wake_lock_destroy name=%s\n", lock->name);
+	spin_lock_irqsave(&list_lock, irqflags);
+	wake_lock_stat_destroy_locked(lock);
+	lock->flags &= ~WAKE_LOCK_INITIALIZED;
+	list_del(&lock->link);
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(wake_lock_destroy);
+
+static void __wake_lock(struct wake_lock *lock, long timeout, bool has_timeout)
+{
+	int type;
+	unsigned long irqflags;
+	long expire_in;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+	type = lock->flags & WAKE_LOCK_TYPE_MASK;
+	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
+	BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));
+
+	wake_lock_stat_locked(lock);
+	lock->flags |= WAKE_LOCK_ACTIVE;
+	list_del(&lock->link);
+	if (has_timeout) {
+		if (debug_mask & DEBUG_WAKE_LOCK)
+			pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",
+				lock->name, type, timeout / HZ,
+				(timeout % HZ) * MSEC_PER_SEC / HZ);
+		lock->expires = jiffies + timeout;
+		lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
+		list_add_tail(&lock->link, &active_wake_locks[type]);
+	} else {
+		if (debug_mask & DEBUG_WAKE_LOCK)
+			pr_info("wake_lock: %s, type %d\n", lock->name, type);
+		lock->expires = LONG_MAX;
+		lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
+		/* Add to head so has_wake_lock only has to examine one entry */
+		list_add(&lock->link, &active_wake_locks[type]);
+	}
+	if (type == WAKE_LOCK_SUSPEND) {
+		current_event_num++;
+		if (lock == &main_wake_lock)
+			update_sleep_wait_stats_locked(true);
+		else if (!wake_lock_active(&main_wake_lock))
+			update_sleep_wait_stats_locked(false);
+		expire_in = has_timeout ? has_wake_lock_locked(type) : -1;
+		if (expire_in > 0) {
+			if (debug_mask & DEBUG_EXPIRE)
+				pr_info("wake_lock: %s, start expire timer, "
+					"%ld\n", lock->name, expire_in);
+			mod_timer(&expire_timer, jiffies + expire_in);
+		} else {
+			if (del_timer(&expire_timer))
+				if (debug_mask & DEBUG_EXPIRE)
+					pr_info("wake_lock: %s, stop expire "
+						"timer\n", lock->name);
+			if (expire_in == 0)
+				queue_work(suspend_work_queue, &suspend_work);
+		}
+	}
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+
+/**
+ * wake_lock() - Lock a wakelock
+ * @lock:       The wakelock to lock.
+ */
+void wake_lock(struct wake_lock *lock)
+{
+	__wake_lock(lock, 0, false);
+}
+EXPORT_SYMBOL(wake_lock);
+
+/**
+ * wake_lock_timeout() - Lock a wakelock with auto-unlock timeout
+ * @lock:       The wakelock to lock.
+ * @timeout:    Timeout in jiffies before the lock auto-unlock
+ */
+void wake_lock_timeout(struct wake_lock *lock, long timeout)
+{
+	__wake_lock(lock, timeout, true);
+}
+EXPORT_SYMBOL(wake_lock_timeout);
+
+/**
+ * wake_lock() - Unlock a wakelock
+ * @lock:       The wakelock to unlock.
+ */
+void wake_unlock(struct wake_lock *lock)
+{
+	int type;
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&list_lock, irqflags);
+
+	wake_unlock_stat_locked(lock, false);
+
+	if (debug_mask & DEBUG_WAKE_LOCK)
+		pr_info("wake_unlock: %s\n", lock->name);
+	lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
+	list_del(&lock->link);
+	list_add(&lock->link, &inactive_locks);
+
+	type = lock->flags & WAKE_LOCK_TYPE_MASK;
+	if (type == WAKE_LOCK_SUSPEND) {
+		long has_lock = has_wake_lock_locked(type);
+		if (has_lock > 0) {
+			if (debug_mask & DEBUG_EXPIRE)
+				pr_info("wake_unlock: %s, start expire timer, "
+					"%ld\n", lock->name, has_lock);
+			mod_timer(&expire_timer, jiffies + has_lock);
+		} else {
+			if (del_timer(&expire_timer))
+				if (debug_mask & DEBUG_EXPIRE)
+					pr_info("wake_unlock: %s, stop expire "
+						"timer\n", lock->name);
+			if (has_lock == 0)
+				queue_work(suspend_work_queue, &suspend_work);
+		}
+		if (lock == &main_wake_lock) {
+			if (debug_mask & DEBUG_SUSPEND)
+				print_active_locks(WAKE_LOCK_SUSPEND);
+			update_sleep_wait_stats_locked(false);
+		}
+	}
+	spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(wake_unlock);
+
+/**
+ * wake_lock_active() - Test if a wakelock is locked
+ * @lock:       The wakelock to check.
+ */
+int wake_lock_active(struct wake_lock *lock)
+{
+	return !!(lock->flags & WAKE_LOCK_ACTIVE);
+}
+EXPORT_SYMBOL(wake_lock_active);
+
+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)
+		wake_lock(&main_wake_lock);
+	else
+		wake_unlock(&main_wake_lock);
+	spin_unlock_irqrestore(&state_lock, irqflags);
+}
+
+static int __init wakelocks_init(void)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
+		INIT_LIST_HEAD(&active_wake_locks[i]);
+
+#ifdef CONFIG_WAKELOCK_STAT
+	wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,
+			"deleted_wake_locks");
+#endif
+	wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
+	wake_lock(&main_wake_lock);
+	wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups");
+
+	ret = platform_device_register(&power_device);
+	if (ret) {
+		pr_err("wakelocks_init: platform_device_register failed\n");
+		goto err_platform_device_register;
+	}
+	ret = platform_driver_register(&power_driver);
+	if (ret) {
+		pr_err("wakelocks_init: platform_driver_register failed\n");
+		goto err_platform_driver_register;
+	}
+
+	suspend_work_queue = create_singlethread_workqueue("suspend");
+	if (suspend_work_queue == NULL) {
+		ret = -ENOMEM;
+		goto err_suspend_work_queue;
+	}
+
+#ifdef CONFIG_WAKELOCK_STAT
+	create_proc_read_entry("wakelocks", S_IRUGO, NULL,
+				wakelock_stats_read_proc, NULL);
+#endif
+
+	return 0;
+
+err_suspend_work_queue:
+	platform_driver_unregister(&power_driver);
+err_platform_driver_register:
+	platform_device_unregister(&power_device);
+err_platform_device_register:
+	wake_lock_destroy(&unknown_wakeup);
+	wake_lock_destroy(&main_wake_lock);
+#ifdef CONFIG_WAKELOCK_STAT
+	wake_lock_destroy(&deleted_wake_locks);
+#endif
+	return ret;
+}
+
+static void  __exit wakelocks_exit(void)
+{
+#ifdef CONFIG_WAKELOCK_STAT
+	remove_proc_entry("wakelocks", NULL);
+#endif
+	destroy_workqueue(suspend_work_queue);
+	platform_driver_unregister(&power_driver);
+	platform_device_unregister(&power_device);
+	wake_lock_destroy(&unknown_wakeup);
+	wake_lock_destroy(&main_wake_lock);
+#ifdef CONFIG_WAKELOCK_STAT
+	wake_lock_destroy(&deleted_wake_locks);
+#endif
+}
+
+core_initcall(wakelocks_init);
+module_exit(wakelocks_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] 34+ messages in thread

* [PATCH 02/10] PM: wakelock: Override wakelocks when not using /sys/power/request_state
  2009-02-11  1:49 ` [PATCH 01/10] PM: Add wake lock api Arve Hjønnevåg
@ 2009-02-11  1:49   ` Arve Hjønnevåg
  2009-02-11  1:49     ` [PATCH 03/10] PM: wakelock: Add driver to access wakelocks from user-space Arve Hjønnevåg
  2009-02-12 22:00   ` [PATCH 01/10] PM: Add wake lock api mark gross
  1 sibling, 1 reply; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland

This preserves existing functionality when CONFIG_WAKELOCK is set.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 include/linux/wakelock.h |    4 ++++
 kernel/power/wakelock.c  |   10 +++++++++-
 2 files changed, 13 insertions(+), 1 deletions(-)

diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h
index ec878d2..af74b65 100755
--- a/include/linux/wakelock.h
+++ b/include/linux/wakelock.h
@@ -72,6 +72,10 @@ int wake_lock_active(struct wake_lock *lock);
  * and non-zero if one or more wake locks are held. Specifically it returns
  * -1 if one or more wake locks with no timeout are active or the
  * number of jiffies until all active wake locks time out.
+ *
+ * To preserve backward compatibility, when the type is WAKE_LOCK_SUSPEND, this
+ * function returns 0 unless it is called during suspend initiated from the
+ * wakelock code.
  */
 long has_wake_lock(int type);
 
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
index 13f5d03..c396b58 100644
--- a/kernel/power/wakelock.c
+++ b/kernel/power/wakelock.c
@@ -49,6 +49,7 @@ static int current_event_num;
 struct workqueue_struct *suspend_work_queue;
 struct wake_lock main_wake_lock;
 static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
+static bool enable_suspend_wakelocks;
 static struct wake_lock unknown_wakeup;
 
 #ifdef CONFIG_WAKELOCK_STAT
@@ -317,6 +318,9 @@ long has_wake_lock(int type)
 {
 	long ret;
 	unsigned long irqflags;
+	if (WARN_ONCE(type == WAKE_LOCK_SUSPEND && !enable_suspend_wakelocks,
+							"ignoring wakelocks\n"))
+		return 0;
 	spin_lock_irqsave(&list_lock, irqflags);
 	ret = has_wake_lock_locked(type);
 	spin_unlock_irqrestore(&list_lock, irqflags);
@@ -328,10 +332,12 @@ static void suspend_worker(struct work_struct *work)
 	int ret;
 	int entry_event_num;
 
+	enable_suspend_wakelocks = 1;
+
 	if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
 		if (debug_mask & DEBUG_SUSPEND)
 			pr_info("suspend: abort suspend\n");
-		return;
+		goto abort;
 	}
 
 	entry_event_num = current_event_num;
@@ -353,6 +359,8 @@ static void suspend_worker(struct work_struct *work)
 			pr_info("suspend: pm_suspend returned with no event\n");
 		wake_lock_timeout(&unknown_wakeup, HZ / 2);
 	}
+abort:
+	enable_suspend_wakelocks = 0;
 }
 static DECLARE_WORK(suspend_work, suspend_worker);
 
-- 
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] 34+ messages in thread

* [PATCH 03/10] PM: wakelock: Add driver to access wakelocks from user-space
  2009-02-11  1:49   ` [PATCH 02/10] PM: wakelock: Override wakelocks when not using /sys/power/request_state Arve Hjønnevåg
@ 2009-02-11  1:49     ` Arve Hjønnevåg
  2009-02-11  1:49       ` [PATCH 04/10] PM: wakelock: Abort task freezing if a wakelock is locked Arve Hjønnevåg
  0 siblings, 1 reply; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland

Adds ioctls to create a wakelock, and to lock and unlock the wakelock.
To delete the wakelock, close the device.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/wakelocks.txt |   24 ++++++
 include/linux/wakelock-dev.h      |   26 +++++++
 kernel/power/Kconfig              |    9 +++
 kernel/power/Makefile             |    1 +
 kernel/power/userwakelock.c       |  145 +++++++++++++++++++++++++++++++++++++
 5 files changed, 205 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/wakelock-dev.h
 create mode 100644 kernel/power/userwakelock.c

diff --git a/Documentation/power/wakelocks.txt b/Documentation/power/wakelocks.txt
index 893a438..939908f 100644
--- a/Documentation/power/wakelocks.txt
+++ b/Documentation/power/wakelocks.txt
@@ -78,3 +78,27 @@ 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.
 
+User-space API
+==============
+
+To create a wakelock from user-space, open the wakelock device:
+    fd = open("/dev/wakelock", O_RDWR | O_CLOEXEC);
+then call:
+    ioctl(fd, WAKELOCK_IOCTL_INIT(strlen(name)), name);
+
+To lock a wakelock call:
+    ioctl(fd, WAKELOCK_IOCTL_LOCK);
+or
+    ioctl(fd, WAKELOCK_IOCTL_LOCK_TIMEOUT, &timespec_timeout);
+
+To unlock call:
+    ioctl(fd, WAKELOCK_IOCTL_UNLOCK);
+
+To destroy the wakelock, 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 a wakelock held. If the process dies or
+the device is closed while the wakelock is locked, a wakelock will be held for
+the number of seconds specified.
+
diff --git a/include/linux/wakelock-dev.h b/include/linux/wakelock-dev.h
new file mode 100644
index 0000000..5dfb1c6
--- /dev/null
+++ b/include/linux/wakelock-dev.h
@@ -0,0 +1,26 @@
+/* include/linux/wakelock-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_WAKELOCK_DEV_H
+#define _LINUX_WAKELOCK_DEV_H
+
+#include <linux/ioctl.h>
+
+#define WAKELOCK_IOCTL_INIT(len)	_IOC(_IOC_WRITE,'w',0,len)
+#define WAKELOCK_IOCTL_LOCK		_IO('w', 1)
+#define WAKELOCK_IOCTL_LOCK_TIMEOUT	_IOW('w', 2, struct timespec)
+#define WAKELOCK_IOCTL_UNLOCK		_IO('w', 3)
+
+#endif
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 9abd97e..6abd5f2 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -133,6 +133,15 @@ config WAKELOCK_STAT
 	---help---
 	  Report wake lock stats in /proc/wakelocks
 
+config USER_WAKELOCK
+	bool "Userspace wake locks"
+	depends on WAKELOCK
+	default y
+	---help---
+	  User-space wake lock api. Creates a misc device with ioctls
+	  to create, lock and unlock a wakelock. The wakelock 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 8d8672b..63d30db 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-y				:= main.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_WAKELOCK)		+= wakelock.o
+obj-$(CONFIG_USER_WAKELOCK)	+= userwakelock.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/userwakelock.c b/kernel/power/userwakelock.c
new file mode 100644
index 0000000..c27c2f7
--- /dev/null
+++ b/kernel/power/userwakelock.c
@@ -0,0 +1,145 @@
+/* kernel/power/userwakelock.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/wakelock.h>
+#include <linux/wakelock-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 int unclean_exit_grace_period = 0;
+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 wake_lock unclean_exit_wake_lock;
+
+struct user_wake_lock {
+	struct wake_lock	wake_lock;
+	char			name[0];
+};
+
+static int create_user_wake_lock(struct file *file, void __user *name,
+				 size_t name_len)
+{
+	struct user_wake_lock *l;
+	if (file->private_data)
+		return -EBUSY;
+	l = kzalloc(sizeof(*l) + name_len + 1, GFP_KERNEL);
+	if (!l)
+		return -ENOMEM;
+	if (copy_from_user(l->name, name, name_len))
+		goto err_fault;
+	wake_lock_init(&l->wake_lock, WAKE_LOCK_SUSPEND, l->name);
+	file->private_data = l;
+	return 0;
+
+err_fault:
+	kfree(l);
+	return -EFAULT;
+}
+
+static long user_wakelock_ioctl(struct file *file, unsigned int cmd,
+				unsigned long _arg)
+{
+	void __user *arg = (void __user *)_arg;
+	struct user_wake_lock *l;
+	struct timespec ts;
+	unsigned long timeout;
+	long ret;
+
+	mutex_lock(&ioctl_lock);
+	if ((cmd & ~IOCSIZE_MASK) == WAKELOCK_IOCTL_INIT(0)) {
+		ret = create_user_wake_lock(file, arg, _IOC_SIZE(cmd));
+		goto done;
+	}
+	l = file->private_data;
+	if (!l) {
+		ret = -ENOENT;
+		goto done;
+	}
+	switch (cmd) {
+	case WAKELOCK_IOCTL_LOCK:
+		wake_lock(&l->wake_lock);
+		ret = 0;
+		break;
+	case WAKELOCK_IOCTL_LOCK_TIMEOUT:
+		if (copy_from_user(&ts, arg, sizeof(ts))) {
+			ret = -EFAULT;
+			goto done;
+		}
+		timeout  = timespec_to_jiffies(&ts);
+		wake_lock_timeout(&l->wake_lock, timeout);
+		ret = 0;
+		break;
+	case WAKELOCK_IOCTL_UNLOCK:
+		wake_unlock(&l->wake_lock);
+		ret = 0;
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+done:
+	if (ret && debug_mask & DEBUG_FAILURE)
+		pr_err("user_wakelock_ioctl: cmd %x failed, %ld\n", cmd, ret);
+	mutex_unlock(&ioctl_lock);
+	return ret;
+}
+
+static int user_wakelock_release(struct inode *inode, struct file *file)
+{
+	struct user_wake_lock *l = file->private_data;
+	if (!l)
+		return 0;
+	if (wake_lock_active(&l->wake_lock) && unclean_exit_grace_period)
+		wake_lock_timeout(&unclean_exit_wake_lock,
+				  unclean_exit_grace_period * HZ);
+	wake_lock_destroy(&l->wake_lock);
+	kfree(l);
+	return 0;
+}
+
+const struct file_operations user_wakelock_fops = {
+	.release = user_wakelock_release,
+	.unlocked_ioctl = user_wakelock_ioctl,
+};
+
+struct miscdevice user_wakelock_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "wakelock",
+	.fops = &user_wakelock_fops,
+};
+
+static int __init user_wakelock_init(void)
+{
+	wake_lock_init(&unclean_exit_wake_lock, WAKE_LOCK_SUSPEND,
+		       "user-unclean-exit");
+	return misc_register(&user_wakelock_device);
+}
+
+static void __exit user_wakelock_exit(void)
+{
+	misc_deregister(&user_wakelock_device);
+	wake_lock_destroy(&unclean_exit_wake_lock);
+}
+
+module_init(user_wakelock_init);
+module_exit(user_wakelock_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] 34+ messages in thread

* [PATCH 04/10] PM: wakelock: Abort task freezing if a wakelock is locked.
  2009-02-11  1:49     ` [PATCH 03/10] PM: wakelock: Add driver to access wakelocks from user-space Arve Hjønnevåg
@ 2009-02-11  1:49       ` Arve Hjønnevåg
  2009-02-11  1:49         ` [PATCH 05/10] PM: Add option to disable /sys/power/state interface Arve Hjønnevåg
  0 siblings, 1 reply; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland

If a wakelock is locked, suspend will fail anyway. Since try_to_freeze_tasks
can take up to 20 seconds to complete or fail, aborting as soon as someone
locks a wakelock (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 |   22 +++++++++++++++++-----
 1 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/kernel/power/process.c b/kernel/power/process.c
index ca63401..8388d01 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/wakelock.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 && has_wake_lock(WAKE_LOCK_SUSPEND)) {
+			wakeup = 1;
+			break;
+		}
 		if (time_after(jiffies, end_time))
 			break;
 	} while (todo);
@@ -77,11 +83,17 @@ 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] 34+ messages in thread

* [PATCH 05/10] PM: Add option to disable /sys/power/state interface
  2009-02-11  1:49       ` [PATCH 04/10] PM: wakelock: Abort task freezing if a wakelock is locked Arve Hjønnevåg
@ 2009-02-11  1:49         ` Arve Hjønnevåg
  2009-02-11  1:49           ` [PATCH 06/10] PM: Add early suspend api Arve Hjønnevåg
  0 siblings, 1 reply; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 kernel/power/Kconfig |    9 +++++++++
 kernel/power/main.c  |    4 ++++
 2 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 6abd5f2..e784014 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -133,6 +133,15 @@ config WAKELOCK_STAT
 	---help---
 	  Report wake lock stats in /proc/wakelocks
 
+config DISABLE_SYS_POWER_STATE
+	bool "Disable /sys/power/state interface"
+	depends on WAKELOCK
+	default n
+	---help---
+	  The /sys/power/state interface does not respect wakelocks. If you
+	  want to run user-space code that does not support wakelocks, do not
+	  enable this option since it removes the interface.
+
 config USER_WAKELOCK
 	bool "Userspace wake locks"
 	depends on WAKELOCK
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 8bdd15a..f22321d 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -483,6 +483,7 @@ struct kobject *power_kobj;
  *	proper enumerated value, and initiates a suspend transition.
  */
 
+#ifndef CONFIG_DISABLE_SYS_POWER_STATE
 static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 			  char *buf)
 {
@@ -539,6 +540,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 }
 
 power_attr(state);
+#endif
 
 /**
  *	request_state - control system power state.
@@ -622,7 +624,9 @@ power_attr(pm_trace);
 #endif /* CONFIG_PM_TRACE */
 
 static struct attribute * g[] = {
+#ifndef CONFIG_DISABLE_SYS_POWER_STATE
 	&state_attr.attr,
+#endif
 #ifdef CONFIG_WAKELOCK
 	&request_state_attr.attr,
 #endif
-- 
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] 34+ messages in thread

* [PATCH 06/10] PM: Add early suspend api.
  2009-02-11  1:49         ` [PATCH 05/10] PM: Add option to disable /sys/power/state interface Arve Hjønnevåg
@ 2009-02-11  1:49           ` Arve Hjønnevåg
  2009-02-11  1:49             ` [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes Arve Hjønnevåg
  2009-02-12 11:34             ` [PATCH 06/10] PM: Add early suspend api Matthew Garrett
  0 siblings, 2 replies; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland

If early-suspend support is enabled, early suspend handlers will be called
when a state other than "on" is written to /sys/power/request_state before
the unlocking "main" wakelock. Writing "on", locks the "main" wakelock and
calls the early-suspend resume handlers.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 Documentation/power/early-suspend.txt |   26 +++++
 include/linux/earlysuspend.h          |   55 ++++++++++
 kernel/power/Kconfig                  |    8 ++
 kernel/power/Makefile                 |    1 +
 kernel/power/earlysuspend.c           |  174 +++++++++++++++++++++++++++++++++
 kernel/power/power.h                  |    5 +
 kernel/power/wakelock.c               |    5 +
 7 files changed, 274 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/power/early-suspend.txt
 create mode 100755 include/linux/earlysuspend.h
 create mode 100644 kernel/power/earlysuspend.c

diff --git a/Documentation/power/early-suspend.txt b/Documentation/power/early-suspend.txt
new file mode 100644
index 0000000..8286d3a
--- /dev/null
+++ b/Documentation/power/early-suspend.txt
@@ -0,0 +1,26 @@
+Early-suspend
+=============
+
+The early-suspend api allows drivers to get notified when user-space writes to 
+/sys/power/request_state to indicate that the user visible sleep state should 
+change. A level controls what order the handlers are called in. Suspend 
+handlers are called in low to high level order, resume handlers are called in 
+the opposite order. 
+
+Four levels are defined:
+EARLY_SUSPEND_LEVEL_BLANK_SCREEN:
+  On suspend the screen should be turned off but the framebuffer must still be
+  accessible. On resume the screen can be turned back on.
+
+EARLY_SUSPEND_LEVEL_STOP_DRAWING:
+  On suspend this level notifies user-space that it should stop accessing the 
+  framebuffer and it waits for it to complete. On resume it notifies user-space 
+  that it should resume screen access.
+  Two methods are provided, console switch or a sysfs interface.
+
+EARLY_SUSPEND_LEVEL_DISABLE_FB:
+  Turn off the framebuffer on suspend and back on on resume.
+
+EARLY_SUSPEND_LEVEL_STOP_INPUT:
+  On suspend turn off input devices that are not capable of wakeup or where
+  wakeup is disabled. On resume turn the same devices back on.
diff --git a/include/linux/earlysuspend.h b/include/linux/earlysuspend.h
new file mode 100755
index 0000000..e3d03fb
--- /dev/null
+++ b/include/linux/earlysuspend.h
@@ -0,0 +1,55 @@
+/* include/linux/earlysuspend.h
+ *
+ * Copyright (C) 2007-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.
+ *
+ */
+
+#ifndef _LINUX_EARLYSUSPEND_H
+#define _LINUX_EARLYSUSPEND_H
+
+#include <linux/list.h>
+
+/* The early_suspend structure defines suspend and resume hooks to be called
+ * when the user visible sleep state of the system changes, and a level to
+ * control the order. They can be used to turn off the screen and input
+ * devices that are not used for wakeup.
+ * Suspend handlers are called in low to high level order, resume handlers are
+ * called in the opposite order. If, when calling register_early_suspend,
+ * the suspend handlers have already been called without a matching call to the
+ * resume handlers, the suspend handler will be called directly from
+ * register_early_suspend. This direct call can violate the normal level order.
+ */
+enum {
+	EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
+	EARLY_SUSPEND_LEVEL_STOP_INPUT = 75,
+	EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
+	EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
+};
+struct early_suspend {
+#ifdef CONFIG_EARLYSUSPEND
+	struct list_head link;
+	int level;
+	void (*suspend)(struct early_suspend *h);
+	void (*resume)(struct early_suspend *h);
+#endif
+};
+
+#ifdef CONFIG_EARLYSUSPEND
+void register_early_suspend(struct early_suspend *handler);
+void unregister_early_suspend(struct early_suspend *handler);
+#else
+#define register_early_suspend(handler) do { } while (0)
+#define unregister_early_suspend(handler) do { } while (0)
+#endif
+
+#endif
+
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index e784014..ccc55be 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -151,6 +151,14 @@ config USER_WAKELOCK
 	  to create, lock and unlock a wakelock. The wakelock will be
 	  deleted when the device is closed.
 
+config EARLYSUSPEND
+	bool "Early suspend"
+	depends on WAKELOCK
+	default y
+	---help---
+	  Call early suspend handlers when the user requested sleep state
+	  changes.
+
 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 63d30db..d3467b3 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_WAKELOCK)		+= wakelock.o
 obj-$(CONFIG_USER_WAKELOCK)	+= userwakelock.o
+obj-$(CONFIG_EARLYSUSPEND)	+= earlysuspend.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/earlysuspend.c b/kernel/power/earlysuspend.c
new file mode 100644
index 0000000..4cdca39
--- /dev/null
+++ b/kernel/power/earlysuspend.c
@@ -0,0 +1,174 @@
+/* kernel/power/earlysuspend.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/earlysuspend.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rtc.h>
+#include <linux/syscalls.h> /* sys_sync */
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+
+#include "power.h"
+
+enum {
+	DEBUG_SUSPEND = 1U << 0,
+	DEBUG_HANDLERS = 1U << 1,
+};
+static int debug_mask;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static DEFINE_MUTEX(early_suspend_lock);
+static LIST_HEAD(early_suspend_handlers);
+static void early_suspend(struct work_struct *work);
+static void late_resume(struct work_struct *work);
+static DECLARE_WORK(early_suspend_work, early_suspend);
+static DECLARE_WORK(late_resume_work, late_resume);
+static DEFINE_SPINLOCK(state_lock);
+enum {
+	SUSPEND_REQUESTED = 0x1,
+	SUSPENDED = 0x2,
+	SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED,
+};
+static int state;
+
+void register_early_suspend(struct early_suspend *handler)
+{
+	struct list_head *pos;
+
+	mutex_lock(&early_suspend_lock);
+	list_for_each(pos, &early_suspend_handlers) {
+		struct early_suspend *e;
+		e = list_entry(pos, struct early_suspend, link);
+		if (e->level > handler->level)
+			break;
+	}
+	list_add_tail(&handler->link, pos);
+	if ((state & SUSPENDED) && handler->suspend)
+		handler->suspend(handler);
+	mutex_unlock(&early_suspend_lock);
+}
+EXPORT_SYMBOL(register_early_suspend);
+
+void unregister_early_suspend(struct early_suspend *handler)
+{
+	mutex_lock(&early_suspend_lock);
+	list_del(&handler->link);
+	mutex_unlock(&early_suspend_lock);
+}
+EXPORT_SYMBOL(unregister_early_suspend);
+
+static void early_suspend(struct work_struct *work)
+{
+	struct early_suspend *pos;
+	unsigned long irqflags;
+	int abort = 0;
+
+	mutex_lock(&early_suspend_lock);
+	spin_lock_irqsave(&state_lock, irqflags);
+	if (state == SUSPEND_REQUESTED)
+		state |= SUSPENDED;
+	else
+		abort = 1;
+	spin_unlock_irqrestore(&state_lock, irqflags);
+
+	if (abort) {
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("early_suspend: abort, state %d\n", state);
+		mutex_unlock(&early_suspend_lock);
+		goto abort;
+	}
+
+	if (debug_mask & DEBUG_SUSPEND)
+		pr_info("early_suspend: call handlers\n");
+	list_for_each_entry(pos, &early_suspend_handlers, link) {
+		if (pos->suspend) {
+			if (debug_mask & DEBUG_HANDLERS)
+				pr_info("early_suspend: call %pF\n",
+					pos->suspend);
+			pos->suspend(pos);
+			if (debug_mask & DEBUG_HANDLERS)
+				pr_info("early_suspend: %pF returned\n",
+					pos->suspend);
+		}
+	}
+	mutex_unlock(&early_suspend_lock);
+
+	if (debug_mask & DEBUG_SUSPEND)
+		pr_info("early_suspend: sync\n");
+
+	sys_sync();
+abort:
+	spin_lock_irqsave(&state_lock, irqflags);
+	if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
+		wake_unlock(&main_wake_lock);
+	spin_unlock_irqrestore(&state_lock, irqflags);
+}
+
+static void late_resume(struct work_struct *work)
+{
+	struct early_suspend *pos;
+	unsigned long irqflags;
+	int abort = 0;
+
+	mutex_lock(&early_suspend_lock);
+	spin_lock_irqsave(&state_lock, irqflags);
+	if (state == SUSPENDED)
+		state = 0; /* clear SUSPENDED */
+	else
+		abort = 1;
+	spin_unlock_irqrestore(&state_lock, irqflags);
+
+	if (abort) {
+		if (debug_mask & DEBUG_SUSPEND)
+			pr_info("late_resume: abort, state %d\n", state);
+		goto abort;
+	}
+	if (debug_mask & DEBUG_SUSPEND)
+		pr_info("late_resume: call handlers\n");
+	list_for_each_entry_reverse(pos, &early_suspend_handlers, link)
+		if (pos->resume) {
+			if (debug_mask & DEBUG_HANDLERS)
+				pr_info("early_suspend: call %pF\n",
+					pos->resume);
+			pos->resume(pos);
+			if (debug_mask & DEBUG_HANDLERS)
+				pr_info("early_suspend: %pF returned\n",
+					pos->resume);
+		}
+	if (debug_mask & DEBUG_SUSPEND)
+		pr_info("late_resume: done\n");
+abort:
+	mutex_unlock(&early_suspend_lock);
+}
+
+void request_early_suspend_state(bool on)
+{
+	unsigned long irqflags;
+	int old_sleep;
+
+	spin_lock_irqsave(&state_lock, irqflags);
+	old_sleep = state & SUSPEND_REQUESTED;
+	if (!old_sleep && !on) {
+		state |= SUSPEND_REQUESTED;
+		queue_work(suspend_work_queue, &early_suspend_work);
+	} else if (old_sleep && on) {
+		state &= ~SUSPEND_REQUESTED;
+		wake_lock(&main_wake_lock);
+		queue_work(suspend_work_queue, &late_resume_work);
+	}
+	spin_unlock_irqrestore(&state_lock, irqflags);
+}
+
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 9468679..7d8538d 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -230,3 +230,8 @@ extern struct workqueue_struct *suspend_work_queue;
 extern struct wake_lock main_wake_lock;
 void request_suspend_state(suspend_state_t state);
 #endif
+
+#ifdef CONFIG_EARLYSUSPEND
+/* kernel/power/earlysuspend.c */
+void request_early_suspend_state(bool on);
+#endif
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
index c396b58..5048ad3 100644
--- a/kernel/power/wakelock.c
+++ b/kernel/power/wakelock.c
@@ -593,10 +593,15 @@ void request_suspend_state(suspend_state_t state)
 			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
 	}
 	requested_suspend_state = state;
+
+#ifdef CONFIG_EARLYSUSPEND
+	request_early_suspend_state(state == PM_SUSPEND_ON);
+#else
 	if (state == PM_SUSPEND_ON)
 		wake_lock(&main_wake_lock);
 	else
 		wake_unlock(&main_wake_lock);
+#endif
 	spin_unlock_irqrestore(&state_lock, irqflags);
 }
 
-- 
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] 34+ messages in thread

* [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes.
  2009-02-11  1:49           ` [PATCH 06/10] PM: Add early suspend api Arve Hjønnevåg
@ 2009-02-11  1:49             ` Arve Hjønnevåg
  2009-02-11  1:49               ` [PATCH 08/10] PM: earlysuspend: Removing dependence on console Arve Hjønnevåg
  2009-02-12 11:28               ` [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes Matthew Garrett
  2009-02-12 11:34             ` [PATCH 06/10] PM: Add early suspend api Matthew Garrett
  1 sibling, 2 replies; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland

When early-suspend is enabled, the framebuffer is turned off at
early-suspend. If CONSOLE_EARLYSUSPEND is enabled, a console switch
is used to notify user space that it should stop drawing before
the framebuffer is disabled, and that it should repaint and resume
drawing after the framebuffer is turned back on.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 kernel/power/Kconfig               |   17 ++++++++
 kernel/power/Makefile              |    1 +
 kernel/power/consoleearlysuspend.c |   78 ++++++++++++++++++++++++++++++++++++
 3 files changed, 96 insertions(+), 0 deletions(-)
 create mode 100644 kernel/power/consoleearlysuspend.c

diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index ccc55be..04e6fad 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -159,6 +159,23 @@ config EARLYSUSPEND
 	  Call early suspend handlers when the user requested sleep state
 	  changes.
 
+choice
+	prompt "User-space screen access"
+	default CONSOLE_EARLYSUSPEND
+	depends on EARLYSUSPEND
+
+	config NO_USER_SPACE_SCREEN_ACCESS_CONTROL
+		bool "None"
+
+	config CONSOLE_EARLYSUSPEND
+		bool "Console switch on early-suspend"
+		depends on EARLYSUSPEND && VT
+		---help---
+		  Register early suspend handler to perform a console switch
+		  when user-space should stop drawing to the screen and a switch
+		  back when it should resume.
+endchoice
+
 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 d3467b3..8bf293d 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_WAKELOCK)		+= wakelock.o
 obj-$(CONFIG_USER_WAKELOCK)	+= userwakelock.o
 obj-$(CONFIG_EARLYSUSPEND)	+= earlysuspend.o
+obj-$(CONFIG_CONSOLE_EARLYSUSPEND)	+= consoleearlysuspend.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/consoleearlysuspend.c b/kernel/power/consoleearlysuspend.c
new file mode 100644
index 0000000..a8befb4
--- /dev/null
+++ b/kernel/power/consoleearlysuspend.c
@@ -0,0 +1,78 @@
+/* kernel/power/consoleearlysuspend.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/console.h>
+#include <linux/earlysuspend.h>
+#include <linux/kbd_kern.h>
+#include <linux/module.h>
+#include <linux/vt_kern.h>
+#include <linux/wait.h>
+
+#define EARLY_SUSPEND_CONSOLE	(MAX_NR_CONSOLES-1)
+
+static int orig_fgconsole;
+static void console_early_suspend(struct early_suspend *h)
+{
+	acquire_console_sem();
+	orig_fgconsole = fg_console;
+	if (vc_allocate(EARLY_SUSPEND_CONSOLE))
+		goto err;
+	if (set_console(EARLY_SUSPEND_CONSOLE))
+		goto err;
+	release_console_sem();
+
+	if (vt_waitactive(EARLY_SUSPEND_CONSOLE))
+		pr_warning("console_early_suspend: Can't switch VCs.\n");
+	return;
+err:
+	pr_warning("console_early_suspend: Can't set console\n");
+	release_console_sem();
+}
+
+static void console_late_resume(struct early_suspend *h)
+{
+	int ret;
+	acquire_console_sem();
+	ret = set_console(orig_fgconsole);
+	release_console_sem();
+	if (ret) {
+		pr_warning("console_late_resume: Can't set console.\n");
+		return;
+	}
+
+	if (vt_waitactive(orig_fgconsole))
+		pr_warning("console_late_resume: Can't switch VCs.\n");
+}
+
+static struct early_suspend console_early_suspend_desc = {
+	.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
+	.suspend = console_early_suspend,
+	.resume = console_late_resume,
+};
+
+static int __init console_early_suspend_init(void)
+{
+	register_early_suspend(&console_early_suspend_desc);
+	return 0;
+}
+
+static void  __exit console_early_suspend_exit(void)
+{
+	unregister_early_suspend(&console_early_suspend_desc);
+}
+
+module_init(console_early_suspend_init);
+module_exit(console_early_suspend_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] 34+ messages in thread

* [PATCH 08/10] PM: earlysuspend: Removing dependence on console.
  2009-02-11  1:49             ` [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes Arve Hjønnevåg
@ 2009-02-11  1:49               ` Arve Hjønnevåg
  2009-02-11  1:49                 ` [PATCH 09/10] Input: Hold wake lock while event queue is not empty Arve Hjønnevåg
  2009-02-12 11:28               ` [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes Matthew Garrett
  1 sibling, 1 reply; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, Rebecca Schultz

From: Rebecca Schultz <rschultz@google.com>

Rather than signaling a full update of the display from userspace via a
console switch, this patch introduces 2 files int /sys/power,
wait_for_fb_sleep and wait_for_fb_wake.  Reading these files will block
until the requested state has been entered.  When a read from
wait_for_fb_sleep returns userspace should stop drawing.  When
wait_for_fb_wake returns, it should do a full update.  If either are called
when the fb driver is already in the requested state, they will return
immediately.

Signed-off-by: Rebecca Schultz <rschultz@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 kernel/power/Kconfig          |    9 +++
 kernel/power/Makefile         |    1 +
 kernel/power/fbearlysuspend.c |  153 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 163 insertions(+), 0 deletions(-)
 create mode 100644 kernel/power/fbearlysuspend.c

diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 04e6fad..a214ef2 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -161,6 +161,7 @@ config EARLYSUSPEND
 
 choice
 	prompt "User-space screen access"
+	default FB_EARLYSUSPEND if !FRAMEBUFFER_CONSOLE
 	default CONSOLE_EARLYSUSPEND
 	depends on EARLYSUSPEND
 
@@ -174,6 +175,14 @@ choice
 		  Register early suspend handler to perform a console switch
 		  when user-space should stop drawing to the screen and a switch
 		  back when it should resume.
+
+	config FB_EARLYSUSPEND
+		bool "Sysfs interface"
+		depends on EARLYSUSPEND
+		---help---
+		  Register early suspend handler that notifies and waits for
+		  user-space through sysfs when user-space should stop drawing
+		  to the screen and notifies user-space when it should resume.
 endchoice
 
 config HIBERNATION
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 8bf293d..5beaa21 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_WAKELOCK)		+= wakelock.o
 obj-$(CONFIG_USER_WAKELOCK)	+= userwakelock.o
 obj-$(CONFIG_EARLYSUSPEND)	+= earlysuspend.o
 obj-$(CONFIG_CONSOLE_EARLYSUSPEND)	+= consoleearlysuspend.o
+obj-$(CONFIG_FB_EARLYSUSPEND)	+= fbearlysuspend.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/fbearlysuspend.c b/kernel/power/fbearlysuspend.c
new file mode 100644
index 0000000..19f3e54
--- /dev/null
+++ b/kernel/power/fbearlysuspend.c
@@ -0,0 +1,153 @@
+/* kernel/power/fbearlysuspend.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/earlysuspend.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+
+#include "power.h"
+
+static wait_queue_head_t fb_state_wq;
+static DEFINE_SPINLOCK(fb_state_lock);
+static enum {
+	FB_STATE_STOPPED_DRAWING,
+	FB_STATE_REQUEST_STOP_DRAWING,
+	FB_STATE_DRAWING_OK,
+} fb_state;
+
+/* tell userspace to stop drawing, wait for it to stop */
+static void stop_drawing_early_suspend(struct early_suspend *h)
+{
+	int ret;
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&fb_state_lock, irq_flags);
+	fb_state = FB_STATE_REQUEST_STOP_DRAWING;
+	spin_unlock_irqrestore(&fb_state_lock, irq_flags);
+
+	wake_up_all(&fb_state_wq);
+	ret = wait_event_timeout(fb_state_wq,
+				 fb_state == FB_STATE_STOPPED_DRAWING,
+				 HZ);
+	if (unlikely(fb_state != FB_STATE_STOPPED_DRAWING))
+		pr_warning("stop_drawing_early_suspend: timeout waiting for "
+			   "userspace to stop drawing\n");
+}
+
+/* tell userspace to start drawing */
+static void start_drawing_late_resume(struct early_suspend *h)
+{
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&fb_state_lock, irq_flags);
+	fb_state = FB_STATE_DRAWING_OK;
+	spin_unlock_irqrestore(&fb_state_lock, irq_flags);
+	wake_up(&fb_state_wq);
+}
+
+static struct early_suspend stop_drawing_early_suspend_desc = {
+	.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
+	.suspend = stop_drawing_early_suspend,
+	.resume = start_drawing_late_resume,
+};
+
+static ssize_t wait_for_fb_sleep_show(struct kobject *kobj,
+				      struct kobj_attribute *attr, char *buf)
+{
+	char *s = buf;
+	int ret;
+
+	ret = wait_event_interruptible(fb_state_wq,
+				       fb_state != FB_STATE_DRAWING_OK);
+	if (ret && fb_state == FB_STATE_DRAWING_OK)
+		return ret;
+	else
+		s += sprintf(buf, "sleeping");
+	return s - buf;
+}
+
+static ssize_t wait_for_fb_wake_show(struct kobject *kobj,
+				     struct kobj_attribute *attr, char *buf)
+{
+	char *s = buf;
+	int ret;
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&fb_state_lock, irq_flags);
+	if (fb_state == FB_STATE_REQUEST_STOP_DRAWING) {
+		fb_state = FB_STATE_STOPPED_DRAWING;
+		wake_up(&fb_state_wq);
+	}
+	spin_unlock_irqrestore(&fb_state_lock, irq_flags);
+
+	ret = wait_event_interruptible(fb_state_wq,
+				       fb_state == FB_STATE_DRAWING_OK);
+	if (ret && fb_state != FB_STATE_DRAWING_OK)
+		return ret;
+	else
+		s += sprintf(buf, "awake");
+
+	return s - buf;
+}
+
+#define power_ro_attr(_name) \
+static struct kobj_attribute _name##_attr = {	\
+	.attr	= {				\
+		.name = __stringify(_name),	\
+		.mode = 0444,			\
+	},					\
+	.show	= _name##_show,			\
+	.store	= NULL,		\
+}
+
+power_ro_attr(wait_for_fb_sleep);
+power_ro_attr(wait_for_fb_wake);
+
+static struct attribute *g[] = {
+	&wait_for_fb_sleep_attr.attr,
+	&wait_for_fb_wake_attr.attr,
+	NULL,
+};
+
+static struct attribute_group attr_group = {
+	.attrs = g,
+};
+
+static int __init fb_earlysuspend_init(void)
+{
+	int ret;
+
+	init_waitqueue_head(&fb_state_wq);
+	fb_state = FB_STATE_DRAWING_OK;
+
+	ret = sysfs_create_group(power_kobj, &attr_group);
+	if (ret) {
+		pr_err("fb_earlysuspend_init: sysfs_create_group failed\n");
+		return ret;
+	}
+
+	register_early_suspend(&stop_drawing_early_suspend_desc);
+	return 0;
+}
+
+static void  __exit fb_earlysuspend_exit(void)
+{
+	unregister_early_suspend(&stop_drawing_early_suspend_desc);
+	sysfs_remove_group(power_kobj, &attr_group);
+}
+
+module_init(fb_earlysuspend_init);
+module_exit(fb_earlysuspend_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] 34+ messages in thread

* [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-11  1:49               ` [PATCH 08/10] PM: earlysuspend: Removing dependence on console Arve Hjønnevåg
@ 2009-02-11  1:49                 ` Arve Hjønnevåg
  2009-02-11  1:49                   ` [PATCH 10/10] ledtrig-sleep: Add led trigger for sleep debugging Arve Hjønnevåg
  2009-02-12 11:31                   ` [PATCH 09/10] Input: Hold wake lock while event queue is not empty Matthew Garrett
  0 siblings, 2 replies; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland

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 |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index ed8baa0..e24050b 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/wakelock.h>
 #include "input-compat.h"
 
 struct evdev {
@@ -43,6 +44,7 @@ struct evdev_client {
 	struct fasync_struct *fasync;
 	struct evdev *evdev;
 	struct list_head node;
+	struct wake_lock wake_lock;
 };
 
 static struct evdev *evdev_table[EVDEV_MINORS];
@@ -55,6 +57,7 @@ static void evdev_pass_event(struct evdev_client *client,
 	 * Interrupts are disabled, just acquire the lock
 	 */
 	spin_lock(&client->buffer_lock);
+	wake_lock_timeout(&client->wake_lock, 5 * HZ);
 	client->buffer[client->head++] = *event;
 	client->head &= EVDEV_BUFFER_SIZE - 1;
 	spin_unlock(&client->buffer_lock);
@@ -236,6 +239,7 @@ static int evdev_release(struct inode *inode, struct file *file)
 	mutex_unlock(&evdev->mutex);
 
 	evdev_detach_client(evdev, client);
+	wake_lock_destroy(&client->wake_lock);
 	kfree(client);
 
 	evdev_close_device(evdev);
@@ -272,6 +276,7 @@ static int evdev_open(struct inode *inode, struct file *file)
 	}
 
 	spin_lock_init(&client->buffer_lock);
+	wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, "evdev");
 	client->evdev = evdev;
 	evdev_attach_client(evdev, client);
 
@@ -335,6 +340,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->head == client->tail)
+			wake_unlock(&client->wake_lock);
 	}
 
 	spin_unlock_irq(&client->buffer_lock);
-- 
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] 34+ messages in thread

* [PATCH 10/10] ledtrig-sleep: Add led trigger for sleep debugging.
  2009-02-11  1:49                 ` [PATCH 09/10] Input: Hold wake lock while event queue is not empty Arve Hjønnevåg
@ 2009-02-11  1:49                   ` Arve Hjønnevåg
  2009-02-12 11:31                   ` [PATCH 09/10] Input: Hold wake lock while event queue is not empty Matthew Garrett
  1 sibling, 0 replies; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-11  1:49 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland

Signed-off-by: Brian Swetland <swetland@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
 drivers/leds/Kconfig         |    6 +++
 drivers/leds/Makefile        |    1 +
 drivers/leds/ledtrig-sleep.c |   80 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+), 0 deletions(-)
 create mode 100644 drivers/leds/ledtrig-sleep.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 7427136..f4c5439 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -223,4 +223,10 @@ config LEDS_TRIGGER_DEFAULT_ON
 	  This allows LEDs to be initialised in the ON state.
 	  If unsure, say Y.
 
+config LEDS_TRIGGER_SLEEP
+	tristate "LED Sleep Mode Trigger"
+	depends on LEDS_TRIGGERS && EARLYSUSPEND
+	help
+	  This turns LEDs on when the screen is off but the cpu still running.
+
 endif # NEW_LEDS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 9d76f0f..9cfc148 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -31,3 +31,4 @@ obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK)	+= ledtrig-ide-disk.o
 obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o
 obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)	+= ledtrig-backlight.o
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
+obj-$(CONFIG_LEDS_TRIGGER_SLEEP)	+= ledtrig-sleep.o
diff --git a/drivers/leds/ledtrig-sleep.c b/drivers/leds/ledtrig-sleep.c
new file mode 100644
index 0000000..f164042
--- /dev/null
+++ b/drivers/leds/ledtrig-sleep.c
@@ -0,0 +1,80 @@
+/* drivers/leds/ledtrig-sleep.c
+ *
+ * Copyright (C) 2007 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/earlysuspend.h>
+#include <linux/leds.h>
+#include <linux/suspend.h>
+
+static int ledtrig_sleep_pm_callback(struct notifier_block *nfb,
+					unsigned long action,
+					void *ignored);
+
+DEFINE_LED_TRIGGER(ledtrig_sleep)
+static struct notifier_block ledtrig_sleep_pm_notifier = {
+	.notifier_call = ledtrig_sleep_pm_callback,
+	.priority = 0,
+};
+
+static void ledtrig_sleep_early_suspend(struct early_suspend *h)
+{
+	led_trigger_event(ledtrig_sleep, LED_FULL);
+}
+
+static void ledtrig_sleep_early_resume(struct early_suspend *h)
+{
+	led_trigger_event(ledtrig_sleep, LED_OFF);
+}
+
+static struct early_suspend ledtrig_sleep_early_suspend_handler = {
+	.suspend = ledtrig_sleep_early_suspend,
+	.resume = ledtrig_sleep_early_resume,
+};
+
+static int ledtrig_sleep_pm_callback(struct notifier_block *nfb,
+					unsigned long action,
+					void *ignored)
+{
+	switch (action) {
+	case PM_HIBERNATION_PREPARE:
+	case PM_SUSPEND_PREPARE:
+		led_trigger_event(ledtrig_sleep, LED_OFF);
+		return NOTIFY_OK;
+	case PM_POST_HIBERNATION:
+	case PM_POST_SUSPEND:
+		led_trigger_event(ledtrig_sleep, LED_FULL);
+		return NOTIFY_OK;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static int __init ledtrig_sleep_init(void)
+{
+	led_trigger_register_simple("sleep", &ledtrig_sleep);
+	register_pm_notifier(&ledtrig_sleep_pm_notifier);
+	register_early_suspend(&ledtrig_sleep_early_suspend_handler);
+	return 0;
+}
+
+static void __exit ledtrig_sleep_exit(void)
+{
+	unregister_early_suspend(&ledtrig_sleep_early_suspend_handler);
+	unregister_pm_notifier(&ledtrig_sleep_pm_notifier);
+	led_trigger_unregister_simple(ledtrig_sleep);
+}
+
+module_init(ledtrig_sleep_init);
+module_exit(ledtrig_sleep_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] 34+ messages in thread

* Re: [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes.
  2009-02-11  1:49             ` [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes Arve Hjønnevåg
  2009-02-11  1:49               ` [PATCH 08/10] PM: earlysuspend: Removing dependence on console Arve Hjønnevåg
@ 2009-02-12 11:28               ` Matthew Garrett
  1 sibling, 0 replies; 34+ messages in thread
From: Matthew Garrett @ 2009-02-12 11:28 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Tue, Feb 10, 2009 at 05:49:12PM -0800, Arve Hjønnevåg wrote:
> When early-suspend is enabled, the framebuffer is turned off at
> early-suspend. If CONSOLE_EARLYSUSPEND is enabled, a console switch
> is used to notify user space that it should stop drawing before
> the framebuffer is disabled, and that it should repaint and resume
> drawing after the framebuffer is turned back on.

Mm. The console VT switching is an ugly hack to deal with the fact that 
in the x86 world graphics reinitialisation is handled by userspace, and 
so we otherwise have races between graphis reinitialisation and X. I 
don't think it's helpful to further perpetuate that hack, especially 
when you have another mechanism for requestng a screen refresh.

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

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-11  1:49                 ` [PATCH 09/10] Input: Hold wake lock while event queue is not empty Arve Hjønnevåg
  2009-02-11  1:49                   ` [PATCH 10/10] ledtrig-sleep: Add led trigger for sleep debugging Arve Hjønnevåg
@ 2009-02-12 11:31                   ` Matthew Garrett
  2009-02-13  0:27                     ` Arve Hjønnevåg
  1 sibling, 1 reply; 34+ messages in thread
From: Matthew Garrett @ 2009-02-12 11:31 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Tue, Feb 10, 2009 at 05:49:14PM -0800, Arve Hjønnevåg wrote:

>  	spin_lock(&client->buffer_lock);
> +	wake_lock_timeout(&client->wake_lock, 5 * HZ);

Why the timeout version? If your input handler vanishes for more than 5 
seconds then presumably you should be thinking about watchdoging the 
entire system.

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

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

* Re: [PATCH 06/10] PM: Add early suspend api.
  2009-02-11  1:49           ` [PATCH 06/10] PM: Add early suspend api Arve Hjønnevåg
  2009-02-11  1:49             ` [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes Arve Hjønnevåg
@ 2009-02-12 11:34             ` Matthew Garrett
  1 sibling, 0 replies; 34+ messages in thread
From: Matthew Garrett @ 2009-02-12 11:34 UTC (permalink / raw)
  To: linux-pm

On Tue, Feb 10, 2009 at 05:49:11PM -0800, Arve Hjønnevåg wrote:

> +EARLY_SUSPEND_LEVEL_BLANK_SCREEN:
> +  On suspend the screen should be turned off but the framebuffer must still be
> +  accessible. On resume the screen can be turned back on.
> +
> +EARLY_SUSPEND_LEVEL_STOP_DRAWING:
> +  On suspend this level notifies user-space that it should stop accessing the 
> +  framebuffer and it waits for it to complete. On resume it notifies user-space 
> +  that it should resume screen access.
> +  Two methods are provided, console switch or a sysfs interface.
> +
> +EARLY_SUSPEND_LEVEL_DISABLE_FB:
> +  Turn off the framebuffer on suspend and back on on resume.
> +

This set seems like a pretty poor example. There's already a userspace 
interface for controlling the LCD via sysfs, and extending this to power 
down the associated framebuffer hardware would be trivial.

> +EARLY_SUSPEND_LEVEL_STOP_INPUT:
> +  On suspend turn off input devices that are not capable of wakeup or where
> +  wakeup is disabled. On resume turn the same devices back on.

Why not just ask your userspace input consumer to release the device and 
then have the driver automatically power itself down when its use count 
hits zero?

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

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

* Re: [PATCH 01/10] PM: Add wake lock api.
  2009-02-11  1:49 ` [PATCH 01/10] PM: Add wake lock api Arve Hjønnevåg
  2009-02-11  1:49   ` [PATCH 02/10] PM: wakelock: Override wakelocks when not using /sys/power/request_state Arve Hjønnevåg
@ 2009-02-12 22:00   ` mark gross
  2009-02-12 23:06     ` Arve Hjønnevåg
  1 sibling, 1 reply; 34+ messages in thread
From: mark gross @ 2009-02-12 22:00 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: ncunningham, u.luckas, swetland, linux-pm

i'm sorry if I re-hash some discussions feel free to slap me.

On Tue, Feb 10, 2009 at 05:49:06PM -0800, Arve Hjønnevåg wrote:
> Adds /sys/power/request_state, a non-blocking interface that specifies
> which suspend state to enter when no wakelocks are locked. A special
> state, "on", stops the process by locking the "main" wakelock.
> 
> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> ---
>  Documentation/power/wakelocks.txt |   80 +++++
>  include/linux/wakelock.h          |   93 +++++
>  kernel/power/Kconfig              |   17 +
>  kernel/power/Makefile             |    1 +
>  kernel/power/main.c               |   63 ++++
>  kernel/power/power.h              |    7 +
>  kernel/power/wakelock.c           |  664 +++++++++++++++++++++++++++++++++++++
>  7 files changed, 925 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/power/wakelocks.txt
>  create mode 100755 include/linux/wakelock.h
>  create mode 100644 kernel/power/wakelock.c
> 
> diff --git a/Documentation/power/wakelocks.txt b/Documentation/power/wakelocks.txt
> new file mode 100644
> index 0000000..893a438
> --- /dev/null
> +++ b/Documentation/power/wakelocks.txt
> @@ -0,0 +1,80 @@
> +Wakelocks
> +=========
> +
> +A locked wakelock, depending on its type, prevents the system from entering
> +suspend or other low-power states. When creating a wakelock, you can select
> +if it prevents suspend or low-power idle states.  If the type is set to
> +WAKE_LOCK_SUSPEND, the wakelock prevents a full system suspend. If the type
> +is WAKE_LOCK_IDLE, low-power states that cause large interrupt latencies, or
> +that disable a set of interrupts, will not be entered from idle until the
> +wakelocks are released. Unless the type is specified, this document refers
> +to wakelocks with the type set to WAKE_LOCK_SUSPEND.

If there are no wakelocks held, what code path pushes the system to a
lower state?  (wake_unlock?)

Would it make more sense to remove the suspend types of locks?  Those
seem to be pretty contentious.

Do the ARM devices really go into a suspend state or just a low power
state?  

> +
> +If the suspend operation has already started when locking a wakelock, it will
> +abort the suspend operation as long it has not already reached the suspend_late
> +stage. This means that locking a wakelock from an interrupt handler or a
> +freezeable thread always works, but if you lock a wakelock from a suspend_late
> +handler you must also return an error from that handler to abort suspend.
> +
> +Wakelocks 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 locks the keypad-scan wakelock
> +  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 locks
> +  the input-event-queue wakelock.
> +- The keypad-scan code detects that no keys are held and unlocks the
> +  keypad-scan wakelock.

Lets assume the system drops to suspend after 0ms of no-locks held.
Don't we have a race between the keypad driver unlocking the only lock
and inpu-event-queue lock getting taken?

> +- The user-space input-event thread returns from select/poll, locks the
> +  process-input-events wakelock and then calls read in the input-event device.
> +- The input-event driver dequeues the key-event and, since the queue is now
> +  empty, it unlocks the input-event-queue wakelock.
> +- The user-space input-event thread returns from read. It determines that the
> +  key should not wake up the full system, releases the process-input-events
> +  wakelock and calls select or poll.
> +
> +                 Key pressed   Key released
> +                     |             |
> +keypad-scan          ++++++++++++++++++
> +input-event-queue        +++          +++
> +process-input-events       +++          +++
> +

it feels like there needs to be interlocking wakelocks to keep the
system from unexpectedly falling asleep.  Or, some hysteresis slop time
baked in to hide the races.

Are wakelocks too fine grained?

> +
> +Driver API
> +==========
> +
> +A driver can use the wakelock api by adding a wakelock variable to its state
> +and calling wake_lock_init. For instance:
> +struct state {
> +	struct wakelock wakelock;
> +}
> +
> +init() {
> +	wake_lock_init(&state->wakelock, WAKE_LOCK_SUSPEND, "wakelockname");
> +}
> +
> +Before freeing the memory, wake_lock_destroy must be called:
> +
> +uninit() {
> +	wake_lock_destroy(&state->wakelock);
> +}
> +
> +When the driver determines that it needs to run (usually in an interrupt
> +handler) it calls wake_lock:
> +	wake_lock(&state->wakelock);
> +
> +When it no longer needs to run it calls wake_unlock:
> +	wake_unlock(&state->wakelock);
> +
> +It can also call wake_lock_timeout to release the wakelock after a delay:
> +	wake_lock_timeout(&state->wakelock, HZ);
> +
> +This works whether the wakelock is already held or not. It is useful if the
> +driver woke up other parts of the system that do not use wakelocks 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.
> +
> diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h
> new file mode 100755
> index 0000000..ec878d2
> --- /dev/null
> +++ b/include/linux/wakelock.h
> @@ -0,0 +1,93 @@
> +/* include/linux/wakelock.h
> + *
> + * Copyright (C) 2007-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.
> + *
> + */
> +
> +#ifndef _LINUX_WAKELOCK_H
> +#define _LINUX_WAKELOCK_H
> +
> +#include <linux/list.h>
> +#include <linux/ktime.h>
> +
> +/* A wake_lock prevents the system from entering suspend or other low power
> + * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock
> + * prevents a full system suspend. If the type is WAKE_LOCK_IDLE, low power
> + * states that cause large interrupt latencies or that disable a set of
> + * interrupts will not entered from idle until the wake_locks are released.
> + */
> +
> +enum {
> +	WAKE_LOCK_SUSPEND, /* Prevent suspend */
> +	WAKE_LOCK_IDLE,    /* Prevent low power idle */
> +	WAKE_LOCK_TYPE_COUNT
> +};
> +
> +struct wake_lock {
> +#ifdef CONFIG_WAKELOCK
> +	struct list_head    link;
> +	int                 flags;
> +	const char         *name;
> +	unsigned long       expires;
> +#ifdef CONFIG_WAKELOCK_STAT
> +	struct {
> +		int             count;
> +		int             expire_count;
> +		int             wakeup_count;
> +		ktime_t         total_time;
> +		ktime_t         prevent_suspend_time;
> +		ktime_t         max_time;
> +		ktime_t         last_time;
> +	} stat;
> +#endif
> +#endif
> +};
> +
> +#ifdef CONFIG_WAKELOCK
> +
> +void wake_lock_init(struct wake_lock *lock, int type, const char *name);
> +void wake_lock_destroy(struct wake_lock *lock);
> +void wake_lock(struct wake_lock *lock);
> +void wake_lock_timeout(struct wake_lock *lock, long timeout);
> +void wake_unlock(struct wake_lock *lock);
> +
> +/* wake_lock_active returns a non-zero value if the wake_lock is currently
> + * locked. If the wake_lock 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 0.
> + */
> +int wake_lock_active(struct wake_lock *lock);
> +
> +/* has_wake_lock can be used by generic power management code to abort suspend.
> + * has_wake_lock returns 0 if no wake locks of the specified type are active,
> + * and non-zero if one or more wake locks are held. Specifically it returns
> + * -1 if one or more wake locks with no timeout are active or the
> + * number of jiffies until all active wake locks time out.
> + */
> +long has_wake_lock(int type);
> +
> +#else
> +
> +static inline void wake_lock_init(struct wake_lock *lock, int type,
> +					const char *name) {}
> +static inline void wake_lock_destroy(struct wake_lock *lock) {}
> +static inline void wake_lock(struct wake_lock *lock) {}
> +static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) {}
> +static inline void wake_unlock(struct wake_lock *lock) {}
> +
> +static inline int wake_lock_active(struct wake_lock *lock) { return 0; }
> +static inline long has_wake_lock(int type) { return 0; }
> +
> +#endif
> +
> +#endif
> +
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 23bd4da..9abd97e 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -116,6 +116,23 @@ config SUSPEND_FREEZER
>  
>  	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
>  
> +config WAKELOCK
> +	bool "Wake lock"
> +	depends on PM
> +	select RTC_LIB
> +	default n
> +	---help---
> +	  Enable wakelocks. When user space requests a sleep state through
> +	  /sys/power/request_state, the requested sleep state will be entered
> +	  when no wake locks are held.
> +
> +config WAKELOCK_STAT
> +	bool "Wake lock stats"
> +	depends on WAKELOCK
> +	default y
> +	---help---
> +	  Report wake lock stats in /proc/wakelocks
> +
>  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 d7a1016..8d8672b 100644
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -6,6 +6,7 @@ endif
>  obj-y				:= main.o
>  obj-$(CONFIG_PM_SLEEP)		+= console.o
>  obj-$(CONFIG_FREEZER)		+= process.o
> +obj-$(CONFIG_WAKELOCK)		+= wakelock.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 2399888..8bdd15a 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/wakelock.h>
>  
>  #include "power.h"
>  
> @@ -388,6 +389,9 @@ static void suspend_finish(void)
>  
>  
>  static const char * const pm_states[PM_SUSPEND_MAX] = {
> +#ifdef CONFIG_WAKELOCK
> +	[PM_SUSPEND_ON]		= "on",
> +#endif
>  	[PM_SUSPEND_STANDBY]	= "standby",
>  	[PM_SUSPEND_MEM]	= "mem",
>  };
> @@ -536,6 +540,62 @@ 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
> + *	requsted. Wakelocks are respected and the requested state will only
> + *	be entered when no wakelocks are held. Write "on" to cancel.
> + *
> + *	If CONFIG_EARLYSUSPEND is set, early_suspend hooks are called when
> + *	the requested state changes to or from "on"
> + */
> +
> +#ifdef CONFIG_WAKELOCK
> +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_WAKELOCK */
> +
>  #ifdef CONFIG_PM_TRACE
>  int pm_trace_enabled;
>  
> @@ -563,6 +623,9 @@ power_attr(pm_trace);
>  
>  static struct attribute * g[] = {
>  	&state_attr.attr,
> +#ifdef CONFIG_WAKELOCK
> +	&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..9468679 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -223,3 +223,10 @@ static inline void suspend_thaw_processes(void)
>  {
>  }
>  #endif
> +
> +#ifdef CONFIG_WAKELOCK
> +/* kernel/power/wakelock.c */
> +extern struct workqueue_struct *suspend_work_queue;
> +extern struct wake_lock main_wake_lock;
> +void request_suspend_state(suspend_state_t state);
> +#endif
> diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
> new file mode 100644
> index 0000000..13f5d03
> --- /dev/null
> +++ b/kernel/power/wakelock.c
> @@ -0,0 +1,664 @@
> +/* kernel/power/wakelock.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/platform_device.h>
> +#include <linux/rtc.h>
> +#include <linux/suspend.h>
> +#include <linux/syscalls.h> /* sys_sync */
> +#include <linux/wakelock.h>
> +#ifdef CONFIG_WAKELOCK_STAT
> +#include <linux/proc_fs.h>
> +#endif
> +#include "power.h"
> +
> +enum {
> +	DEBUG_EXIT_SUSPEND = 1U << 0,
> +	DEBUG_WAKEUP = 1U << 1,
> +	DEBUG_USER_STATE = 1U << 2,
> +	DEBUG_SUSPEND = 1U << 3,
> +	DEBUG_EXPIRE = 1U << 4,
> +	DEBUG_WAKE_LOCK = 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 WAKE_LOCK_TYPE_MASK              (0x0f)
> +#define WAKE_LOCK_INITIALIZED            (1U << 8)
> +#define WAKE_LOCK_ACTIVE                 (1U << 9)
> +#define WAKE_LOCK_AUTO_EXPIRE            (1U << 10)
> +#define WAKE_LOCK_PREVENTING_SUSPEND     (1U << 11)
> +
> +static DEFINE_SPINLOCK(list_lock);
> +static DEFINE_SPINLOCK(state_lock);
> +static LIST_HEAD(inactive_locks);
> +static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];
> +static int current_event_num;
> +struct workqueue_struct *suspend_work_queue;
> +struct wake_lock main_wake_lock;
> +static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
> +static struct wake_lock unknown_wakeup;
> +
> +#ifdef CONFIG_WAKELOCK_STAT
> +static struct wake_lock deleted_wake_locks;
> +static ktime_t last_sleep_time_update;
> +static bool wait_for_wakeup;
> +
> +bool stats_get_expired_time(struct wake_lock *lock, ktime_t *expire_time)
> +{
> +	struct timespec ts;
> +	struct timespec kt;
> +	struct timespec tomono;
> +	struct timespec delta;
> +	unsigned long seq;
> +	long timeout;
> +
> +	if (!(lock->flags & WAKE_LOCK_AUTO_EXPIRE))
> +		return false;
> +	do {
> +		seq = read_seqbegin(&xtime_lock);
> +		timeout = lock->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_lock_stat(char *buf, struct wake_lock *lock)
> +{
> +	int lock_count = lock->stat.count;
> +	int expire_count = lock->stat.expire_count;
> +	ktime_t active_time = ktime_set(0, 0);
> +	ktime_t total_time = lock->stat.total_time;
> +	ktime_t max_time = lock->stat.max_time;
> +	ktime_t prevent_suspend_time = lock->stat.prevent_suspend_time;
> +	if (lock->flags & WAKE_LOCK_ACTIVE) {
> +		ktime_t now, add_time;
> +		bool expired = stats_get_expired_time(lock, &now);
> +		if (!expired)
> +			now = ktime_get();
> +		add_time = ktime_sub(now, lock->stat.last_time);
> +		lock_count++;
> +		if (!expired)
> +			active_time = add_time;
> +		else
> +			expire_count++;
> +		total_time = ktime_add(total_time, add_time);
> +		if (lock->flags & WAKE_LOCK_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%d\t%lld\t%lld\t%lld\t%lld\t"
> +		       "%lld\n", lock->name, lock_count, expire_count,
> +		       lock->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(lock->stat.last_time));
> +}
> +
> +
> +static int wakelock_stats_read_proc(char *page, char **start, off_t off,
> +			       int count, int *eof, void *data)
> +{
> +	unsigned long irqflags;
> +	struct wake_lock *lock;
> +	int len = 0;
> +	char *p = page;
> +	int type;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	p += sprintf(p, "name\tcount\texpire_count\twake_count\tactive_since"
> +		     "\ttotal_time\tsleep_time\tmax_time\tlast_change\n");
> +	list_for_each_entry(lock, &inactive_locks, link) {
> +		p += print_lock_stat(p, lock);
> +	}
> +	for (type = 0; type < WAKE_LOCK_TYPE_COUNT; type++) {
> +		list_for_each_entry(lock, &active_wake_locks[type], link)
> +			p += print_lock_stat(p, lock);
> +	}
> +	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 wake_lock_stat_init_locked(struct wake_lock *lock)
> +{
> +	lock->stat.count = 0;
> +	lock->stat.expire_count = 0;
> +	lock->stat.wakeup_count = 0;
> +	lock->stat.total_time = ktime_set(0, 0);
> +	lock->stat.prevent_suspend_time = ktime_set(0, 0);
> +	lock->stat.max_time = ktime_set(0, 0);
> +	lock->stat.last_time = ktime_set(0, 0);
> +}
> +
> +static void wake_lock_stat_destroy_locked(struct wake_lock *lock)
> +{
> +	if (lock->stat.count) {
> +		deleted_wake_locks.stat.count += lock->stat.count;
> +		deleted_wake_locks.stat.expire_count += lock->stat.expire_count;
> +		deleted_wake_locks.stat.total_time =
> +			ktime_add(deleted_wake_locks.stat.total_time,
> +				  lock->stat.total_time);
> +		deleted_wake_locks.stat.prevent_suspend_time =
> +			ktime_add(deleted_wake_locks.stat.prevent_suspend_time,
> +				  lock->stat.prevent_suspend_time);
> +		deleted_wake_locks.stat.max_time =
> +			ktime_add(deleted_wake_locks.stat.max_time,
> +				  lock->stat.max_time);
> +	}
> +}
> +
> +static void wake_unlock_stat_locked(struct wake_lock *lock, bool expired)
> +{
> +	ktime_t duration;
> +	ktime_t now;
> +	if (!(lock->flags & WAKE_LOCK_ACTIVE))
> +		return;
> +	if (stats_get_expired_time(lock, &now))
> +		expired = true;
> +	else
> +		now = ktime_get();
> +	lock->stat.count++;
> +	if (expired)
> +		lock->stat.expire_count++;
> +	duration = ktime_sub(now, lock->stat.last_time);
> +	lock->stat.total_time = ktime_add(lock->stat.total_time, duration);
> +	if (ktime_to_ns(duration) > ktime_to_ns(lock->stat.max_time))
> +		lock->stat.max_time = duration;
> +	lock->stat.last_time = ktime_get();
> +	if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
> +		duration = ktime_sub(now, last_sleep_time_update);
> +		lock->stat.prevent_suspend_time = ktime_add(
> +			lock->stat.prevent_suspend_time, duration);
> +		lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
> +	}
> +}
> +
> +static void wake_lock_stat_locked(struct wake_lock *lock)
> +{
> +	int type = lock->flags & WAKE_LOCK_TYPE_MASK;
> +	if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {
> +		if (debug_mask & DEBUG_WAKEUP)
> +			pr_info("wakeup wake lock: %s\n", lock->name);
> +		wait_for_wakeup = false;
> +		lock->stat.wakeup_count++;
> +	}
> +	if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
> +	    time_is_before_eq_jiffies(lock->expires)) {
> +		wake_unlock_stat_locked(lock, false);
> +		lock->stat.last_time = ktime_get();
> +	}
> +
> +	if (!(lock->flags & WAKE_LOCK_ACTIVE))
> +		lock->stat.last_time = ktime_get();
> +}
> +
> +static void update_sleep_wait_stats_locked(bool done)
> +{
> +	struct wake_lock *lock;
> +	ktime_t now, etime, elapsed, add;
> +	bool expired;
> +
> +	now = ktime_get();
> +	elapsed = ktime_sub(now, last_sleep_time_update);
> +	list_for_each_entry(lock, &active_wake_locks[WAKE_LOCK_SUSPEND], link) {
> +		expired = stats_get_expired_time(lock, &etime);
> +		if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
> +			if (expired)
> +				add = ktime_sub(etime, last_sleep_time_update);
> +			else
> +				add = elapsed;
> +			lock->stat.prevent_suspend_time = ktime_add(
> +				lock->stat.prevent_suspend_time, add);
> +		}
> +		if (done || expired)
> +			lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
> +		else
> +			lock->flags |= WAKE_LOCK_PREVENTING_SUSPEND;
> +	}
> +	last_sleep_time_update = now;
> +}
> +
> +#else
> +
> +static inline void wake_lock_stat_init_locked(struct wake_lock *lock) {}
> +static inline void wake_lock_stat_destroy_locked(struct wake_lock *lock) {}
> +static inline void wake_lock_stat_locked(struct wake_lock *lock) {}
> +static inline void wake_unlock_stat_locked(struct wake_lock *lock,
> +					   bool expired) {}
> +static inline void update_sleep_wait_stats_locked(bool done) {}
> +
> +#endif
> +
> +
> +static void expire_wake_lock(struct wake_lock *lock)
> +{
> +	wake_unlock_stat_locked(lock, true);
> +	lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
> +	list_del(&lock->link);
> +	list_add(&lock->link, &inactive_locks);
> +	if (debug_mask & (DEBUG_WAKE_LOCK | DEBUG_EXPIRE))
> +		pr_info("expired wake lock %s\n", lock->name);
> +}
> +
> +static void print_active_locks(int type)
> +{
> +	unsigned long irqflags;
> +	struct wake_lock *lock;
> +
> +	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	list_for_each_entry(lock, &active_wake_locks[type], link) {
> +		if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
> +			long timeout = lock->expires - jiffies;
> +			if (timeout <= 0)
> +				pr_info("wake lock %s, expired\n", lock->name);
> +			else
> +				pr_info("active wake lock %s, time left %ld\n",
> +					lock->name, timeout);
> +		} else
> +			pr_info("active wake lock %s\n", lock->name);
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +
> +static long has_wake_lock_locked(int type)
> +{
> +	struct wake_lock *lock, *n;
> +	long max_timeout = 0;
> +
> +	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
> +	list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
> +		if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
> +			long timeout = lock->expires - jiffies;
> +			if (timeout <= 0)
> +				expire_wake_lock(lock);
> +			else if (timeout > max_timeout)
> +				max_timeout = timeout;
> +		} else
> +			return -1;
> +	}
> +	return max_timeout;
> +}
> +
> +long has_wake_lock(int type)
> +{
> +	long ret;
> +	unsigned long irqflags;
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	ret = has_wake_lock_locked(type);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +	return ret;
> +}
> +
> +static void suspend_worker(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: abort suspend\n");
> +		return;
> +	}
> +
> +	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) {
> +		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 (current_event_num == entry_event_num) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +		wake_lock_timeout(&unknown_wakeup, HZ / 2);
> +	}
> +}
> +static DECLARE_WORK(suspend_work, suspend_worker);
> +
> +static void expire_wake_locks(unsigned long data)
> +{
> +	long has_lock;
> +	unsigned long irqflags;
> +	if (debug_mask & DEBUG_EXPIRE)
> +		pr_info("expire_wake_locks: start\n");
> +	if (debug_mask & DEBUG_SUSPEND)
> +		print_active_locks(WAKE_LOCK_SUSPEND);
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);
> +	if (debug_mask & DEBUG_EXPIRE)
> +		pr_info("expire_wake_locks: done, has_lock %ld\n", has_lock);
> +	if (has_lock == 0)
> +		queue_work(suspend_work_queue, &suspend_work);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
> +
> +static int power_suspend_late(struct platform_device *pdev, pm_message_t state)
> +{
> +	int ret = has_wake_lock(WAKE_LOCK_SUSPEND) ? -EAGAIN : 0;
> +#ifdef CONFIG_WAKELOCK_STAT
> +	wait_for_wakeup = true;
> +#endif
> +	if (debug_mask & DEBUG_SUSPEND)
> +		pr_info("power_suspend_late return %d\n", ret);
> +	return ret;
> +}
> +
> +static struct platform_driver power_driver = {
> +	.driver.name = "power",
> +	.suspend_late = power_suspend_late,
> +};
> +static struct platform_device power_device = {
> +	.name = "power",
> +};
> +
> +/**
> + * wake_lock_init() - Initialize a wakelock
> + * @lock:	The wakelock to initialize.
> + * @type:	Type of wakelock, e.g. WAKE_LOCK_SUSPEND
> + * @name:	The name of the wakelock to show in /proc/wakelocks
> + */
> +void wake_lock_init(struct wake_lock *lock, int type, const char *name)
> +{
> +	unsigned long irqflags = 0;
> +
> +	if (name)
> +		lock->name = name;
> +	BUG_ON(!lock->name);
> +
> +	if (debug_mask & DEBUG_WAKE_LOCK)
> +		pr_info("wake_lock_init name=%s\n", lock->name);
> +
> +	lock->flags = (type & WAKE_LOCK_TYPE_MASK) | WAKE_LOCK_INITIALIZED;
> +	INIT_LIST_HEAD(&lock->link);
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	wake_lock_stat_init_locked(lock);
> +	list_add(&lock->link, &inactive_locks);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(wake_lock_init);
> +
> +/**
> + * wake_lock_destroy() - Destroy a wakelock
> + * @lock:       The wakelock to destroy.
> + */
> +void wake_lock_destroy(struct wake_lock *lock)
> +{
> +	unsigned long irqflags;
> +	if (debug_mask & DEBUG_WAKE_LOCK)
> +		pr_info("wake_lock_destroy name=%s\n", lock->name);
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	wake_lock_stat_destroy_locked(lock);
> +	lock->flags &= ~WAKE_LOCK_INITIALIZED;
> +	list_del(&lock->link);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(wake_lock_destroy);
> +
> +static void __wake_lock(struct wake_lock *lock, long timeout, bool has_timeout)

If the timeout == 0 or < 0 can you assume has_timeout?

> +{
> +	int type;
> +	unsigned long irqflags;
> +	long expire_in;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	type = lock->flags & WAKE_LOCK_TYPE_MASK;
> +	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
> +	BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));
> +
> +	wake_lock_stat_locked(lock);
> +	lock->flags |= WAKE_LOCK_ACTIVE;
> +	list_del(&lock->link);
> +	if (has_timeout) {
> +		if (debug_mask & DEBUG_WAKE_LOCK)
> +			pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",
> +				lock->name, type, timeout / HZ,
> +				(timeout % HZ) * MSEC_PER_SEC / HZ);
> +		lock->expires = jiffies + timeout;
> +		lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
> +		list_add_tail(&lock->link, &active_wake_locks[type]);
> +	} else {
> +		if (debug_mask & DEBUG_WAKE_LOCK)
> +			pr_info("wake_lock: %s, type %d\n", lock->name, type);
> +		lock->expires = LONG_MAX;
> +		lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
> +		/* Add to head so has_wake_lock only has to examine one entry */
> +		list_add(&lock->link, &active_wake_locks[type]);
> +	}
> +	if (type == WAKE_LOCK_SUSPEND) {
> +		current_event_num++;
> +		if (lock == &main_wake_lock)
> +			update_sleep_wait_stats_locked(true);
> +		else if (!wake_lock_active(&main_wake_lock))
> +			update_sleep_wait_stats_locked(false);
> +		expire_in = has_timeout ? has_wake_lock_locked(type) : -1;
> +		if (expire_in > 0) {
> +			if (debug_mask & DEBUG_EXPIRE)
> +				pr_info("wake_lock: %s, start expire timer, "
> +					"%ld\n", lock->name, expire_in);
> +			mod_timer(&expire_timer, jiffies + expire_in);
> +		} else {
> +			if (del_timer(&expire_timer))
> +				if (debug_mask & DEBUG_EXPIRE)
> +					pr_info("wake_lock: %s, stop expire "
> +						"timer\n", lock->name);
> +			if (expire_in == 0)
> +				queue_work(suspend_work_queue, &suspend_work);
> +		}
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +
> +/**
> + * wake_lock() - Lock a wakelock
> + * @lock:       The wakelock to lock.
> + */
> +void wake_lock(struct wake_lock *lock)

Naming is a little confusing to me.  wake_lock(...) reads as an
initialization / constructor type of function, but its really a locking
operation.  Perhaps lock_wake_lock would be more clear.

> +{
> +	__wake_lock(lock, 0, false);
> +}
> +EXPORT_SYMBOL(wake_lock);
> +
> +/**
> + * wake_lock_timeout() - Lock a wakelock with auto-unlock timeout
> + * @lock:       The wakelock to lock.
> + * @timeout:    Timeout in jiffies before the lock auto-unlock
> + */
> +void wake_lock_timeout(struct wake_lock *lock, long timeout)
> +{
> +	__wake_lock(lock, timeout, true);
> +}
> +EXPORT_SYMBOL(wake_lock_timeout);
> +
> +/**
> + * wake_lock() - Unlock a wakelock
> + * @lock:       The wakelock to unlock.
> + */
> +void wake_unlock(struct wake_lock *lock)

unlock_wake_lock?

> +{
> +	int type;
> +	unsigned long irqflags;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	wake_unlock_stat_locked(lock, false);
> +
> +	if (debug_mask & DEBUG_WAKE_LOCK)
> +		pr_info("wake_unlock: %s\n", lock->name);
> +	lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
> +	list_del(&lock->link);
> +	list_add(&lock->link, &inactive_locks);
> +
> +	type = lock->flags & WAKE_LOCK_TYPE_MASK;
> +	if (type == WAKE_LOCK_SUSPEND) {
> +		long has_lock = has_wake_lock_locked(type);
> +		if (has_lock > 0) {
> +			if (debug_mask & DEBUG_EXPIRE)
> +				pr_info("wake_unlock: %s, start expire timer, "
> +					"%ld\n", lock->name, has_lock);
> +			mod_timer(&expire_timer, jiffies + has_lock);
> +		} else {
> +			if (del_timer(&expire_timer))
> +				if (debug_mask & DEBUG_EXPIRE)
> +					pr_info("wake_unlock: %s, stop expire "
> +						"timer\n", lock->name);
> +			if (has_lock == 0)
> +				queue_work(suspend_work_queue, &suspend_work);
> +		}
> +		if (lock == &main_wake_lock) {
> +			if (debug_mask & DEBUG_SUSPEND)
> +				print_active_locks(WAKE_LOCK_SUSPEND);
> +			update_sleep_wait_stats_locked(false);
> +		}
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(wake_unlock);
> +
> +/**
> + * wake_lock_active() - Test if a wakelock is locked
> + * @lock:       The wakelock to check.
> + */
> +int wake_lock_active(struct wake_lock *lock)
> +{
> +	return !!(lock->flags & WAKE_LOCK_ACTIVE);

A double bang? '!!'  shouldn't this be syntax error?  looks odd.

> +}
> +EXPORT_SYMBOL(wake_lock_active);
> +
> +void request_suspend_state(suspend_state_t state)
> +{
> +	unsigned long irqflags;
> +	spin_lock_irqsave(&state_lock, irqflags);
why not grab this lock after the debug_mask if block?


--mgross

> +	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)
> +		wake_lock(&main_wake_lock);
> +	else
> +		wake_unlock(&main_wake_lock);
> +	spin_unlock_irqrestore(&state_lock, irqflags);
> +}
> +
> +static int __init wakelocks_init(void)
> +{
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
> +		INIT_LIST_HEAD(&active_wake_locks[i]);
> +
> +#ifdef CONFIG_WAKELOCK_STAT
> +	wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,
> +			"deleted_wake_locks");
> +#endif
> +	wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
> +	wake_lock(&main_wake_lock);
> +	wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups");
> +
> +	ret = platform_device_register(&power_device);
> +	if (ret) {
> +		pr_err("wakelocks_init: platform_device_register failed\n");
> +		goto err_platform_device_register;
> +	}
> +	ret = platform_driver_register(&power_driver);
> +	if (ret) {
> +		pr_err("wakelocks_init: platform_driver_register failed\n");
> +		goto err_platform_driver_register;
> +	}
> +
> +	suspend_work_queue = create_singlethread_workqueue("suspend");
> +	if (suspend_work_queue == NULL) {
> +		ret = -ENOMEM;
> +		goto err_suspend_work_queue;
> +	}
> +
> +#ifdef CONFIG_WAKELOCK_STAT
> +	create_proc_read_entry("wakelocks", S_IRUGO, NULL,
> +				wakelock_stats_read_proc, NULL);
> +#endif
> +
> +	return 0;
> +
> +err_suspend_work_queue:
> +	platform_driver_unregister(&power_driver);
> +err_platform_driver_register:
> +	platform_device_unregister(&power_device);
> +err_platform_device_register:
> +	wake_lock_destroy(&unknown_wakeup);
> +	wake_lock_destroy(&main_wake_lock);
> +#ifdef CONFIG_WAKELOCK_STAT
> +	wake_lock_destroy(&deleted_wake_locks);
> +#endif
> +	return ret;
> +}
> +
> +static void  __exit wakelocks_exit(void)
> +{
> +#ifdef CONFIG_WAKELOCK_STAT
> +	remove_proc_entry("wakelocks", NULL);
> +#endif
> +	destroy_workqueue(suspend_work_queue);
> +	platform_driver_unregister(&power_driver);
> +	platform_device_unregister(&power_device);
> +	wake_lock_destroy(&unknown_wakeup);
> +	wake_lock_destroy(&main_wake_lock);
> +#ifdef CONFIG_WAKELOCK_STAT
> +	wake_lock_destroy(&deleted_wake_locks);
> +#endif
> +}
> +
> +core_initcall(wakelocks_init);
> +module_exit(wakelocks_exit);
> -- 
> 1.6.1

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

* Re: [PATCH 01/10] PM: Add wake lock api.
  2009-02-12 22:00   ` [PATCH 01/10] PM: Add wake lock api mark gross
@ 2009-02-12 23:06     ` Arve Hjønnevåg
  0 siblings, 0 replies; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-12 23:06 UTC (permalink / raw)
  To: mgross; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Thu, Feb 12, 2009 at 2:00 PM, mark gross <mgross@linux.intel.com> wrote:
>> +A locked wakelock, depending on its type, prevents the system from entering
>> +suspend or other low-power states. When creating a wakelock, you can select
>> +if it prevents suspend or low-power idle states.  If the type is set to
>> +WAKE_LOCK_SUSPEND, the wakelock prevents a full system suspend. If the type
>> +is WAKE_LOCK_IDLE, low-power states that cause large interrupt latencies, or
>> +that disable a set of interrupts, will not be entered from idle until the
>> +wakelocks are released. Unless the type is specified, this document refers
>> +to wakelocks with the type set to WAKE_LOCK_SUSPEND.
>
> If there are no wakelocks held, what code path pushes the system to a
> lower state?  (wake_unlock?)

wake_unlock, wake_lock_timeout or the expire timer may wake up the
suspend worker which calls pm_suspend.

> Would it make more sense to remove the suspend types of locks?  Those
> seem to be pretty contentious.

I can remove the idle wake lock type from the patch, but I prefer to
keep the type so we can easily add types. We need the idle lock on the
g1 for now, and anyone adds emergency or high priority sleep support
to linux, we may want a separate wakelock type for that.

> Do the ARM devices really go into a suspend state or just a low power
> state?

If by suspend state you mean something more than halt or
wait-for-interrupt, then yes. The on the G1 the cpu core is powered
down, and when its wakes up it starts at the reset vector. But, it is
fast enough to also be usable from idle.

>> +Wakelocks 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 locks the keypad-scan wakelock
>> +  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 locks
>> +  the input-event-queue wakelock.
>> +- The keypad-scan code detects that no keys are held and unlocks the
>> +  keypad-scan wakelock.
>
> Lets assume the system drops to suspend after 0ms of no-locks held.
> Don't we have a race between the keypad driver unlocking the only lock
> and inpu-event-queue lock getting taken?

The input-event-queue lock is locked before the keypad driver lock is unlocked.

>> +- The user-space input-event thread returns from select/poll, locks the
>> +  process-input-events wakelock and then calls read in the input-event device.
>> +- The input-event driver dequeues the key-event and, since the queue is now
>> +  empty, it unlocks the input-event-queue wakelock.
>> +- The user-space input-event thread returns from read. It determines that the
>> +  key should not wake up the full system, releases the process-input-events
>> +  wakelock and calls select or poll.
>> +
>> +                 Key pressed   Key released
>> +                     |             |
>> +keypad-scan          ++++++++++++++++++
>> +input-event-queue        +++          +++
>> +process-input-events       +++          +++
>> +
>
> it feels like there needs to be interlocking wakelocks to keep the
> system from unexpectedly falling asleep.  Or, some hysteresis slop time
> baked in to hide the races.

These locks overlap. There is no need for any slop time.

>> +static void __wake_lock(struct wake_lock *lock, long timeout, bool has_timeout)
>
> If the timeout == 0 or < 0 can you assume has_timeout?

No, wake_lock(lock) is different from wake_lock_timeout(lock, 0) or
wake_lock_timeout(lock, -1).


>> +/**
>> + * wake_lock() - Lock a wakelock
>> + * @lock:       The wakelock to lock.
>> + */
>> +void wake_lock(struct wake_lock *lock)
>
> Naming is a little confusing to me.  wake_lock(...) reads as an
> initialization / constructor type of function, but its really a locking
> operation.  Perhaps lock_wake_lock would be more clear.

So is spin_lock. Most init functions in the kernel have init in their name.

>> +/**
>> + * wake_lock() - Unlock a wakelock
>> + * @lock:       The wakelock to unlock.
>> + */
>> +void wake_unlock(struct wake_lock *lock)
>
> unlock_wake_lock?

That does not seem consistent with other kernel apis.


>> +int wake_lock_active(struct wake_lock *lock)
>> +{
>> +     return !!(lock->flags & WAKE_LOCK_ACTIVE);
>
> A double bang? '!!'  shouldn't this be syntax error?  looks odd.

No.

>
>> +}
>> +EXPORT_SYMBOL(wake_lock_active);
>> +
>> +void request_suspend_state(suspend_state_t state)
>> +{
>> +     unsigned long irqflags;
>> +     spin_lock_irqsave(&state_lock, irqflags);
> why not grab this lock after the debug_mask if block?

To make sure the debug output is correct. It prints state protected by
the spinlock.


-- 
Arve Hjønnevåg

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-12 11:31                   ` [PATCH 09/10] Input: Hold wake lock while event queue is not empty Matthew Garrett
@ 2009-02-13  0:27                     ` Arve Hjønnevåg
  2009-02-13  0:34                       ` Matthew Garrett
  0 siblings, 1 reply; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-13  0:27 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Thu, Feb 12, 2009 at 3:31 AM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> On Tue, Feb 10, 2009 at 05:49:14PM -0800, Arve Hjønnevåg wrote:
>
>>       spin_lock(&client->buffer_lock);
>> +     wake_lock_timeout(&client->wake_lock, 5 * HZ);
>
> Why the timeout version? If your input handler vanishes for more than 5
> seconds then presumably you should be thinking about watchdoging the
> entire system.

The timeout allows the system to eventually suspend if someone opened
the input device and but are not reading from it. We hit this once. I
can remove the timeout, but these bugs are more visible in the stats
if we keep timeout since the expire counts will be non-zero.

-- 
Arve Hjønnevåg

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-13  0:27                     ` Arve Hjønnevåg
@ 2009-02-13  0:34                       ` Matthew Garrett
  2009-02-13  0:38                         ` Arve Hjønnevåg
  0 siblings, 1 reply; 34+ messages in thread
From: Matthew Garrett @ 2009-02-13  0:34 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Thu, Feb 12, 2009 at 04:27:53PM -0800, Arve Hjønnevåg wrote:
> On Thu, Feb 12, 2009 at 3:31 AM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> > On Tue, Feb 10, 2009 at 05:49:14PM -0800, Arve Hjønnevåg wrote:
> >
> >>       spin_lock(&client->buffer_lock);
> >> +     wake_lock_timeout(&client->wake_lock, 5 * HZ);
> >
> > Why the timeout version? If your input handler vanishes for more than 5
> > seconds then presumably you should be thinking about watchdoging the
> > entire system.
> 
> The timeout allows the system to eventually suspend if someone opened
> the input device and but are not reading from it. We hit this once. I
> can remove the timeout, but these bugs are more visible in the stats
> if we keep timeout since the expire counts will be non-zero.

Mm. I'm not convinced about kernel behaviour that's designed to work 
around userspace failures. The assumption that your input consumer will 
act within 5 seconds is a policy decision, so should probably be left to 
userspace.

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

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-13  0:34                       ` Matthew Garrett
@ 2009-02-13  0:38                         ` Arve Hjønnevåg
  2009-02-13  0:40                           ` Matthew Garrett
  0 siblings, 1 reply; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-13  0:38 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Thu, Feb 12, 2009 at 4:34 PM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> On Thu, Feb 12, 2009 at 04:27:53PM -0800, Arve Hjønnevåg wrote:
>> On Thu, Feb 12, 2009 at 3:31 AM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
>> > On Tue, Feb 10, 2009 at 05:49:14PM -0800, Arve Hjønnevåg wrote:
>> >
>> >>       spin_lock(&client->buffer_lock);
>> >> +     wake_lock_timeout(&client->wake_lock, 5 * HZ);
>> >
>> > Why the timeout version? If your input handler vanishes for more than 5
>> > seconds then presumably you should be thinking about watchdoging the
>> > entire system.
>>
>> The timeout allows the system to eventually suspend if someone opened
>> the input device and but are not reading from it. We hit this once. I
>> can remove the timeout, but these bugs are more visible in the stats
>> if we keep timeout since the expire counts will be non-zero.
>
> Mm. I'm not convinced about kernel behaviour that's designed to work
> around userspace failures. The assumption that your input consumer will
> act within 5 seconds is a policy decision, so should probably be left to
> userspace.

I can make it a module param. I still need a default. I considering 0
(no wakelock), 5 seconds or infinite.

-- 
Arve Hjønnevåg

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-13  0:38                         ` Arve Hjønnevåg
@ 2009-02-13  0:40                           ` Matthew Garrett
  2009-02-13  0:52                             ` Arve Hjønnevåg
  0 siblings, 1 reply; 34+ messages in thread
From: Matthew Garrett @ 2009-02-13  0:40 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Thu, Feb 12, 2009 at 04:38:27PM -0800, Arve Hjønnevåg wrote:

> I can make it a module param. I still need a default. I considering 0
> (no wakelock), 5 seconds or infinite.

I'm not clear on this. Why do you need a default? Just fix your 
userspace to reboot the system if the input consumer stops responding 
and won't restart. Your system is entirely fucked at that point anyway.

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

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-13  0:40                           ` Matthew Garrett
@ 2009-02-13  0:52                             ` Arve Hjønnevåg
  2009-02-13  0:57                               ` Matthew Garrett
  0 siblings, 1 reply; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-13  0:52 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Thu, Feb 12, 2009 at 4:40 PM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> On Thu, Feb 12, 2009 at 04:38:27PM -0800, Arve Hjønnevåg wrote:
>
>> I can make it a module param. I still need a default. I considering 0
>> (no wakelock), 5 seconds or infinite.
>
> I'm not clear on this. Why do you need a default? Just fix your
> userspace to reboot the system if the input consumer stops responding
> and won't restart. Your system is entirely fucked at that point anyway.

That was not the bug. Another component opened one of the input
devices, but did not read events from it.

-- 
Arve Hjønnevåg

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-13  0:52                             ` Arve Hjønnevåg
@ 2009-02-13  0:57                               ` Matthew Garrett
  2009-02-13 23:06                                 ` Rafael J. Wysocki
  2009-02-13 23:51                                 ` Arve Hjønnevåg
  0 siblings, 2 replies; 34+ messages in thread
From: Matthew Garrett @ 2009-02-13  0:57 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Thu, Feb 12, 2009 at 04:52:22PM -0800, Arve Hjønnevåg wrote:

> That was not the bug. Another component opened one of the input
> devices, but did not read events from it.

It's not the job of the kernel to guard against userspace doing foolish 
things. Either you want to wait for input events to be consumed before 
suspend or you don't - arbitrary timeouts provide no guarantees about 
the correctness of your platform's behaviour. The default permissions on 
the event devices mean that the only components that could interfere 
with this are ones under your control, so fixing them seems like the 
sensible approach.

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

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-13  0:57                               ` Matthew Garrett
@ 2009-02-13 23:06                                 ` Rafael J. Wysocki
  2009-02-13 23:51                                 ` Arve Hjønnevåg
  1 sibling, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2009-02-13 23:06 UTC (permalink / raw)
  To: linux-pm; +Cc: ncunningham, u.luckas, swetland, Arve

On Friday 13 February 2009, Matthew Garrett wrote:
> On Thu, Feb 12, 2009 at 04:52:22PM -0800, Arve Hjønnevåg wrote:
> 
> > That was not the bug. Another component opened one of the input
> > devices, but did not read events from it.
> 
> It's not the job of the kernel to guard against userspace doing foolish 
> things.

Amen.

> Either you want to wait for input events to be consumed before 
> suspend or you don't - arbitrary timeouts provide no guarantees about 
> the correctness of your platform's behaviour. The default permissions on 
> the event devices mean that the only components that could interfere 
> with this are ones under your control, so fixing them seems like the 
> sensible approach.

This is exactly my point, but I couldn't find words to say it so clearly.

Thanks,
Rafael

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-13  0:57                               ` Matthew Garrett
  2009-02-13 23:06                                 ` Rafael J. Wysocki
@ 2009-02-13 23:51                                 ` Arve Hjønnevåg
  2009-02-14  0:09                                   ` Matthew Garrett
  1 sibling, 1 reply; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-13 23:51 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Thu, Feb 12, 2009 at 4:57 PM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> On Thu, Feb 12, 2009 at 04:52:22PM -0800, Arve Hjønnevåg wrote:
>
>> That was not the bug. Another component opened one of the input
>> devices, but did not read events from it.
>
> It's not the job of the kernel to guard against userspace doing foolish
> things.

Not true. The kernel in general tries to protect processes from
causing harm to other processes or to the kernel itself.

> Either you want to wait for input events to be consumed before
> suspend or you don't - arbitrary timeouts provide no guarantees about
> the correctness of your platform's behaviour. The default permissions on
> the event devices mean that the only components that could interfere
> with this are ones under your control, so fixing them seems like the
> sensible approach.

We did fix the bug. My point is that is it completely unreasonable for
the user space to code take more than 5 seconds to read an input
event. Trying to protecting the system (not the app) against this is
not unreasonable.

-- 
Arve Hjønnevåg

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-13 23:51                                 ` Arve Hjønnevåg
@ 2009-02-14  0:09                                   ` Matthew Garrett
  2009-02-14  0:13                                     ` Arve Hjønnevåg
  0 siblings, 1 reply; 34+ messages in thread
From: Matthew Garrett @ 2009-02-14  0:09 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Fri, Feb 13, 2009 at 03:51:40PM -0800, Arve Hjønnevåg wrote:
> On Thu, Feb 12, 2009 at 4:57 PM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> > It's not the job of the kernel to guard against userspace doing foolish
> > things.
> 
> Not true. The kernel in general tries to protect processes from
> causing harm to other processes or to the kernel itself.

Harm, yes. This isn't harm. It's undesirable, in the same way that an 
application taking a wake lock and then spinning until your battery runs 
out is undesirable. It's not the kernel's job to prevent that.

> > Either you want to wait for input events to be consumed before
> > suspend or you don't - arbitrary timeouts provide no guarantees about
> > the correctness of your platform's behaviour. The default permissions on
> > the event devices mean that the only components that could interfere
> > with this are ones under your control, so fixing them seems like the
> > sensible approach.
> 
> We did fix the bug. My point is that is it completely unreasonable for
> the user space to code take more than 5 seconds to read an input
> event. Trying to protecting the system (not the app) against this is
> not unreasonable.

Completely unreasoable *in your use case*. The kernel doesn't exist to 
satisfy only your use case - it has to satisfy as many as possible. 
That's why hardcoding policy decisions such as this timeout is a bad 
idea.

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

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-14  0:09                                   ` Matthew Garrett
@ 2009-02-14  0:13                                     ` Arve Hjønnevåg
  2009-02-14  0:18                                       ` Matthew Garrett
  0 siblings, 1 reply; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-14  0:13 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Fri, Feb 13, 2009 at 4:09 PM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> On Fri, Feb 13, 2009 at 03:51:40PM -0800, Arve Hjønnevåg wrote:
>> On Thu, Feb 12, 2009 at 4:57 PM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
>> > It's not the job of the kernel to guard against userspace doing foolish
>> > things.
>>
>> Not true. The kernel in general tries to protect processes from
>> causing harm to other processes or to the kernel itself.
>
> Harm, yes. This isn't harm. It's undesirable, in the same way that an
> application taking a wake lock and then spinning until your battery runs
> out is undesirable. It's not the kernel's job to prevent that.
>
>> > Either you want to wait for input events to be consumed before
>> > suspend or you don't - arbitrary timeouts provide no guarantees about
>> > the correctness of your platform's behaviour. The default permissions on
>> > the event devices mean that the only components that could interfere
>> > with this are ones under your control, so fixing them seems like the
>> > sensible approach.
>>
>> We did fix the bug. My point is that is it completely unreasonable for
>> the user space to code take more than 5 seconds to read an input
>> event. Trying to protecting the system (not the app) against this is
>> not unreasonable.
>
> Completely unreasoable *in your use case*. The kernel doesn't exist to
> satisfy only your use case - it has to satisfy as many as possible.
> That's why hardcoding policy decisions such as this timeout is a bad
> idea.

Which is why I offered to make it configurable.

-- 
Arve Hjønnevåg

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

* Re: [PATCH 09/10] Input: Hold wake lock while event queue is not empty.
  2009-02-14  0:13                                     ` Arve Hjønnevåg
@ 2009-02-14  0:18                                       ` Matthew Garrett
  0 siblings, 0 replies; 34+ messages in thread
From: Matthew Garrett @ 2009-02-14  0:18 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: swetland, linux-pm, u.luckas, ncunningham

On Fri, Feb 13, 2009 at 04:13:22PM -0800, Arve Hjønnevåg wrote:

> Which is why I offered to make it configurable.

If you don't trust your userspace then your argument suggests every wake 
lock that faces userspace has to have a timeout. When the logical 
extension to an answer to a problem is "Add a configuration option to 
almost every driver", you might want to rethink.

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

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

* Re: [RFC][PATCH 00/11] Android PM extensions (version 3)
  2009-02-11  1:49 [RFC][PATCH 00/11] Android PM extensions (version 3) Arve Hjønnevåg
  2009-02-11  1:49 ` [PATCH 01/10] PM: Add wake lock api Arve Hjønnevåg
@ 2009-02-17 21:05 ` Pavel Machek
  2009-02-19  1:43   ` Arve Hjønnevåg
  1 sibling, 1 reply; 34+ messages in thread
From: Pavel Machek @ 2009-02-17 21:05 UTC (permalink / raw)
  To: Arve Hj??nnev??g; +Cc: ncunningham, u.luckas, swetland, linux-pm

Hi!

> The following patch series adds two apis, wakelock and earlysuspend.
> The Android platform uses the earlysuspend api to turn the screen
> and some input devices on and off. The wakelock code determines when
> to enter the full suspend state.

earlysuspend is an ugly hack and wakelock is very wrong name at the
very least... as seen in previous discussion. Can we get that fixed?

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

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

* Re: [RFC][PATCH 00/11] Android PM extensions (version 3)
  2009-02-17 21:05 ` [RFC][PATCH 00/11] Android PM extensions (version 3) Pavel Machek
@ 2009-02-19  1:43   ` Arve Hjønnevåg
  2009-02-19 12:54     ` Rafael J. Wysocki
  2009-02-22 13:48     ` Pavel Machek
  0 siblings, 2 replies; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-19  1:43 UTC (permalink / raw)
  To: Pavel Machek; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Tue, Feb 17, 2009 at 1:05 PM, Pavel Machek <pavel@ucw.cz> wrote:
> Hi!
>
>> The following patch series adds two apis, wakelock and earlysuspend.
>> The Android platform uses the earlysuspend api to turn the screen
>> and some input devices on and off. The wakelock code determines when
>> to enter the full suspend state.
>
> earlysuspend is an ugly hack and wakelock is very wrong name at the
> very least... as seen in previous discussion. Can we get that fixed?

I don't have a fix for earlysuspend, but it is far less important than
wakelocks, so I can drop it from the patch series if that is
preferred.

Regarding the name, I don't agree with your statement that wakelock is
a very wrong name. Like I said before, you can view it as a
reader/writer lock where the readers protect the wake state of the
system. That said, if there is a better name that more than one person
can agree on, I can rename the api. Here is a list of suggestions I
have seen so far along with the api I think they dictate if the
existing functionality is to be preserved:

wake_lock:
- api: wake_lock_init, wake_lock_destroy, wake_lock, wake_lock_timout,
wake_unlock
- pros: matches android user space api.
- cons: Can be confused with mutual exclusion apis.

suspend_stop_valve:
- api: open/close?
- pros: ?
- cons: Api can be confused with device open/close.

suspend_lock/sleep_lock:
- api: same as wakelock, but replace wake with suspend or sleep
- pros: Sleep or suspend is more easily associated with power
management than wake by some people.
- cons: Can be confused with mutual exclusion apis, (suspend_lock) was
confusing to people that also wrote android user space code.

suspend_inhibitor: (from inhibit_suspend)
- api: suspend_inhibitor_init, suspend_inhibitor_destroy,
suspend_inhibit, suspend_inhibit_timeout, suspend_uninhibit
- pros: The effect is more obvious than *_lock.
- cons: Does not match android user space api (but less confusing than
suspend/sleep_lock).

-- 
Arve Hjønnevåg

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

* Re: [RFC][PATCH 00/11] Android PM extensions (version 3)
  2009-02-19  1:43   ` Arve Hjønnevåg
@ 2009-02-19 12:54     ` Rafael J. Wysocki
  2009-02-22 13:48     ` Pavel Machek
  1 sibling, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2009-02-19 12:54 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Thursday 19 February 2009, Arve Hjønnevåg wrote:
> On Tue, Feb 17, 2009 at 1:05 PM, Pavel Machek <pavel@ucw.cz> wrote:
> > Hi!
> >
> >> The following patch series adds two apis, wakelock and earlysuspend.
> >> The Android platform uses the earlysuspend api to turn the screen
> >> and some input devices on and off. The wakelock code determines when
> >> to enter the full suspend state.
> >
> > earlysuspend is an ugly hack and wakelock is very wrong name at the
> > very least... as seen in previous discussion. Can we get that fixed?
> 
> I don't have a fix for earlysuspend, but it is far less important than
> wakelocks, so I can drop it from the patch series if that is
> preferred.

Well, I think it should be rethought at least.

> Regarding the name, I don't agree with your statement that wakelock is
> a very wrong name. Like I said before, you can view it as a
> reader/writer lock where the readers protect the wake state of the
> system. That said, if there is a better name that more than one person
> can agree on, I can rename the api. Here is a list of suggestions I
> have seen so far along with the api I think they dictate if the
> existing functionality is to be preserved:
> 
> wake_lock:
> - api: wake_lock_init, wake_lock_destroy, wake_lock, wake_lock_timout,
> wake_unlock
> - pros: matches android user space api.
> - cons: Can be confused with mutual exclusion apis.
> 
> suspend_stop_valve:
> - api: open/close?
> - pros: ?
> - cons: Api can be confused with device open/close.
> 
> suspend_lock/sleep_lock:
> - api: same as wakelock, but replace wake with suspend or sleep
> - pros: Sleep or suspend is more easily associated with power
> management than wake by some people.
> - cons: Can be confused with mutual exclusion apis, (suspend_lock) was
> confusing to people that also wrote android user space code.
> 
> suspend_inhibitor: (from inhibit_suspend)
> - api: suspend_inhibitor_init, suspend_inhibitor_destroy,
> suspend_inhibit, suspend_inhibit_timeout, suspend_uninhibit
> - pros: The effect is more obvious than *_lock.
> - cons: Does not match android user space api (but less confusing than
> suspend/sleep_lock).

FWIW, I'd prefer the last one.

The timeouted version still remains questionable, though.

Thanks,
Rafael

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

* Re: [RFC][PATCH 00/11] Android PM extensions (version 3)
  2009-02-19  1:43   ` Arve Hjønnevåg
  2009-02-19 12:54     ` Rafael J. Wysocki
@ 2009-02-22 13:48     ` Pavel Machek
  2009-02-23 23:31       ` Arve Hjønnevåg
  1 sibling, 1 reply; 34+ messages in thread
From: Pavel Machek @ 2009-02-22 13:48 UTC (permalink / raw)
  To: Arve Hj?nnev?g; +Cc: ncunningham, u.luckas, swetland, linux-pm

Hi!

> > earlysuspend is an ugly hack and wakelock is very wrong name at the
> > very least... as seen in previous discussion. Can we get that fixed?
> 
> I don't have a fix for earlysuspend, but it is far less important than
> wakelocks, so I can drop it from the patch series if that is
> preferred.
> 
> Regarding the name, I don't agree with your statement that wakelock is
> a very wrong name. Like I said before, you can view it as a
> reader/writer lock where the readers protect the wake state of the
> system. That said, if there is a better name that more than one person
> can agree on, I can rename the api. Here is a list of suggestions I
> have seen so far along with the api I think they dictate if the
> existing functionality is to be preserved:


> suspend_inhibitor: (from inhibit_suspend)
> - api: suspend_inhibitor_init, suspend_inhibitor_destroy,
> suspend_inhibit, suspend_inhibit_timeout, suspend_uninhibit
> - pros: The effect is more obvious than *_lock.
> - cons: Does not match android user space api (but less confusing than
> suspend/sleep_lock).

I like this one, as does rafael, so :-).

I thought you are switching to /dev based api anyway so rename should
not be a problem?
								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] 34+ messages in thread

* Re: [RFC][PATCH 00/11] Android PM extensions (version 3)
  2009-02-22 13:48     ` Pavel Machek
@ 2009-02-23 23:31       ` Arve Hjønnevåg
  2009-02-23 23:54         ` Rafael J. Wysocki
  2009-02-25 13:23         ` Pavel Machek
  0 siblings, 2 replies; 34+ messages in thread
From: Arve Hjønnevåg @ 2009-02-23 23:31 UTC (permalink / raw)
  To: Pavel Machek; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Sun, Feb 22, 2009 at 5:48 AM, Pavel Machek <pavel@ucw.cz> wrote:
> Hi!
>
>> > earlysuspend is an ugly hack and wakelock is very wrong name at the
>> > very least... as seen in previous discussion. Can we get that fixed?
>>
>> I don't have a fix for earlysuspend, but it is far less important than
>> wakelocks, so I can drop it from the patch series if that is
>> preferred.
>>
>> Regarding the name, I don't agree with your statement that wakelock is
>> a very wrong name. Like I said before, you can view it as a
>> reader/writer lock where the readers protect the wake state of the
>> system. That said, if there is a better name that more than one person
>> can agree on, I can rename the api. Here is a list of suggestions I
>> have seen so far along with the api I think they dictate if the
>> existing functionality is to be preserved:
>
>
>> suspend_inhibitor: (from inhibit_suspend)
>> - api: suspend_inhibitor_init, suspend_inhibitor_destroy,
>> suspend_inhibit, suspend_inhibit_timeout, suspend_uninhibit
>> - pros: The effect is more obvious than *_lock.
>> - cons: Does not match android user space api (but less confusing than
>> suspend/sleep_lock).
>
> I like this one, as does rafael, so :-).
>
> I thought you are switching to /dev based api anyway so rename should
> not be a problem?

There is no requirement for the kernel api to match the user-space
api, it is just less confusing. The android java apis provide a
wakelock interface. We cannot change this api, but the both the in
kernel api and the api from the kernel to user space can be changed.

I did a quick poll here. 2 people preferred suspend_inhibitor and 3
people preferred wake_lock. The people who preferred wake_lock did not
like the word inhibit(or). Block(er) was suggested as an alternative.

-- 
Arve Hjønnevåg

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

* Re: [RFC][PATCH 00/11] Android PM extensions (version 3)
  2009-02-23 23:31       ` Arve Hjønnevåg
@ 2009-02-23 23:54         ` Rafael J. Wysocki
  2009-02-25 13:23         ` Pavel Machek
  1 sibling, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2009-02-23 23:54 UTC (permalink / raw)
  To: Arve Hjønnevåg; +Cc: ncunningham, u.luckas, swetland, linux-pm

On Tuesday 24 February 2009, Arve Hjønnevåg wrote:
> On Sun, Feb 22, 2009 at 5:48 AM, Pavel Machek <pavel@ucw.cz> wrote:
> > Hi!
> >
> >> > earlysuspend is an ugly hack and wakelock is very wrong name at the
> >> > very least... as seen in previous discussion. Can we get that fixed?
> >>
> >> I don't have a fix for earlysuspend, but it is far less important than
> >> wakelocks, so I can drop it from the patch series if that is
> >> preferred.
> >>
> >> Regarding the name, I don't agree with your statement that wakelock is
> >> a very wrong name. Like I said before, you can view it as a
> >> reader/writer lock where the readers protect the wake state of the
> >> system. That said, if there is a better name that more than one person
> >> can agree on, I can rename the api. Here is a list of suggestions I
> >> have seen so far along with the api I think they dictate if the
> >> existing functionality is to be preserved:
> >
> >
> >> suspend_inhibitor: (from inhibit_suspend)
> >> - api: suspend_inhibitor_init, suspend_inhibitor_destroy,
> >> suspend_inhibit, suspend_inhibit_timeout, suspend_uninhibit
> >> - pros: The effect is more obvious than *_lock.
> >> - cons: Does not match android user space api (but less confusing than
> >> suspend/sleep_lock).
> >
> > I like this one, as does rafael, so :-).
> >
> > I thought you are switching to /dev based api anyway so rename should
> > not be a problem?
> 
> There is no requirement for the kernel api to match the user-space
> api, it is just less confusing. The android java apis provide a
> wakelock interface. We cannot change this api, but the both the in
> kernel api and the api from the kernel to user space can be changed.
> 
> I did a quick poll here. 2 people preferred suspend_inhibitor and 3
> people preferred wake_lock. The people who preferred wake_lock did not
> like the word inhibit(or). Block(er) was suggested as an alternative.

"blocker" would be fine too, as far as I'm concerned.

Thanks,
Rafael

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

* Re: [RFC][PATCH 00/11] Android PM extensions (version 3)
  2009-02-23 23:31       ` Arve Hjønnevåg
  2009-02-23 23:54         ` Rafael J. Wysocki
@ 2009-02-25 13:23         ` Pavel Machek
  1 sibling, 0 replies; 34+ messages in thread
From: Pavel Machek @ 2009-02-25 13:23 UTC (permalink / raw)
  To: Arve Hj?nnev?g; +Cc: ncunningham, u.luckas, swetland, linux-pm

Hi!

> There is no requirement for the kernel api to match the user-space
> api, it is just less confusing. The android java apis provide a
> wakelock interface. We cannot change this api, but the both the in
> kernel api and the api from the kernel to user space can be changed.
> 
> I did a quick poll here. 2 people preferred suspend_inhibitor and 3
> people preferred wake_lock. The people who preferred wake_lock did not
> like the word inhibit(or). Block(er) was suggested as an alternative.

suspend_block / suspend_unblock sounds fine to me.

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

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

end of thread, other threads:[~2009-02-25 13:23 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-02-11  1:49 [RFC][PATCH 00/11] Android PM extensions (version 3) Arve Hjønnevåg
2009-02-11  1:49 ` [PATCH 01/10] PM: Add wake lock api Arve Hjønnevåg
2009-02-11  1:49   ` [PATCH 02/10] PM: wakelock: Override wakelocks when not using /sys/power/request_state Arve Hjønnevåg
2009-02-11  1:49     ` [PATCH 03/10] PM: wakelock: Add driver to access wakelocks from user-space Arve Hjønnevåg
2009-02-11  1:49       ` [PATCH 04/10] PM: wakelock: Abort task freezing if a wakelock is locked Arve Hjønnevåg
2009-02-11  1:49         ` [PATCH 05/10] PM: Add option to disable /sys/power/state interface Arve Hjønnevåg
2009-02-11  1:49           ` [PATCH 06/10] PM: Add early suspend api Arve Hjønnevåg
2009-02-11  1:49             ` [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes Arve Hjønnevåg
2009-02-11  1:49               ` [PATCH 08/10] PM: earlysuspend: Removing dependence on console Arve Hjønnevåg
2009-02-11  1:49                 ` [PATCH 09/10] Input: Hold wake lock while event queue is not empty Arve Hjønnevåg
2009-02-11  1:49                   ` [PATCH 10/10] ledtrig-sleep: Add led trigger for sleep debugging Arve Hjønnevåg
2009-02-12 11:31                   ` [PATCH 09/10] Input: Hold wake lock while event queue is not empty Matthew Garrett
2009-02-13  0:27                     ` Arve Hjønnevåg
2009-02-13  0:34                       ` Matthew Garrett
2009-02-13  0:38                         ` Arve Hjønnevåg
2009-02-13  0:40                           ` Matthew Garrett
2009-02-13  0:52                             ` Arve Hjønnevåg
2009-02-13  0:57                               ` Matthew Garrett
2009-02-13 23:06                                 ` Rafael J. Wysocki
2009-02-13 23:51                                 ` Arve Hjønnevåg
2009-02-14  0:09                                   ` Matthew Garrett
2009-02-14  0:13                                     ` Arve Hjønnevåg
2009-02-14  0:18                                       ` Matthew Garrett
2009-02-12 11:28               ` [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes Matthew Garrett
2009-02-12 11:34             ` [PATCH 06/10] PM: Add early suspend api Matthew Garrett
2009-02-12 22:00   ` [PATCH 01/10] PM: Add wake lock api mark gross
2009-02-12 23:06     ` Arve Hjønnevåg
2009-02-17 21:05 ` [RFC][PATCH 00/11] Android PM extensions (version 3) Pavel Machek
2009-02-19  1:43   ` Arve Hjønnevåg
2009-02-19 12:54     ` Rafael J. Wysocki
2009-02-22 13:48     ` Pavel Machek
2009-02-23 23:31       ` Arve Hjønnevåg
2009-02-23 23:54         ` Rafael J. Wysocki
2009-02-25 13:23         ` Pavel Machek

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.