xref: /openbmc/linux/kernel/power/wakelock.c (revision 9cfc5c90)
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/capability.h>
13 #include <linux/ctype.h>
14 #include <linux/device.h>
15 #include <linux/err.h>
16 #include <linux/hrtimer.h>
17 #include <linux/list.h>
18 #include <linux/rbtree.h>
19 #include <linux/slab.h>
20 #include <linux/workqueue.h>
21 
22 #include "power.h"
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 #ifdef CONFIG_PM_WAKELOCKS_GC
31 	struct list_head	lru;
32 #endif
33 };
34 
35 static struct rb_root wakelocks_tree = RB_ROOT;
36 
37 ssize_t pm_show_wakelocks(char *buf, bool show_active)
38 {
39 	struct rb_node *node;
40 	struct wakelock *wl;
41 	char *str = buf;
42 	char *end = buf + PAGE_SIZE;
43 
44 	mutex_lock(&wakelocks_lock);
45 
46 	for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {
47 		wl = rb_entry(node, struct wakelock, node);
48 		if (wl->ws.active == show_active)
49 			str += scnprintf(str, end - str, "%s ", wl->name);
50 	}
51 	if (str > buf)
52 		str--;
53 
54 	str += scnprintf(str, end - str, "\n");
55 
56 	mutex_unlock(&wakelocks_lock);
57 	return (str - buf);
58 }
59 
60 #if CONFIG_PM_WAKELOCKS_LIMIT > 0
61 static unsigned int number_of_wakelocks;
62 
63 static inline bool wakelocks_limit_exceeded(void)
64 {
65 	return number_of_wakelocks > CONFIG_PM_WAKELOCKS_LIMIT;
66 }
67 
68 static inline void increment_wakelocks_number(void)
69 {
70 	number_of_wakelocks++;
71 }
72 
73 static inline void decrement_wakelocks_number(void)
74 {
75 	number_of_wakelocks--;
76 }
77 #else /* CONFIG_PM_WAKELOCKS_LIMIT = 0 */
78 static inline bool wakelocks_limit_exceeded(void) { return false; }
79 static inline void increment_wakelocks_number(void) {}
80 static inline void decrement_wakelocks_number(void) {}
81 #endif /* CONFIG_PM_WAKELOCKS_LIMIT */
82 
83 #ifdef CONFIG_PM_WAKELOCKS_GC
84 #define WL_GC_COUNT_MAX	100
85 #define WL_GC_TIME_SEC	300
86 
87 static void __wakelocks_gc(struct work_struct *work);
88 static LIST_HEAD(wakelocks_lru_list);
89 static DECLARE_WORK(wakelock_work, __wakelocks_gc);
90 static unsigned int wakelocks_gc_count;
91 
92 static inline void wakelocks_lru_add(struct wakelock *wl)
93 {
94 	list_add(&wl->lru, &wakelocks_lru_list);
95 }
96 
97 static inline void wakelocks_lru_most_recent(struct wakelock *wl)
98 {
99 	list_move(&wl->lru, &wakelocks_lru_list);
100 }
101 
102 static void __wakelocks_gc(struct work_struct *work)
103 {
104 	struct wakelock *wl, *aux;
105 	ktime_t now;
106 
107 	mutex_lock(&wakelocks_lock);
108 
109 	now = ktime_get();
110 	list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) {
111 		u64 idle_time_ns;
112 		bool active;
113 
114 		spin_lock_irq(&wl->ws.lock);
115 		idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time));
116 		active = wl->ws.active;
117 		spin_unlock_irq(&wl->ws.lock);
118 
119 		if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC))
120 			break;
121 
122 		if (!active) {
123 			wakeup_source_remove(&wl->ws);
124 			rb_erase(&wl->node, &wakelocks_tree);
125 			list_del(&wl->lru);
126 			kfree(wl->name);
127 			kfree(wl);
128 			decrement_wakelocks_number();
129 		}
130 	}
131 	wakelocks_gc_count = 0;
132 
133 	mutex_unlock(&wakelocks_lock);
134 }
135 
136 static void wakelocks_gc(void)
137 {
138 	if (++wakelocks_gc_count <= WL_GC_COUNT_MAX)
139 		return;
140 
141 	schedule_work(&wakelock_work);
142 }
143 #else /* !CONFIG_PM_WAKELOCKS_GC */
144 static inline void wakelocks_lru_add(struct wakelock *wl) {}
145 static inline void wakelocks_lru_most_recent(struct wakelock *wl) {}
146 static inline void wakelocks_gc(void) {}
147 #endif /* !CONFIG_PM_WAKELOCKS_GC */
148 
149 static struct wakelock *wakelock_lookup_add(const char *name, size_t len,
150 					    bool add_if_not_found)
151 {
152 	struct rb_node **node = &wakelocks_tree.rb_node;
153 	struct rb_node *parent = *node;
154 	struct wakelock *wl;
155 
156 	while (*node) {
157 		int diff;
158 
159 		parent = *node;
160 		wl = rb_entry(*node, struct wakelock, node);
161 		diff = strncmp(name, wl->name, len);
162 		if (diff == 0) {
163 			if (wl->name[len])
164 				diff = -1;
165 			else
166 				return wl;
167 		}
168 		if (diff < 0)
169 			node = &(*node)->rb_left;
170 		else
171 			node = &(*node)->rb_right;
172 	}
173 	if (!add_if_not_found)
174 		return ERR_PTR(-EINVAL);
175 
176 	if (wakelocks_limit_exceeded())
177 		return ERR_PTR(-ENOSPC);
178 
179 	/* Not found, we have to add a new one. */
180 	wl = kzalloc(sizeof(*wl), GFP_KERNEL);
181 	if (!wl)
182 		return ERR_PTR(-ENOMEM);
183 
184 	wl->name = kstrndup(name, len, GFP_KERNEL);
185 	if (!wl->name) {
186 		kfree(wl);
187 		return ERR_PTR(-ENOMEM);
188 	}
189 	wl->ws.name = wl->name;
190 	wakeup_source_add(&wl->ws);
191 	rb_link_node(&wl->node, parent, node);
192 	rb_insert_color(&wl->node, &wakelocks_tree);
193 	wakelocks_lru_add(wl);
194 	increment_wakelocks_number();
195 	return wl;
196 }
197 
198 int pm_wake_lock(const char *buf)
199 {
200 	const char *str = buf;
201 	struct wakelock *wl;
202 	u64 timeout_ns = 0;
203 	size_t len;
204 	int ret = 0;
205 
206 	if (!capable(CAP_BLOCK_SUSPEND))
207 		return -EPERM;
208 
209 	while (*str && !isspace(*str))
210 		str++;
211 
212 	len = str - buf;
213 	if (!len)
214 		return -EINVAL;
215 
216 	if (*str && *str != '\n') {
217 		/* Find out if there's a valid timeout string appended. */
218 		ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
219 		if (ret)
220 			return -EINVAL;
221 	}
222 
223 	mutex_lock(&wakelocks_lock);
224 
225 	wl = wakelock_lookup_add(buf, len, true);
226 	if (IS_ERR(wl)) {
227 		ret = PTR_ERR(wl);
228 		goto out;
229 	}
230 	if (timeout_ns) {
231 		u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
232 
233 		do_div(timeout_ms, NSEC_PER_MSEC);
234 		__pm_wakeup_event(&wl->ws, timeout_ms);
235 	} else {
236 		__pm_stay_awake(&wl->ws);
237 	}
238 
239 	wakelocks_lru_most_recent(wl);
240 
241  out:
242 	mutex_unlock(&wakelocks_lock);
243 	return ret;
244 }
245 
246 int pm_wake_unlock(const char *buf)
247 {
248 	struct wakelock *wl;
249 	size_t len;
250 	int ret = 0;
251 
252 	if (!capable(CAP_BLOCK_SUSPEND))
253 		return -EPERM;
254 
255 	len = strlen(buf);
256 	if (!len)
257 		return -EINVAL;
258 
259 	if (buf[len-1] == '\n')
260 		len--;
261 
262 	if (!len)
263 		return -EINVAL;
264 
265 	mutex_lock(&wakelocks_lock);
266 
267 	wl = wakelock_lookup_add(buf, len, false);
268 	if (IS_ERR(wl)) {
269 		ret = PTR_ERR(wl);
270 		goto out;
271 	}
272 	__pm_relax(&wl->ws);
273 
274 	wakelocks_lru_most_recent(wl);
275 	wakelocks_gc();
276 
277  out:
278 	mutex_unlock(&wakelocks_lock);
279 	return ret;
280 }
281