[linux-pm] [PATCH 03/10] PM: wakelock: Add driver to access wakelocks from user-space

Arve Hjønnevåg arve at android.com
Tue Feb 10 17:49:08 PST 2009


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 at 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



More information about the linux-pm mailing list