[PATCH 2/8] PM: suspend_block: Add driver to access suspend blockers from user-space

Arve Hjønnevåg
2009-04-15T01:41:26Z

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

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

diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
index 743b870..35bc713 100644
--- a/Documentation/power/suspend-blockers.txt
+++ b/Documentation/power/suspend-blockers.txt
@@ -74,3 +74,20 @@ if (list_empty(&state->pending_work))
else
suspend_block(&state->suspend_blocker);

+User-space API
+==============
+
+To create a suspend_blocker from user-space, open the suspend_blocker device:
+ fd = open("/dev/suspend_blocker", O_RDWR | O_CLOEXEC);
+then call:
+ ioctl(fd, SUSPEND_BLOCKER_IOCTL_INIT(strlen(name)), name);
+
+To activate a suspend_blocker call:
+ ioctl(fd, SUSPEND_BLOCKER_IOCTL_BLOCK);
+
+To unblock call:
+ ioctl(fd, SUSPEND_BLOCKER_IOCTL_UNBLOCK);
+
+To destroy the suspend_blocker, close the device:
+ close(fd);
+
diff --git a/include/linux/suspend_block_dev.h b/include/linux/suspend_block_dev.h
new file mode 100644
index 0000000..a7c0bf9
--- /dev/null
+++ b/include/linux/suspend_block_dev.h
@@ -0,0 +1,25 @@
+/* include/linux/suspend_block_dev.h
+ *
+ * Copyright (C) 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SUSPEND_BLOCK_DEV_H
+#define _LINUX_SUSPEND_BLOCK_DEV_H
+
+#include <linux/ioctl.h>
+
+#define SUSPEND_BLOCKER_IOCTL_INIT(len) _IOC(_IOC_WRITE, 's', 0, len)
+#define SUSPEND_BLOCKER_IOCTL_BLOCK _IO('s', 1)
+#define SUSPEND_BLOCKER_IOCTL_UNBLOCK _IO('s', 3)
+
+#endif
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 9d1df13..2cf4c4d 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -126,6 +126,15 @@ config SUSPEND_BLOCK
/sys/power/request_state, the requested sleep state will be entered
when no suspend_blockers are active.

+config USER_SUSPEND_BLOCK
+ bool "Userspace suspend blockers"
+ depends on SUSPEND_BLOCK
+ default y
+ ---help---
+ User-space suspend block api. Creates a misc device with ioctls
+ to create, block and unblock a suspend_blocker. The suspend_blocker
+ will be deleted when the device is closed.
+
config HIBERNATION
bool "Hibernation (aka 'suspend to disk')"
depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 29cdc9e..5c524f1 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM) += main.o
obj-$(CONFIG_PM_SLEEP) += console.o
obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_SUSPEND_BLOCK) += suspend_block.o
+obj-$(CONFIG_USER_SUSPEND_BLOCK) += user_suspend_block.o
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o

obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff --git a/kernel/power/user_suspend_block.c b/kernel/power/user_suspend_block.c
new file mode 100644
index 0000000..782407c
--- /dev/null
+++ b/kernel/power/user_suspend_block.c
@@ -0,0 +1,125 @@
+/* kernel/power/user_suspend_block.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/suspend_block.h>
+#include <linux/suspend_block_dev.h>
+
+enum {
+ DEBUG_FAILURE = BIT(0),
+};
+static int debug_mask = DEBUG_FAILURE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static DEFINE_MUTEX(ioctl_lock);
+
+struct user_suspend_blocker {
+ struct suspend_blocker blocker;
+ char name[0];
+};
+
+static int create_user_suspend_blocker(struct file *file, void __user *name,
+ size_t name_len)
+{
+ struct user_suspend_blocker *bl;
+ if (file->private_data)
+ return -EBUSY;
+ bl = kzalloc(sizeof(*bl) + name_len + 1, GFP_KERNEL);
+ if (!bl)
+ return -ENOMEM;
+ if (copy_from_user(bl->name, name, name_len))
+ goto err_fault;
+ suspend_blocker_init(&bl->blocker, bl->name);
+ file->private_data = bl;
+ return 0;
+
+err_fault:
+ kfree(bl);
+ return -EFAULT;
+}
+
+static long user_suspend_blocker_ioctl(struct file *file, unsigned int cmd,
+ unsigned long _arg)
+{
+ void __user *arg = (void __user *)_arg;
+ struct user_suspend_blocker *bl;
+ long ret;
+
+ mutex_lock(&ioctl_lock);
+ if ((cmd & ~IOCSIZE_MASK) == SUSPEND_BLOCKER_IOCTL_INIT(0)) {
+ ret = create_user_suspend_blocker(file, arg, _IOC_SIZE(cmd));
+ goto done;
+ }
+ bl = file->private_data;
+ if (!bl) {
+ ret = -ENOENT;
+ goto done;
+ }
+ switch (cmd) {
+ case SUSPEND_BLOCKER_IOCTL_BLOCK:
+ suspend_block(&bl->blocker);
+ ret = 0;
+ break;
+ case SUSPEND_BLOCKER_IOCTL_UNBLOCK:
+ suspend_unblock(&bl->blocker);
+ ret = 0;
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+done:
+ if (ret && debug_mask & DEBUG_FAILURE)
+ pr_err("user_suspend_blocker_ioctl: cmd %x failed, %ld\n",
+ cmd, ret);
+ mutex_unlock(&ioctl_lock);
+ return ret;
+}
+
+static int user_suspend_blocker_release(struct inode *inode, struct file *file)
+{
+ struct user_suspend_blocker *bl = file->private_data;
+ if (!bl)
+ return 0;
+ suspend_blocker_destroy(&bl->blocker);
+ kfree(bl);
+ return 0;
+}
+
+const struct file_operations user_suspend_blocker_fops = {
+ .release = user_suspend_blocker_release,
+ .unlocked_ioctl = user_suspend_blocker_ioctl,
+};
+
+struct miscdevice user_suspend_blocker_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "suspend_blocker",
+ .fops = &user_suspend_blocker_fops,
+};
+
+static int __init user_suspend_blocker_init(void)
+{
+ return misc_register(&user_suspend_blocker_device);
+}
+
+static void __exit user_suspend_blocker_exit(void)
+{
+ misc_deregister(&user_suspend_blocker_device);
+}
+
+module_init(user_suspend_blocker_init);
+module_exit(user_suspend_blocker_exit);
--
1.6.1

_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org