aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-power42
-rw-r--r--drivers/base/power/wakeup.c1
-rw-r--r--kernel/power/Kconfig8
-rw-r--r--kernel/power/Makefile1
-rw-r--r--kernel/power/main.c41
-rw-r--r--kernel/power/power.h9
-rw-r--r--kernel/power/wakelock.c215
7 files changed, 317 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power
index 237c735db6c9..31725ffeeb3a 100644
--- a/Documentation/ABI/testing/sysfs-power
+++ b/Documentation/ABI/testing/sysfs-power
@@ -189,3 +189,45 @@ Description:
189 189
190 Reading from this file causes the last string successfully 190 Reading from this file causes the last string successfully
191 written to it to be returned. 191 written to it to be returned.
192
193What: /sys/power/wake_lock
194Date: February 2012
195Contact: Rafael J. Wysocki <rjw@sisk.pl>
196Description:
197 The /sys/power/wake_lock file allows user space to create
198 wakeup source objects and activate them on demand (if one of
199 those wakeup sources is active, reads from the
200 /sys/power/wakeup_count file block or return false). When a
201 string without white space is written to /sys/power/wake_lock,
202 it will be assumed to represent a wakeup source name. If there
203 is a wakeup source object with that name, it will be activated
204 (unless active already). Otherwise, a new wakeup source object
205 will be registered, assigned the given name and activated.
206 If a string written to /sys/power/wake_lock contains white
207 space, the part of the string preceding the white space will be
208 regarded as a wakeup source name and handled as descrived above.
209 The other part of the string will be regarded as a timeout (in
210 nanoseconds) such that the wakeup source will be automatically
211 deactivated after it has expired. The timeout, if present, is
212 set regardless of the current state of the wakeup source object
213 in question.
214
215 Reads from this file return a string consisting of the names of
216 wakeup sources created with the help of it that are active at
217 the moment, separated with spaces.
218
219
220What: /sys/power/wake_unlock
221Date: February 2012
222Contact: Rafael J. Wysocki <rjw@sisk.pl>
223Description:
224 The /sys/power/wake_unlock file allows user space to deactivate
225 wakeup sources created with the help of /sys/power/wake_lock.
226 When a string is written to /sys/power/wake_unlock, it will be
227 assumed to represent the name of a wakeup source to deactivate.
228 If a wakeup source object of that name exists and is active at
229 the moment, it will be deactivated.
230
231 Reads from this file return a string consisting of the names of
232 wakeup sources created with the help of /sys/power/wake_lock
233 that are inactive at the moment, separated with spaces.
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 2595b8d8fe1f..cbb463b3a750 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -133,6 +133,7 @@ void wakeup_source_add(struct wakeup_source *ws)
133 spin_lock_init(&ws->lock); 133 spin_lock_init(&ws->lock);
134 setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws); 134 setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws);
135 ws->active = false; 135 ws->active = false;
136 ws->last_time = ktime_get();
136 137
137 spin_lock_irq(&events_lock); 138 spin_lock_irq(&events_lock);
138 list_add_rcu(&ws->entry, &wakeup_sources); 139 list_add_rcu(&ws->entry, &wakeup_sources);
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 67947083f842..1d534076d33a 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -111,6 +111,14 @@ config PM_AUTOSLEEP
111 Allow the kernel to trigger a system transition into a global sleep 111 Allow the kernel to trigger a system transition into a global sleep
112 state automatically whenever there are no active wakeup sources. 112 state automatically whenever there are no active wakeup sources.
113 113
114config PM_WAKELOCKS
115 bool "User space wakeup sources interface"
116 depends on PM_SLEEP
117 default n
118 ---help---
119 Allow user space to create, activate and deactivate wakeup source
120 objects with the help of a sysfs-based interface.
121
114config PM_RUNTIME 122config PM_RUNTIME
115 bool "Run-time PM core functionality" 123 bool "Run-time PM core functionality"
116 depends on !IA64_HP_SIM 124 depends on !IA64_HP_SIM
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 010b2f7e148c..29472bff11ef 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -10,5 +10,6 @@ obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o
10obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ 10obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \
11 block_io.o 11 block_io.o
12obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o 12obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o
13obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o
13 14
14obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o 15obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff --git a/kernel/power/main.c b/kernel/power/main.c
index ba6a5645952d..54ec071de337 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -431,6 +431,43 @@ static ssize_t autosleep_store(struct kobject *kobj,
431 431
432power_attr(autosleep); 432power_attr(autosleep);
433#endif /* CONFIG_PM_AUTOSLEEP */ 433#endif /* CONFIG_PM_AUTOSLEEP */
434
435#ifdef CONFIG_PM_WAKELOCKS
436static ssize_t wake_lock_show(struct kobject *kobj,
437 struct kobj_attribute *attr,
438 char *buf)
439{
440 return pm_show_wakelocks(buf, true);
441}
442
443static ssize_t wake_lock_store(struct kobject *kobj,
444 struct kobj_attribute *attr,
445 const char *buf, size_t n)
446{
447 int error = pm_wake_lock(buf);
448 return error ? error : n;
449}
450
451power_attr(wake_lock);
452
453static ssize_t wake_unlock_show(struct kobject *kobj,
454 struct kobj_attribute *attr,
455 char *buf)
456{
457 return pm_show_wakelocks(buf, false);
458}
459
460static ssize_t wake_unlock_store(struct kobject *kobj,
461 struct kobj_attribute *attr,
462 const char *buf, size_t n)
463{
464 int error = pm_wake_unlock(buf);
465 return error ? error : n;
466}
467
468power_attr(wake_unlock);
469
470#endif /* CONFIG_PM_WAKELOCKS */
434#endif /* CONFIG_PM_SLEEP */ 471#endif /* CONFIG_PM_SLEEP */
435 472
436#ifdef CONFIG_PM_TRACE 473#ifdef CONFIG_PM_TRACE
@@ -487,6 +524,10 @@ static struct attribute * g[] = {
487#ifdef CONFIG_PM_AUTOSLEEP 524#ifdef CONFIG_PM_AUTOSLEEP
488 &autosleep_attr.attr, 525 &autosleep_attr.attr,
489#endif 526#endif
527#ifdef CONFIG_PM_WAKELOCKS
528 &wake_lock_attr.attr,
529 &wake_unlock_attr.attr,
530#endif
490#ifdef CONFIG_PM_DEBUG 531#ifdef CONFIG_PM_DEBUG
491 &pm_test_attr.attr, 532 &pm_test_attr.attr,
492#endif 533#endif
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 4cf80fa115d9..b0bd4beaebfe 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -282,3 +282,12 @@ static inline void pm_autosleep_unlock(void) {}
282static inline suspend_state_t pm_autosleep_state(void) { return PM_SUSPEND_ON; } 282static inline suspend_state_t pm_autosleep_state(void) { return PM_SUSPEND_ON; }
283 283
284#endif /* !CONFIG_PM_AUTOSLEEP */ 284#endif /* !CONFIG_PM_AUTOSLEEP */
285
286#ifdef CONFIG_PM_WAKELOCKS
287
288/* kernel/power/wakelock.c */
289extern ssize_t pm_show_wakelocks(char *buf, bool show_active);
290extern int pm_wake_lock(const char *buf);
291extern int pm_wake_unlock(const char *buf);
292
293#endif /* !CONFIG_PM_WAKELOCKS */
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
new file mode 100644
index 000000000000..579700665e8c
--- /dev/null
+++ b/kernel/power/wakelock.c
@@ -0,0 +1,215 @@
1/*
2 * kernel/power/wakelock.c
3 *
4 * User space wakeup sources support.
5 *
6 * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
7 *
8 * This code is based on the analogous interface allowing user space to
9 * manipulate wakelocks on Android.
10 */
11
12#include <linux/ctype.h>
13#include <linux/device.h>
14#include <linux/err.h>
15#include <linux/hrtimer.h>
16#include <linux/list.h>
17#include <linux/rbtree.h>
18#include <linux/slab.h>
19
20#define WL_NUMBER_LIMIT 100
21#define WL_GC_COUNT_MAX 100
22#define WL_GC_TIME_SEC 300
23
24static DEFINE_MUTEX(wakelocks_lock);
25
26struct wakelock {
27 char *name;
28 struct rb_node node;
29 struct wakeup_source ws;
30 struct list_head lru;
31};
32
33static struct rb_root wakelocks_tree = RB_ROOT;
34static LIST_HEAD(wakelocks_lru_list);
35static unsigned int number_of_wakelocks;
36static unsigned int wakelocks_gc_count;
37
38ssize_t pm_show_wakelocks(char *buf, bool show_active)
39{
40 struct rb_node *node;
41 struct wakelock *wl;
42 char *str = buf;
43 char *end = buf + PAGE_SIZE;
44
45 mutex_lock(&wakelocks_lock);
46
47 for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {
48 wl = rb_entry(node, struct wakelock, node);
49 if (wl->ws.active == show_active)
50 str += scnprintf(str, end - str, "%s ", wl->name);
51 }
52 if (str > buf)
53 str--;
54
55 str += scnprintf(str, end - str, "\n");
56
57 mutex_unlock(&wakelocks_lock);
58 return (str - buf);
59}
60
61static struct wakelock *wakelock_lookup_add(const char *name, size_t len,
62 bool add_if_not_found)
63{
64 struct rb_node **node = &wakelocks_tree.rb_node;
65 struct rb_node *parent = *node;
66 struct wakelock *wl;
67
68 while (*node) {
69 int diff;
70
71 parent = *node;
72 wl = rb_entry(*node, struct wakelock, node);
73 diff = strncmp(name, wl->name, len);
74 if (diff == 0) {
75 if (wl->name[len])
76 diff = -1;
77 else
78 return wl;
79 }
80 if (diff < 0)
81 node = &(*node)->rb_left;
82 else
83 node = &(*node)->rb_right;
84 }
85 if (!add_if_not_found)
86 return ERR_PTR(-EINVAL);
87
88 if (number_of_wakelocks > WL_NUMBER_LIMIT)
89 return ERR_PTR(-ENOSPC);
90
91 /* Not found, we have to add a new one. */
92 wl = kzalloc(sizeof(*wl), GFP_KERNEL);
93 if (!wl)
94 return ERR_PTR(-ENOMEM);
95
96 wl->name = kstrndup(name, len, GFP_KERNEL);
97 if (!wl->name) {
98 kfree(wl);
99 return ERR_PTR(-ENOMEM);
100 }
101 wl->ws.name = wl->name;
102 wakeup_source_add(&wl->ws);
103 rb_link_node(&wl->node, parent, node);
104 rb_insert_color(&wl->node, &wakelocks_tree);
105 list_add(&wl->lru, &wakelocks_lru_list);
106 number_of_wakelocks++;
107 return wl;
108}
109
110int pm_wake_lock(const char *buf)
111{
112 const char *str = buf;
113 struct wakelock *wl;
114 u64 timeout_ns = 0;
115 size_t len;
116 int ret = 0;
117
118 while (*str && !isspace(*str))
119 str++;
120
121 len = str - buf;
122 if (!len)
123 return -EINVAL;
124
125 if (*str && *str != '\n') {
126 /* Find out if there's a valid timeout string appended. */
127 ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
128 if (ret)
129 return -EINVAL;
130 }
131
132 mutex_lock(&wakelocks_lock);
133
134 wl = wakelock_lookup_add(buf, len, true);
135 if (IS_ERR(wl)) {
136 ret = PTR_ERR(wl);
137 goto out;
138 }
139 if (timeout_ns) {
140 u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
141
142 do_div(timeout_ms, NSEC_PER_MSEC);
143 __pm_wakeup_event(&wl->ws, timeout_ms);
144 } else {
145 __pm_stay_awake(&wl->ws);
146 }
147
148 list_move(&wl->lru, &wakelocks_lru_list);
149
150 out:
151 mutex_unlock(&wakelocks_lock);
152 return ret;
153}
154
155static void wakelocks_gc(void)
156{
157 struct wakelock *wl, *aux;
158 ktime_t now = ktime_get();
159
160 list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) {
161 u64 idle_time_ns;
162 bool active;
163
164 spin_lock_irq(&wl->ws.lock);
165 idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time));
166 active = wl->ws.active;
167 spin_unlock_irq(&wl->ws.lock);
168
169 if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC))
170 break;
171
172 if (!active) {
173 wakeup_source_remove(&wl->ws);
174 rb_erase(&wl->node, &wakelocks_tree);
175 list_del(&wl->lru);
176 kfree(wl->name);
177 kfree(wl);
178 number_of_wakelocks--;
179 }
180 }
181 wakelocks_gc_count = 0;
182}
183
184int pm_wake_unlock(const char *buf)
185{
186 struct wakelock *wl;
187 size_t len;
188 int ret = 0;
189
190 len = strlen(buf);
191 if (!len)
192 return -EINVAL;
193
194 if (buf[len-1] == '\n')
195 len--;
196
197 if (!len)
198 return -EINVAL;
199
200 mutex_lock(&wakelocks_lock);
201
202 wl = wakelock_lookup_add(buf, len, false);
203 if (IS_ERR(wl)) {
204 ret = PTR_ERR(wl);
205 goto out;
206 }
207 __pm_relax(&wl->ws);
208 list_move(&wl->lru, &wakelocks_lru_list);
209 if (++wakelocks_gc_count > WL_GC_COUNT_MAX)
210 wakelocks_gc();
211
212 out:
213 mutex_unlock(&wakelocks_lock);
214 return ret;
215}