diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-power | 42 | ||||
-rw-r--r-- | drivers/base/power/wakeup.c | 1 | ||||
-rw-r--r-- | kernel/power/Kconfig | 8 | ||||
-rw-r--r-- | kernel/power/Makefile | 1 | ||||
-rw-r--r-- | kernel/power/main.c | 41 | ||||
-rw-r--r-- | kernel/power/power.h | 9 | ||||
-rw-r--r-- | kernel/power/wakelock.c | 215 |
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 | |||
193 | What: /sys/power/wake_lock | ||
194 | Date: February 2012 | ||
195 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
196 | Description: | ||
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 | |||
220 | What: /sys/power/wake_unlock | ||
221 | Date: February 2012 | ||
222 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
223 | Description: | ||
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 | ||
114 | config 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 | |||
114 | config PM_RUNTIME | 122 | config 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 | |||
10 | obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ | 10 | obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ |
11 | block_io.o | 11 | block_io.o |
12 | obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o | 12 | obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o |
13 | obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o | ||
13 | 14 | ||
14 | obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o | 15 | obj-$(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 | ||
432 | power_attr(autosleep); | 432 | power_attr(autosleep); |
433 | #endif /* CONFIG_PM_AUTOSLEEP */ | 433 | #endif /* CONFIG_PM_AUTOSLEEP */ |
434 | |||
435 | #ifdef CONFIG_PM_WAKELOCKS | ||
436 | static 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 | |||
443 | static 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 | |||
451 | power_attr(wake_lock); | ||
452 | |||
453 | static 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 | |||
460 | static 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 | |||
468 | power_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) {} | |||
282 | static inline suspend_state_t pm_autosleep_state(void) { return PM_SUSPEND_ON; } | 282 | static 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 */ | ||
289 | extern ssize_t pm_show_wakelocks(char *buf, bool show_active); | ||
290 | extern int pm_wake_lock(const char *buf); | ||
291 | extern 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 | |||
24 | static DEFINE_MUTEX(wakelocks_lock); | ||
25 | |||
26 | struct wakelock { | ||
27 | char *name; | ||
28 | struct rb_node node; | ||
29 | struct wakeup_source ws; | ||
30 | struct list_head lru; | ||
31 | }; | ||
32 | |||
33 | static struct rb_root wakelocks_tree = RB_ROOT; | ||
34 | static LIST_HEAD(wakelocks_lru_list); | ||
35 | static unsigned int number_of_wakelocks; | ||
36 | static unsigned int wakelocks_gc_count; | ||
37 | |||
38 | ssize_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 | |||
61 | static 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 | |||
110 | int 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 | |||
155 | static 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 | |||
184 | int 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 | } | ||