wakelock.c (c73893e2ca731b4a81ae59246ab57979aa188777) | wakelock.c (4e585d25e120f1eae0a3a8bf8f6ebc7692afec18) |
---|---|
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 | 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_GC_COUNT_MAX 100 21#define WL_GC_TIME_SEC 300 22 | |
23static DEFINE_MUTEX(wakelocks_lock); 24 25struct wakelock { 26 char *name; 27 struct rb_node node; 28 struct wakeup_source ws; | 20static DEFINE_MUTEX(wakelocks_lock); 21 22struct wakelock { 23 char *name; 24 struct rb_node node; 25 struct wakeup_source ws; |
26#ifdef CONFIG_PM_WAKELOCKS_GC |
|
29 struct list_head lru; | 27 struct list_head lru; |
28#endif |
|
30}; 31 32static struct rb_root wakelocks_tree = RB_ROOT; | 29}; 30 31static struct rb_root wakelocks_tree = RB_ROOT; |
33static LIST_HEAD(wakelocks_lru_list); 34static unsigned int wakelocks_gc_count; | |
35 36ssize_t pm_show_wakelocks(char *buf, bool show_active) 37{ 38 struct rb_node *node; 39 struct wakelock *wl; 40 char *str = buf; 41 char *end = buf + PAGE_SIZE; 42 --- 31 unchanged lines hidden (view full) --- 74 number_of_wakelocks--; 75} 76#else /* CONFIG_PM_WAKELOCKS_LIMIT = 0 */ 77static inline bool wakelocks_limit_exceeded(void) { return false; } 78static inline void increment_wakelocks_number(void) {} 79static inline void decrement_wakelocks_number(void) {} 80#endif /* CONFIG_PM_WAKELOCKS_LIMIT */ 81 | 32 33ssize_t pm_show_wakelocks(char *buf, bool show_active) 34{ 35 struct rb_node *node; 36 struct wakelock *wl; 37 char *str = buf; 38 char *end = buf + PAGE_SIZE; 39 --- 31 unchanged lines hidden (view full) --- 71 number_of_wakelocks--; 72} 73#else /* CONFIG_PM_WAKELOCKS_LIMIT = 0 */ 74static inline bool wakelocks_limit_exceeded(void) { return false; } 75static inline void increment_wakelocks_number(void) {} 76static inline void decrement_wakelocks_number(void) {} 77#endif /* CONFIG_PM_WAKELOCKS_LIMIT */ 78 |
79#ifdef CONFIG_PM_WAKELOCKS_GC 80#define WL_GC_COUNT_MAX 100 81#define WL_GC_TIME_SEC 300 82 83static LIST_HEAD(wakelocks_lru_list); 84static unsigned int wakelocks_gc_count; 85 86static inline void wakelocks_lru_add(struct wakelock *wl) 87{ 88 list_add(&wl->lru, &wakelocks_lru_list); 89} 90 91static inline void wakelocks_lru_most_recent(struct wakelock *wl) 92{ 93 list_move(&wl->lru, &wakelocks_lru_list); 94} 95 96static void wakelocks_gc(void) 97{ 98 struct wakelock *wl, *aux; 99 ktime_t now; 100 101 if (++wakelocks_gc_count <= WL_GC_COUNT_MAX) 102 return; 103 104 now = ktime_get(); 105 list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) { 106 u64 idle_time_ns; 107 bool active; 108 109 spin_lock_irq(&wl->ws.lock); 110 idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time)); 111 active = wl->ws.active; 112 spin_unlock_irq(&wl->ws.lock); 113 114 if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC)) 115 break; 116 117 if (!active) { 118 wakeup_source_remove(&wl->ws); 119 rb_erase(&wl->node, &wakelocks_tree); 120 list_del(&wl->lru); 121 kfree(wl->name); 122 kfree(wl); 123 decrement_wakelocks_number(); 124 } 125 } 126 wakelocks_gc_count = 0; 127} 128#else /* !CONFIG_PM_WAKELOCKS_GC */ 129static inline void wakelocks_lru_add(struct wakelock *wl) {} 130static inline void wakelocks_lru_most_recent(struct wakelock *wl) {} 131static inline void wakelocks_gc(void) {} 132#endif /* !CONFIG_PM_WAKELOCKS_GC */ 133 |
|
82static struct wakelock *wakelock_lookup_add(const char *name, size_t len, 83 bool add_if_not_found) 84{ 85 struct rb_node **node = &wakelocks_tree.rb_node; 86 struct rb_node *parent = *node; 87 struct wakelock *wl; 88 89 while (*node) { --- 28 unchanged lines hidden (view full) --- 118 if (!wl->name) { 119 kfree(wl); 120 return ERR_PTR(-ENOMEM); 121 } 122 wl->ws.name = wl->name; 123 wakeup_source_add(&wl->ws); 124 rb_link_node(&wl->node, parent, node); 125 rb_insert_color(&wl->node, &wakelocks_tree); | 134static struct wakelock *wakelock_lookup_add(const char *name, size_t len, 135 bool add_if_not_found) 136{ 137 struct rb_node **node = &wakelocks_tree.rb_node; 138 struct rb_node *parent = *node; 139 struct wakelock *wl; 140 141 while (*node) { --- 28 unchanged lines hidden (view full) --- 170 if (!wl->name) { 171 kfree(wl); 172 return ERR_PTR(-ENOMEM); 173 } 174 wl->ws.name = wl->name; 175 wakeup_source_add(&wl->ws); 176 rb_link_node(&wl->node, parent, node); 177 rb_insert_color(&wl->node, &wakelocks_tree); |
126 list_add(&wl->lru, &wakelocks_lru_list); | 178 wakelocks_lru_add(wl); |
127 increment_wakelocks_number(); 128 return wl; 129} 130 131int pm_wake_lock(const char *buf) 132{ 133 const char *str = buf; 134 struct wakelock *wl; --- 26 unchanged lines hidden (view full) --- 161 u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1; 162 163 do_div(timeout_ms, NSEC_PER_MSEC); 164 __pm_wakeup_event(&wl->ws, timeout_ms); 165 } else { 166 __pm_stay_awake(&wl->ws); 167 } 168 | 179 increment_wakelocks_number(); 180 return wl; 181} 182 183int pm_wake_lock(const char *buf) 184{ 185 const char *str = buf; 186 struct wakelock *wl; --- 26 unchanged lines hidden (view full) --- 213 u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1; 214 215 do_div(timeout_ms, NSEC_PER_MSEC); 216 __pm_wakeup_event(&wl->ws, timeout_ms); 217 } else { 218 __pm_stay_awake(&wl->ws); 219 } 220 |
169 list_move(&wl->lru, &wakelocks_lru_list); | 221 wakelocks_lru_most_recent(wl); |
170 171 out: 172 mutex_unlock(&wakelocks_lock); 173 return ret; 174} 175 | 222 223 out: 224 mutex_unlock(&wakelocks_lock); 225 return ret; 226} 227 |
176static void wakelocks_gc(void) 177{ 178 struct wakelock *wl, *aux; 179 ktime_t now = ktime_get(); 180 181 list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) { 182 u64 idle_time_ns; 183 bool active; 184 185 spin_lock_irq(&wl->ws.lock); 186 idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time)); 187 active = wl->ws.active; 188 spin_unlock_irq(&wl->ws.lock); 189 190 if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC)) 191 break; 192 193 if (!active) { 194 wakeup_source_remove(&wl->ws); 195 rb_erase(&wl->node, &wakelocks_tree); 196 list_del(&wl->lru); 197 kfree(wl->name); 198 kfree(wl); 199 decrement_wakelocks_number(); 200 } 201 } 202 wakelocks_gc_count = 0; 203} 204 | |
205int pm_wake_unlock(const char *buf) 206{ 207 struct wakelock *wl; 208 size_t len; 209 int ret = 0; 210 211 len = strlen(buf); 212 if (!len) --- 8 unchanged lines hidden (view full) --- 221 mutex_lock(&wakelocks_lock); 222 223 wl = wakelock_lookup_add(buf, len, false); 224 if (IS_ERR(wl)) { 225 ret = PTR_ERR(wl); 226 goto out; 227 } 228 __pm_relax(&wl->ws); | 228int pm_wake_unlock(const char *buf) 229{ 230 struct wakelock *wl; 231 size_t len; 232 int ret = 0; 233 234 len = strlen(buf); 235 if (!len) --- 8 unchanged lines hidden (view full) --- 244 mutex_lock(&wakelocks_lock); 245 246 wl = wakelock_lookup_add(buf, len, false); 247 if (IS_ERR(wl)) { 248 ret = PTR_ERR(wl); 249 goto out; 250 } 251 __pm_relax(&wl->ws); |
229 list_move(&wl->lru, &wakelocks_lru_list); 230 if (++wakelocks_gc_count > WL_GC_COUNT_MAX) 231 wakelocks_gc(); | |
232 | 252 |
253 wakelocks_lru_most_recent(wl); 254 wakelocks_gc(); 255 |
|
233 out: 234 mutex_unlock(&wakelocks_lock); 235 return ret; 236} | 256 out: 257 mutex_unlock(&wakelocks_lock); 258 return ret; 259} |