Merge "PM / Sleep: Add user space interface for manipulating wakeup sources, v3"
diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power
index 237c735..31725ff 100644
--- a/Documentation/ABI/testing/sysfs-power
+++ b/Documentation/ABI/testing/sysfs-power
@@ -189,3 +189,45 @@
 
 		Reading from this file causes the last string successfully
 		written to it to be returned.
+
+What:		/sys/power/wake_lock
+Date:		February 2012
+Contact:	Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+		The /sys/power/wake_lock file allows user space to create
+		wakeup source objects and activate them on demand (if one of
+		those wakeup sources is active, reads from the
+		/sys/power/wakeup_count file block or return false).  When a
+		string without white space is written to /sys/power/wake_lock,
+		it will be assumed to represent a wakeup source name.  If there
+		is a wakeup source object with that name, it will be activated
+		(unless active already).  Otherwise, a new wakeup source object
+		will be registered, assigned the given name and activated.
+		If a string written to /sys/power/wake_lock contains white
+		space, the part of the string preceding the white space will be
+		regarded as a wakeup source name and handled as descrived above.
+		The other part of the string will be regarded as a timeout (in
+		nanoseconds) such that the wakeup source will be automatically
+		deactivated after it has expired.  The timeout, if present, is
+		set regardless of the current state of the wakeup source object
+		in question.
+
+		Reads from this file return a string consisting of the names of
+		wakeup sources created with the help of it that are active at
+		the moment, separated with spaces.
+
+
+What:		/sys/power/wake_unlock
+Date:		February 2012
+Contact:	Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+		The /sys/power/wake_unlock file allows user space to deactivate
+		wakeup sources created with the help of /sys/power/wake_lock.
+		When a string is written to /sys/power/wake_unlock, it will be
+		assumed to represent the name of a wakeup source to deactivate.
+		If a wakeup source object of that name exists and is active at
+		the moment, it will be deactivated.
+
+		Reads from this file return a string consisting of the names of
+		wakeup sources created with the help of /sys/power/wake_lock
+		that are inactive at the moment, separated with spaces.
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 2595b8d..cbb463b 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -133,6 +133,7 @@
 	spin_lock_init(&ws->lock);
 	setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws);
 	ws->active = false;
+	ws->last_time = ktime_get();
 
 	spin_lock_irq(&events_lock);
 	list_add_rcu(&ws->entry, &wakeup_sources);
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 479b90b..f142239 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -111,6 +111,14 @@
 	Allow the kernel to trigger a system transition into a global sleep
 	state automatically whenever there are no active wakeup sources.
 
+config PM_WAKELOCKS
+	bool "User space wakeup sources interface"
+	depends on PM_SLEEP
+	default n
+	---help---
+	Allow user space to create, activate and deactivate wakeup source
+	objects with the help of a sysfs-based interface.
+
 config PM_RUNTIME
 	bool "Run-time PM core functionality"
 	depends on !IA64_HP_SIM
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 010b2f7..29472bf 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -10,5 +10,6 @@
 obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o \
 				   block_io.o
 obj-$(CONFIG_PM_AUTOSLEEP)	+= autosleep.o
+obj-$(CONFIG_PM_WAKELOCKS)	+= wakelock.o
 
 obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 80b49ae..c2bfc34 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -520,6 +520,43 @@
 
 power_attr(autosleep);
 #endif /* CONFIG_PM_AUTOSLEEP */
+
+#ifdef CONFIG_PM_WAKELOCKS
+static ssize_t wake_lock_show(struct kobject *kobj,
+			      struct kobj_attribute *attr,
+			      char *buf)
+{
+	return pm_show_wakelocks(buf, true);
+}
+
+static ssize_t wake_lock_store(struct kobject *kobj,
+			       struct kobj_attribute *attr,
+			       const char *buf, size_t n)
+{
+	int error = pm_wake_lock(buf);
+	return error ? error : n;
+}
+
+power_attr(wake_lock);
+
+static ssize_t wake_unlock_show(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				char *buf)
+{
+	return pm_show_wakelocks(buf, false);
+}
+
+static ssize_t wake_unlock_store(struct kobject *kobj,
+				 struct kobj_attribute *attr,
+				 const char *buf, size_t n)
+{
+	int error = pm_wake_unlock(buf);
+	return error ? error : n;
+}
+
+power_attr(wake_unlock);
+
+#endif /* CONFIG_PM_WAKELOCKS */
 #endif /* CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM_TRACE
@@ -583,6 +620,10 @@
 #ifdef CONFIG_PM_AUTOSLEEP
 	&autosleep_attr.attr,
 #endif
+#ifdef CONFIG_PM_WAKELOCKS
+	&wake_lock_attr.attr,
+	&wake_unlock_attr.attr,
+#endif
 #ifdef CONFIG_PM_DEBUG
 	&pm_test_attr.attr,
 #endif
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 4cf80fa..b0bd4be 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -282,3 +282,12 @@
 static inline suspend_state_t pm_autosleep_state(void) { return PM_SUSPEND_ON; }
 
 #endif /* !CONFIG_PM_AUTOSLEEP */
+
+#ifdef CONFIG_PM_WAKELOCKS
+
+/* kernel/power/wakelock.c */
+extern ssize_t pm_show_wakelocks(char *buf, bool show_active);
+extern int pm_wake_lock(const char *buf);
+extern int pm_wake_unlock(const char *buf);
+
+#endif /* !CONFIG_PM_WAKELOCKS */
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
index 2583856..5797006 100644
--- a/kernel/power/wakelock.c
+++ b/kernel/power/wakelock.c
@@ -1,712 +1,215 @@
-/* kernel/power/wakelock.c
+/*
+ * kernel/power/wakelock.c
  *
- * Copyright (C) 2005-2008 Google, Inc.
+ * User space wakeup sources support.
  *
- * 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.
+ * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
  *
- * 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.
- *
+ * This code is based on the analogous interface allowing user space to
+ * manipulate wakelocks on Android.
  */
 
-#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"
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
 
-enum {
-	DEBUG_EXIT_SUSPEND = 1U << 0,
-	DEBUG_WAKEUP = 1U << 1,
-	DEBUG_SUSPEND = 1U << 2,
-	DEBUG_EXPIRE = 1U << 3,
-	DEBUG_WAKE_LOCK = 1U << 4,
+#define WL_NUMBER_LIMIT	100
+#define WL_GC_COUNT_MAX	100
+#define WL_GC_TIME_SEC	300
+
+static DEFINE_MUTEX(wakelocks_lock);
+
+struct wakelock {
+	char			*name;
+	struct rb_node		node;
+	struct wakeup_source	ws;
+	struct list_head	lru;
 };
-static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP;
-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 struct rb_root wakelocks_tree = RB_ROOT;
+static LIST_HEAD(wakelocks_lru_list);
+static unsigned int number_of_wakelocks;
+static unsigned int wakelocks_gc_count;
 
-static DEFINE_SPINLOCK(list_lock);
-static LIST_HEAD(inactive_locks);
-static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];
-static int current_event_num;
-static int suspend_sys_sync_count;
-static DEFINE_SPINLOCK(suspend_sys_sync_lock);
-static struct workqueue_struct *suspend_sys_sync_work_queue;
-static DECLARE_COMPLETION(suspend_sys_sync_comp);
-struct workqueue_struct *suspend_work_queue;
-struct wake_lock main_wake_lock;
-suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
-static struct wake_lock unknown_wakeup;
-static struct wake_lock suspend_backoff_lock;
-
-#define SUSPEND_BACKOFF_THRESHOLD	10
-#define SUSPEND_BACKOFF_INTERVAL	10000
-
-static unsigned suspend_short_count;
-
-#ifdef CONFIG_WAKELOCK_STAT
-static struct wake_lock deleted_wake_locks;
-static ktime_t last_sleep_time_update;
-static int wait_for_wakeup;
-
-int get_expired_time(struct wake_lock *lock, ktime_t *expire_time)
+ssize_t pm_show_wakelocks(char *buf, bool show_active)
 {
-	struct timespec ts;
-	struct timespec kt;
-	struct timespec tomono;
-	struct timespec delta;
-	struct timespec sleep;
-	long timeout;
+	struct rb_node *node;
+	struct wakelock *wl;
+	char *str = buf;
+	char *end = buf + PAGE_SIZE;
 
-	if (!(lock->flags & WAKE_LOCK_AUTO_EXPIRE))
-		return 0;
-	get_xtime_and_monotonic_and_sleep_offset(&kt, &tomono, &sleep);
-	timeout = lock->expires - jiffies;
-	if (timeout > 0)
-		return 0;
-	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 1;
-}
+	mutex_lock(&wakelocks_lock);
 
-
-static int print_lock_stat(struct seq_file *m, 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;
-		int expired = 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;
+	for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {
+		wl = rb_entry(node, struct wakelock, node);
+		if (wl->ws.active == show_active)
+			str += scnprintf(str, end - str, "%s ", wl->name);
 	}
+	if (str > buf)
+		str--;
 
-	return seq_printf(m,
-		     "\"%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));
+	str += scnprintf(str, end - str, "\n");
+
+	mutex_unlock(&wakelocks_lock);
+	return (str - buf);
 }
 
-static int wakelock_stats_show(struct seq_file *m, void *unused)
+static struct wakelock *wakelock_lookup_add(const char *name, size_t len,
+					    bool add_if_not_found)
 {
-	unsigned long irqflags;
-	struct wake_lock *lock;
-	int ret;
-	int type;
+	struct rb_node **node = &wakelocks_tree.rb_node;
+	struct rb_node *parent = *node;
+	struct wakelock *wl;
 
-	spin_lock_irqsave(&list_lock, irqflags);
+	while (*node) {
+		int diff;
 
-	ret = seq_puts(m, "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)
-		ret = print_lock_stat(m, lock);
-	for (type = 0; type < WAKE_LOCK_TYPE_COUNT; type++) {
-		list_for_each_entry(lock, &active_wake_locks[type], link)
-			ret = print_lock_stat(m, lock);
-	}
-	spin_unlock_irqrestore(&list_lock, irqflags);
-	return 0;
-}
-
-static void wake_unlock_stat_locked(struct wake_lock *lock, int expired)
-{
-	ktime_t duration;
-	ktime_t now;
-	if (!(lock->flags & WAKE_LOCK_ACTIVE))
-		return;
-	if (get_expired_time(lock, &now))
-		expired = 1;
-	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 update_sleep_wait_stats_locked(int done)
-{
-	struct wake_lock *lock;
-	ktime_t now, etime, elapsed, add;
-	int 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 = get_expired_time(lock, &etime);
-		if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
-			if (expired)
-				add = ktime_sub(etime, last_sleep_time_update);
+		parent = *node;
+		wl = rb_entry(*node, struct wakelock, node);
+		diff = strncmp(name, wl->name, len);
+		if (diff == 0) {
+			if (wl->name[len])
+				diff = -1;
 			else
-				add = elapsed;
-			lock->stat.prevent_suspend_time = ktime_add(
-				lock->stat.prevent_suspend_time, add);
+				return wl;
 		}
-		if (done || expired)
-			lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
+		if (diff < 0)
+			node = &(*node)->rb_left;
 		else
-			lock->flags |= WAKE_LOCK_PREVENTING_SUSPEND;
+			node = &(*node)->rb_right;
 	}
-	last_sleep_time_update = now;
-}
-#endif
+	if (!add_if_not_found)
+		return ERR_PTR(-EINVAL);
 
+	if (number_of_wakelocks > WL_NUMBER_LIMIT)
+		return ERR_PTR(-ENOSPC);
 
-static void expire_wake_lock(struct wake_lock *lock)
-{
-#ifdef CONFIG_WAKELOCK_STAT
-	wake_unlock_stat_locked(lock, 1);
-#endif
-	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);
-}
+	/* Not found, we have to add a new one. */
+	wl = kzalloc(sizeof(*wl), GFP_KERNEL);
+	if (!wl)
+		return ERR_PTR(-ENOMEM);
 
-/* Caller must acquire the list_lock spinlock */
-static void print_active_locks(int type)
-{
-	struct wake_lock *lock;
-	bool print_expired = true;
-
-	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
-	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("active wake lock %s, time left %ld\n",
-					lock->name, timeout);
-			else if (print_expired)
-				pr_info("wake lock %s, expired\n", lock->name);
-		} else {
-			pr_info("active wake lock %s\n", lock->name);
-			if (!(debug_mask & DEBUG_EXPIRE))
-				print_expired = false;
-		}
+	wl->name = kstrndup(name, len, GFP_KERNEL);
+	if (!wl->name) {
+		kfree(wl);
+		return ERR_PTR(-ENOMEM);
 	}
+	wl->ws.name = wl->name;
+	wakeup_source_add(&wl->ws);
+	rb_link_node(&wl->node, parent, node);
+	rb_insert_color(&wl->node, &wakelocks_tree);
+	list_add(&wl->lru, &wakelocks_lru_list);
+	number_of_wakelocks++;
+	return wl;
 }
 
-static long has_wake_lock_locked(int type)
+int pm_wake_lock(const char *buf)
 {
-	struct wake_lock *lock, *n;
-	long max_timeout = 0;
+	const char *str = buf;
+	struct wakelock *wl;
+	u64 timeout_ns = 0;
+	size_t len;
+	int ret = 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;
+	while (*str && !isspace(*str))
+		str++;
+
+	len = str - buf;
+	if (!len)
+		return -EINVAL;
+
+	if (*str && *str != '\n') {
+		/* Find out if there's a valid timeout string appended. */
+		ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
+		if (ret)
+			return -EINVAL;
 	}
-	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);
-	if (ret && (debug_mask & DEBUG_WAKEUP) && type == WAKE_LOCK_SUSPEND)
-		print_active_locks(type);
-	spin_unlock_irqrestore(&list_lock, irqflags);
+	mutex_lock(&wakelocks_lock);
+
+	wl = wakelock_lookup_add(buf, len, true);
+	if (IS_ERR(wl)) {
+		ret = PTR_ERR(wl);
+		goto out;
+	}
+	if (timeout_ns) {
+		u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
+
+		do_div(timeout_ms, NSEC_PER_MSEC);
+		__pm_wakeup_event(&wl->ws, timeout_ms);
+	} else {
+		__pm_stay_awake(&wl->ws);
+	}
+
+	list_move(&wl->lru, &wakelocks_lru_list);
+
+ out:
+	mutex_unlock(&wakelocks_lock);
 	return ret;
 }
 
-static void suspend_sys_sync(struct work_struct *work)
+static void wakelocks_gc(void)
 {
-	if (debug_mask & DEBUG_SUSPEND)
-		pr_info("PM: Syncing filesystems...\n");
+	struct wakelock *wl, *aux;
+	ktime_t now = ktime_get();
 
-	sys_sync();
+	list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) {
+		u64 idle_time_ns;
+		bool active;
 
-	if (debug_mask & DEBUG_SUSPEND)
-		pr_info("sync done.\n");
+		spin_lock_irq(&wl->ws.lock);
+		idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time));
+		active = wl->ws.active;
+		spin_unlock_irq(&wl->ws.lock);
 
-	spin_lock(&suspend_sys_sync_lock);
-	suspend_sys_sync_count--;
-	spin_unlock(&suspend_sys_sync_lock);
-}
-static DECLARE_WORK(suspend_sys_sync_work, suspend_sys_sync);
+		if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC))
+			break;
 
-void suspend_sys_sync_queue(void)
-{
-	int ret;
-
-	spin_lock(&suspend_sys_sync_lock);
-	ret = queue_work(suspend_sys_sync_work_queue, &suspend_sys_sync_work);
-	if (ret)
-		suspend_sys_sync_count++;
-	spin_unlock(&suspend_sys_sync_lock);
-}
-
-static bool suspend_sys_sync_abort;
-static void suspend_sys_sync_handler(unsigned long);
-static DEFINE_TIMER(suspend_sys_sync_timer, suspend_sys_sync_handler, 0, 0);
-/* value should be less then half of input event wake lock timeout value
- * which is currently set to 5*HZ (see drivers/input/evdev.c)
- */
-#define SUSPEND_SYS_SYNC_TIMEOUT (HZ/4)
-static void suspend_sys_sync_handler(unsigned long arg)
-{
-	if (suspend_sys_sync_count == 0) {
-		complete(&suspend_sys_sync_comp);
-	} else if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
-		suspend_sys_sync_abort = true;
-		complete(&suspend_sys_sync_comp);
-	} else {
-		mod_timer(&suspend_sys_sync_timer, jiffies +
-				SUSPEND_SYS_SYNC_TIMEOUT);
-	}
-}
-
-int suspend_sys_sync_wait(void)
-{
-	suspend_sys_sync_abort = false;
-
-	if (suspend_sys_sync_count != 0) {
-		mod_timer(&suspend_sys_sync_timer, jiffies +
-				SUSPEND_SYS_SYNC_TIMEOUT);
-		wait_for_completion(&suspend_sys_sync_comp);
-	}
-	if (suspend_sys_sync_abort) {
-		pr_info("suspend aborted....while waiting for sys_sync\n");
-		return -EAGAIN;
-	}
-
-	return 0;
-}
-
-static void suspend_backoff(void)
-{
-	pr_info("suspend: too many immediate wakeups, back off\n");
-	wake_lock_timeout(&suspend_backoff_lock,
-			  msecs_to_jiffies(SUSPEND_BACKOFF_INTERVAL));
-}
-
-static void suspend(struct work_struct *work)
-{
-	int ret;
-	int entry_event_num;
-	struct timespec ts_entry, ts_exit;
-
-	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;
-	suspend_sys_sync_queue();
-	if (debug_mask & DEBUG_SUSPEND)
-		pr_info("suspend: enter suspend\n");
-	getnstimeofday(&ts_entry);
-	ret = pm_suspend(requested_suspend_state);
-	getnstimeofday(&ts_exit);
-
-	if (debug_mask & DEBUG_EXIT_SUSPEND) {
-		struct rtc_time tm;
-		rtc_time_to_tm(ts_exit.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_exit.tv_nsec);
-	}
-
-	if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) {
-		++suspend_short_count;
-
-		if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) {
-			suspend_backoff();
-			suspend_short_count = 0;
+		if (!active) {
+			wakeup_source_remove(&wl->ws);
+			rb_erase(&wl->node, &wakelocks_tree);
+			list_del(&wl->lru);
+			kfree(wl->name);
+			kfree(wl);
+			number_of_wakelocks--;
 		}
-	} else {
-		suspend_short_count = 0;
 	}
+	wakelocks_gc_count = 0;
+}
 
-	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);
+int pm_wake_unlock(const char *buf)
+{
+	struct wakelock *wl;
+	size_t len;
+	int ret = 0;
+
+	len = strlen(buf);
+	if (!len)
+		return -EINVAL;
+
+	if (buf[len-1] == '\n')
+		len--;
+
+	if (!len)
+		return -EINVAL;
+
+	mutex_lock(&wakelocks_lock);
+
+	wl = wakelock_lookup_add(buf, len, false);
+	if (IS_ERR(wl)) {
+		ret = PTR_ERR(wl);
+		goto out;
 	}
-}
-static DECLARE_WORK(suspend_work, suspend);
+	__pm_relax(&wl->ws);
+	list_move(&wl->lru, &wakelocks_lru_list);
+	if (++wakelocks_gc_count > WL_GC_COUNT_MAX)
+		wakelocks_gc();
 
-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");
-	spin_lock_irqsave(&list_lock, irqflags);
-	if (debug_mask & DEBUG_SUSPEND)
-		print_active_locks(WAKE_LOCK_SUSPEND);
-	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 device *dev)
-{
-	int ret = has_wake_lock(WAKE_LOCK_SUSPEND) ? -EAGAIN : 0;
-#ifdef CONFIG_WAKELOCK_STAT
-	wait_for_wakeup = !ret;
-#endif
-	if (debug_mask & DEBUG_SUSPEND)
-		pr_info("power_suspend_late return %d\n", ret);
+ out:
+	mutex_unlock(&wakelocks_lock);
 	return ret;
 }
-
-static struct dev_pm_ops power_driver_pm_ops = {
-	.suspend_noirq = power_suspend_late,
-};
-
-static struct platform_driver power_driver = {
-	.driver.name = "power",
-	.driver.pm = &power_driver_pm_ops,
-};
-static struct platform_device power_device = {
-	.name = "power",
-};
-
-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);
-#ifdef CONFIG_WAKELOCK_STAT
-	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);
-#endif
-	lock->flags = (type & WAKE_LOCK_TYPE_MASK) | WAKE_LOCK_INITIALIZED;
-
-	INIT_LIST_HEAD(&lock->link);
-	spin_lock_irqsave(&list_lock, irqflags);
-	list_add(&lock->link, &inactive_locks);
-	spin_unlock_irqrestore(&list_lock, irqflags);
-}
-EXPORT_SYMBOL(wake_lock_init);
-
-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);
-	lock->flags &= ~WAKE_LOCK_INITIALIZED;
-#ifdef CONFIG_WAKELOCK_STAT
-	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);
-	}
-#endif
-	list_del(&lock->link);
-	spin_unlock_irqrestore(&list_lock, irqflags);
-}
-EXPORT_SYMBOL(wake_lock_destroy);
-
-static void wake_lock_internal(
-	struct wake_lock *lock, long timeout, int 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));
-#ifdef CONFIG_WAKELOCK_STAT
-	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 = 0;
-		lock->stat.wakeup_count++;
-	}
-	if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
-	    (long)(lock->expires - jiffies) <= 0) {
-		wake_unlock_stat_locked(lock, 0);
-		lock->stat.last_time = ktime_get();
-	}
-#endif
-	if (!(lock->flags & WAKE_LOCK_ACTIVE)) {
-		lock->flags |= WAKE_LOCK_ACTIVE;
-#ifdef CONFIG_WAKELOCK_STAT
-		lock->stat.last_time = ktime_get();
-#endif
-	}
-	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;
-		list_add(&lock->link, &active_wake_locks[type]);
-	}
-	if (type == WAKE_LOCK_SUSPEND) {
-		current_event_num++;
-#ifdef CONFIG_WAKELOCK_STAT
-		if (lock == &main_wake_lock)
-			update_sleep_wait_stats_locked(1);
-		else if (!wake_lock_active(&main_wake_lock))
-			update_sleep_wait_stats_locked(0);
-#endif
-		if (has_timeout)
-			expire_in = has_wake_lock_locked(type);
-		else
-			expire_in = -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);
-}
-
-void wake_lock(struct wake_lock *lock)
-{
-	wake_lock_internal(lock, 0, 0);
-}
-EXPORT_SYMBOL(wake_lock);
-
-void wake_lock_timeout(struct wake_lock *lock, long timeout)
-{
-	wake_lock_internal(lock, timeout, 1);
-}
-EXPORT_SYMBOL(wake_lock_timeout);
-
-void wake_unlock(struct wake_lock *lock)
-{
-	int type;
-	unsigned long irqflags;
-	spin_lock_irqsave(&list_lock, irqflags);
-	type = lock->flags & WAKE_LOCK_TYPE_MASK;
-#ifdef CONFIG_WAKELOCK_STAT
-	wake_unlock_stat_locked(lock, 0);
-#endif
-	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);
-	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);
-#ifdef CONFIG_WAKELOCK_STAT
-			update_sleep_wait_stats_locked(0);
-#endif
-		}
-	}
-	spin_unlock_irqrestore(&list_lock, irqflags);
-}
-EXPORT_SYMBOL(wake_unlock);
-
-int wake_lock_active(struct wake_lock *lock)
-{
-	return !!(lock->flags & WAKE_LOCK_ACTIVE);
-}
-EXPORT_SYMBOL(wake_lock_active);
-
-static int wakelock_stats_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, wakelock_stats_show, NULL);
-}
-
-static const struct file_operations wakelock_stats_fops = {
-	.owner = THIS_MODULE,
-	.open = wakelock_stats_open,
-	.read = seq_read,
-	.llseek = seq_lseek,
-	.release = single_release,
-};
-
-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");
-	wake_lock_init(&suspend_backoff_lock, WAKE_LOCK_SUSPEND,
-		       "suspend_backoff");
-
-	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;
-	}
-
-	INIT_COMPLETION(suspend_sys_sync_comp);
-	suspend_sys_sync_work_queue =
-		create_singlethread_workqueue("suspend_sys_sync");
-	if (suspend_sys_sync_work_queue == NULL) {
-		ret = -ENOMEM;
-		goto err_suspend_sys_sync_work_queue;
-	}
-
-	suspend_work_queue = create_singlethread_workqueue("suspend");
-	if (suspend_work_queue == NULL) {
-		ret = -ENOMEM;
-		goto err_suspend_work_queue;
-	}
-
-#ifdef CONFIG_WAKELOCK_STAT
-	proc_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_fops);
-#endif
-
-	return 0;
-
-err_suspend_work_queue:
-err_suspend_sys_sync_work_queue:
-	platform_driver_unregister(&power_driver);
-err_platform_driver_register:
-	platform_device_unregister(&power_device);
-err_platform_device_register:
-	wake_lock_destroy(&suspend_backoff_lock);
-	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);
-	destroy_workqueue(suspend_sys_sync_work_queue);
-	platform_driver_unregister(&power_driver);
-	platform_device_unregister(&power_device);
-	wake_lock_destroy(&suspend_backoff_lock);
-	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);