xref: /openbmc/linux/kernel/power/wakelock.c (revision b86ff9820fd5df69295273b9aa68e58786ffc23f)
1*b86ff982SRafael J. Wysocki /*
2*b86ff982SRafael J. Wysocki  * kernel/power/wakelock.c
3*b86ff982SRafael J. Wysocki  *
4*b86ff982SRafael J. Wysocki  * User space wakeup sources support.
5*b86ff982SRafael J. Wysocki  *
6*b86ff982SRafael J. Wysocki  * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
7*b86ff982SRafael J. Wysocki  *
8*b86ff982SRafael J. Wysocki  * This code is based on the analogous interface allowing user space to
9*b86ff982SRafael J. Wysocki  * manipulate wakelocks on Android.
10*b86ff982SRafael J. Wysocki  */
11*b86ff982SRafael J. Wysocki 
12*b86ff982SRafael J. Wysocki #include <linux/ctype.h>
13*b86ff982SRafael J. Wysocki #include <linux/device.h>
14*b86ff982SRafael J. Wysocki #include <linux/err.h>
15*b86ff982SRafael J. Wysocki #include <linux/hrtimer.h>
16*b86ff982SRafael J. Wysocki #include <linux/list.h>
17*b86ff982SRafael J. Wysocki #include <linux/rbtree.h>
18*b86ff982SRafael J. Wysocki #include <linux/slab.h>
19*b86ff982SRafael J. Wysocki 
20*b86ff982SRafael J. Wysocki #define WL_NUMBER_LIMIT	100
21*b86ff982SRafael J. Wysocki #define WL_GC_COUNT_MAX	100
22*b86ff982SRafael J. Wysocki #define WL_GC_TIME_SEC	300
23*b86ff982SRafael J. Wysocki 
24*b86ff982SRafael J. Wysocki static DEFINE_MUTEX(wakelocks_lock);
25*b86ff982SRafael J. Wysocki 
26*b86ff982SRafael J. Wysocki struct wakelock {
27*b86ff982SRafael J. Wysocki 	char			*name;
28*b86ff982SRafael J. Wysocki 	struct rb_node		node;
29*b86ff982SRafael J. Wysocki 	struct wakeup_source	ws;
30*b86ff982SRafael J. Wysocki 	struct list_head	lru;
31*b86ff982SRafael J. Wysocki };
32*b86ff982SRafael J. Wysocki 
33*b86ff982SRafael J. Wysocki static struct rb_root wakelocks_tree = RB_ROOT;
34*b86ff982SRafael J. Wysocki static LIST_HEAD(wakelocks_lru_list);
35*b86ff982SRafael J. Wysocki static unsigned int number_of_wakelocks;
36*b86ff982SRafael J. Wysocki static unsigned int wakelocks_gc_count;
37*b86ff982SRafael J. Wysocki 
38*b86ff982SRafael J. Wysocki ssize_t pm_show_wakelocks(char *buf, bool show_active)
39*b86ff982SRafael J. Wysocki {
40*b86ff982SRafael J. Wysocki 	struct rb_node *node;
41*b86ff982SRafael J. Wysocki 	struct wakelock *wl;
42*b86ff982SRafael J. Wysocki 	char *str = buf;
43*b86ff982SRafael J. Wysocki 	char *end = buf + PAGE_SIZE;
44*b86ff982SRafael J. Wysocki 
45*b86ff982SRafael J. Wysocki 	mutex_lock(&wakelocks_lock);
46*b86ff982SRafael J. Wysocki 
47*b86ff982SRafael J. Wysocki 	for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {
48*b86ff982SRafael J. Wysocki 		wl = rb_entry(node, struct wakelock, node);
49*b86ff982SRafael J. Wysocki 		if (wl->ws.active == show_active)
50*b86ff982SRafael J. Wysocki 			str += scnprintf(str, end - str, "%s ", wl->name);
51*b86ff982SRafael J. Wysocki 	}
52*b86ff982SRafael J. Wysocki 	if (str > buf)
53*b86ff982SRafael J. Wysocki 		str--;
54*b86ff982SRafael J. Wysocki 
55*b86ff982SRafael J. Wysocki 	str += scnprintf(str, end - str, "\n");
56*b86ff982SRafael J. Wysocki 
57*b86ff982SRafael J. Wysocki 	mutex_unlock(&wakelocks_lock);
58*b86ff982SRafael J. Wysocki 	return (str - buf);
59*b86ff982SRafael J. Wysocki }
60*b86ff982SRafael J. Wysocki 
61*b86ff982SRafael J. Wysocki static struct wakelock *wakelock_lookup_add(const char *name, size_t len,
62*b86ff982SRafael J. Wysocki 					    bool add_if_not_found)
63*b86ff982SRafael J. Wysocki {
64*b86ff982SRafael J. Wysocki 	struct rb_node **node = &wakelocks_tree.rb_node;
65*b86ff982SRafael J. Wysocki 	struct rb_node *parent = *node;
66*b86ff982SRafael J. Wysocki 	struct wakelock *wl;
67*b86ff982SRafael J. Wysocki 
68*b86ff982SRafael J. Wysocki 	while (*node) {
69*b86ff982SRafael J. Wysocki 		int diff;
70*b86ff982SRafael J. Wysocki 
71*b86ff982SRafael J. Wysocki 		parent = *node;
72*b86ff982SRafael J. Wysocki 		wl = rb_entry(*node, struct wakelock, node);
73*b86ff982SRafael J. Wysocki 		diff = strncmp(name, wl->name, len);
74*b86ff982SRafael J. Wysocki 		if (diff == 0) {
75*b86ff982SRafael J. Wysocki 			if (wl->name[len])
76*b86ff982SRafael J. Wysocki 				diff = -1;
77*b86ff982SRafael J. Wysocki 			else
78*b86ff982SRafael J. Wysocki 				return wl;
79*b86ff982SRafael J. Wysocki 		}
80*b86ff982SRafael J. Wysocki 		if (diff < 0)
81*b86ff982SRafael J. Wysocki 			node = &(*node)->rb_left;
82*b86ff982SRafael J. Wysocki 		else
83*b86ff982SRafael J. Wysocki 			node = &(*node)->rb_right;
84*b86ff982SRafael J. Wysocki 	}
85*b86ff982SRafael J. Wysocki 	if (!add_if_not_found)
86*b86ff982SRafael J. Wysocki 		return ERR_PTR(-EINVAL);
87*b86ff982SRafael J. Wysocki 
88*b86ff982SRafael J. Wysocki 	if (number_of_wakelocks > WL_NUMBER_LIMIT)
89*b86ff982SRafael J. Wysocki 		return ERR_PTR(-ENOSPC);
90*b86ff982SRafael J. Wysocki 
91*b86ff982SRafael J. Wysocki 	/* Not found, we have to add a new one. */
92*b86ff982SRafael J. Wysocki 	wl = kzalloc(sizeof(*wl), GFP_KERNEL);
93*b86ff982SRafael J. Wysocki 	if (!wl)
94*b86ff982SRafael J. Wysocki 		return ERR_PTR(-ENOMEM);
95*b86ff982SRafael J. Wysocki 
96*b86ff982SRafael J. Wysocki 	wl->name = kstrndup(name, len, GFP_KERNEL);
97*b86ff982SRafael J. Wysocki 	if (!wl->name) {
98*b86ff982SRafael J. Wysocki 		kfree(wl);
99*b86ff982SRafael J. Wysocki 		return ERR_PTR(-ENOMEM);
100*b86ff982SRafael J. Wysocki 	}
101*b86ff982SRafael J. Wysocki 	wl->ws.name = wl->name;
102*b86ff982SRafael J. Wysocki 	wakeup_source_add(&wl->ws);
103*b86ff982SRafael J. Wysocki 	rb_link_node(&wl->node, parent, node);
104*b86ff982SRafael J. Wysocki 	rb_insert_color(&wl->node, &wakelocks_tree);
105*b86ff982SRafael J. Wysocki 	list_add(&wl->lru, &wakelocks_lru_list);
106*b86ff982SRafael J. Wysocki 	number_of_wakelocks++;
107*b86ff982SRafael J. Wysocki 	return wl;
108*b86ff982SRafael J. Wysocki }
109*b86ff982SRafael J. Wysocki 
110*b86ff982SRafael J. Wysocki int pm_wake_lock(const char *buf)
111*b86ff982SRafael J. Wysocki {
112*b86ff982SRafael J. Wysocki 	const char *str = buf;
113*b86ff982SRafael J. Wysocki 	struct wakelock *wl;
114*b86ff982SRafael J. Wysocki 	u64 timeout_ns = 0;
115*b86ff982SRafael J. Wysocki 	size_t len;
116*b86ff982SRafael J. Wysocki 	int ret = 0;
117*b86ff982SRafael J. Wysocki 
118*b86ff982SRafael J. Wysocki 	while (*str && !isspace(*str))
119*b86ff982SRafael J. Wysocki 		str++;
120*b86ff982SRafael J. Wysocki 
121*b86ff982SRafael J. Wysocki 	len = str - buf;
122*b86ff982SRafael J. Wysocki 	if (!len)
123*b86ff982SRafael J. Wysocki 		return -EINVAL;
124*b86ff982SRafael J. Wysocki 
125*b86ff982SRafael J. Wysocki 	if (*str && *str != '\n') {
126*b86ff982SRafael J. Wysocki 		/* Find out if there's a valid timeout string appended. */
127*b86ff982SRafael J. Wysocki 		ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
128*b86ff982SRafael J. Wysocki 		if (ret)
129*b86ff982SRafael J. Wysocki 			return -EINVAL;
130*b86ff982SRafael J. Wysocki 	}
131*b86ff982SRafael J. Wysocki 
132*b86ff982SRafael J. Wysocki 	mutex_lock(&wakelocks_lock);
133*b86ff982SRafael J. Wysocki 
134*b86ff982SRafael J. Wysocki 	wl = wakelock_lookup_add(buf, len, true);
135*b86ff982SRafael J. Wysocki 	if (IS_ERR(wl)) {
136*b86ff982SRafael J. Wysocki 		ret = PTR_ERR(wl);
137*b86ff982SRafael J. Wysocki 		goto out;
138*b86ff982SRafael J. Wysocki 	}
139*b86ff982SRafael J. Wysocki 	if (timeout_ns) {
140*b86ff982SRafael J. Wysocki 		u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
141*b86ff982SRafael J. Wysocki 
142*b86ff982SRafael J. Wysocki 		do_div(timeout_ms, NSEC_PER_MSEC);
143*b86ff982SRafael J. Wysocki 		__pm_wakeup_event(&wl->ws, timeout_ms);
144*b86ff982SRafael J. Wysocki 	} else {
145*b86ff982SRafael J. Wysocki 		__pm_stay_awake(&wl->ws);
146*b86ff982SRafael J. Wysocki 	}
147*b86ff982SRafael J. Wysocki 
148*b86ff982SRafael J. Wysocki 	list_move(&wl->lru, &wakelocks_lru_list);
149*b86ff982SRafael J. Wysocki 
150*b86ff982SRafael J. Wysocki  out:
151*b86ff982SRafael J. Wysocki 	mutex_unlock(&wakelocks_lock);
152*b86ff982SRafael J. Wysocki 	return ret;
153*b86ff982SRafael J. Wysocki }
154*b86ff982SRafael J. Wysocki 
155*b86ff982SRafael J. Wysocki static void wakelocks_gc(void)
156*b86ff982SRafael J. Wysocki {
157*b86ff982SRafael J. Wysocki 	struct wakelock *wl, *aux;
158*b86ff982SRafael J. Wysocki 	ktime_t now = ktime_get();
159*b86ff982SRafael J. Wysocki 
160*b86ff982SRafael J. Wysocki 	list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) {
161*b86ff982SRafael J. Wysocki 		u64 idle_time_ns;
162*b86ff982SRafael J. Wysocki 		bool active;
163*b86ff982SRafael J. Wysocki 
164*b86ff982SRafael J. Wysocki 		spin_lock_irq(&wl->ws.lock);
165*b86ff982SRafael J. Wysocki 		idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time));
166*b86ff982SRafael J. Wysocki 		active = wl->ws.active;
167*b86ff982SRafael J. Wysocki 		spin_unlock_irq(&wl->ws.lock);
168*b86ff982SRafael J. Wysocki 
169*b86ff982SRafael J. Wysocki 		if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC))
170*b86ff982SRafael J. Wysocki 			break;
171*b86ff982SRafael J. Wysocki 
172*b86ff982SRafael J. Wysocki 		if (!active) {
173*b86ff982SRafael J. Wysocki 			wakeup_source_remove(&wl->ws);
174*b86ff982SRafael J. Wysocki 			rb_erase(&wl->node, &wakelocks_tree);
175*b86ff982SRafael J. Wysocki 			list_del(&wl->lru);
176*b86ff982SRafael J. Wysocki 			kfree(wl->name);
177*b86ff982SRafael J. Wysocki 			kfree(wl);
178*b86ff982SRafael J. Wysocki 			number_of_wakelocks--;
179*b86ff982SRafael J. Wysocki 		}
180*b86ff982SRafael J. Wysocki 	}
181*b86ff982SRafael J. Wysocki 	wakelocks_gc_count = 0;
182*b86ff982SRafael J. Wysocki }
183*b86ff982SRafael J. Wysocki 
184*b86ff982SRafael J. Wysocki int pm_wake_unlock(const char *buf)
185*b86ff982SRafael J. Wysocki {
186*b86ff982SRafael J. Wysocki 	struct wakelock *wl;
187*b86ff982SRafael J. Wysocki 	size_t len;
188*b86ff982SRafael J. Wysocki 	int ret = 0;
189*b86ff982SRafael J. Wysocki 
190*b86ff982SRafael J. Wysocki 	len = strlen(buf);
191*b86ff982SRafael J. Wysocki 	if (!len)
192*b86ff982SRafael J. Wysocki 		return -EINVAL;
193*b86ff982SRafael J. Wysocki 
194*b86ff982SRafael J. Wysocki 	if (buf[len-1] == '\n')
195*b86ff982SRafael J. Wysocki 		len--;
196*b86ff982SRafael J. Wysocki 
197*b86ff982SRafael J. Wysocki 	if (!len)
198*b86ff982SRafael J. Wysocki 		return -EINVAL;
199*b86ff982SRafael J. Wysocki 
200*b86ff982SRafael J. Wysocki 	mutex_lock(&wakelocks_lock);
201*b86ff982SRafael J. Wysocki 
202*b86ff982SRafael J. Wysocki 	wl = wakelock_lookup_add(buf, len, false);
203*b86ff982SRafael J. Wysocki 	if (IS_ERR(wl)) {
204*b86ff982SRafael J. Wysocki 		ret = PTR_ERR(wl);
205*b86ff982SRafael J. Wysocki 		goto out;
206*b86ff982SRafael J. Wysocki 	}
207*b86ff982SRafael J. Wysocki 	__pm_relax(&wl->ws);
208*b86ff982SRafael J. Wysocki 	list_move(&wl->lru, &wakelocks_lru_list);
209*b86ff982SRafael J. Wysocki 	if (++wakelocks_gc_count > WL_GC_COUNT_MAX)
210*b86ff982SRafael J. Wysocki 		wakelocks_gc();
211*b86ff982SRafael J. Wysocki 
212*b86ff982SRafael J. Wysocki  out:
213*b86ff982SRafael J. Wysocki 	mutex_unlock(&wakelocks_lock);
214*b86ff982SRafael J. Wysocki 	return ret;
215*b86ff982SRafael J. Wysocki }
216