本文主体内容是转载的,并修改了部分的内容。
1. Linux kernel wakelock 定义
A wake_lock prevents the system from entering suspend or other low power states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock prevents a full system suspend. If the type is WAKE_LOCK_IDLE, low powerstates that cause large interrupt latencies or that disable a set of interrupts will not entered from idle until the wake_locks are released.
当唤醒锁被激活的时候,它能阻止系统进入挂起状态或者其他低功耗模式。
如果锁类型被设置为 WAKE_LOCK_SUSPEND,该唤醒锁阻止整个系统进入挂起状态。
如果锁类型被设置为 WAKE_LOCK_IDLE, 除非该锁被释放,否则产生大量中断潜在因素或者禁止一系列中断的系统低功耗模式将被阻止进入。但是系统可以进入挂起睡眠状态。
2. 编译标志
根据Kconfig 文件, 相关的内核编译标志分别为:
CONFIG_HAS_WAKELOCK 选择是否使能 wakelock模块。
CONFIG_WAKELOCK: 依赖CONFIG_HAS_WAKELOCK。当用户空间请求睡眠状态时,该请求将被延置直到没有唤醒锁为使能状态。
CONFIG_WAKELOCK_STAT: 报告唤醒锁状态并写入到 /proc/wakelocks 文件
CONFIG_USER_WAKELOCK 用户空间唤醒锁。写入"锁名称" 或者“超时锁名称” 到 /sys/power/wake_lock文件来使能唤醒锁 ( 根据需要与否来创建一个唤醒锁)
写入“锁名称”到 /sys/power/wake_unlock 文件来解锁一个用户空间唤醒锁。
CONFIG_EARLYSUSPEND 当用户空间请求睡眠状态改变时, 调用early suspend handlers 该标志依赖于 CONFIG_WAKELOCK编译标志。
实例:
void ath6kl_setup_android_resource(struct ath6kl *ar)
{
#ifdef CONFIG_HAS_EARLYSUSPEND //用户空间请求睡眠状态的请求需要处理时
ar->screen_off = false;
ar->early_suspend.suspend = ath6kl_early_suspend;
ar->early_suspend.resume = ath6kl_late_resume;
ar->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
register_early_suspend(&ar->early_suspend);
#endif
#ifdef CONFIG_HAS_WAKELOCK // 使能wakelock模块
wake_lock_init(&ar->wake_lock,
WAKE_LOCK_SUSPEND,
"ath6kl_lock_wl");
wake_lock_init(&ar->p2p_wake_lock,
WAKE_LOCK_SUSPEND,
"ath6kl_p2p_suspend_wl");
#endif
}
wake_lock_init(&ar->wake_lock,
WAKE_LOCK_SUSPEND,
"ath6kl_lock_wl");
//初始化一个ar结构体中的唤醒锁,配置成WAKE_LOCK_SUSPEND类型,并且命名为“ath6kl_lock_wl”,可以通过cat /proc/wakelocks 查看。
睡眠状态的产生大致过程如下:
用户空间请求睡眠
------->该请求等待到没有唤醒锁(wake_lock)处于激活(使能)状态才被处理
------->处理时调用early_suspend handlers
关于CONFIG_HAS_WAKELOCK的配置,在如下编译后产生的文件中可以看到:
/out/target/product/*****/obj/KERNEL_OBJ/include/config/auto.conf:167:CONFIG_HAS_WAKELOCK=y
./out/target/product/*****/obj/KERNEL_OBJ/include/generated/autoconf.h:169:#define CONFIG_HAS_WAKELOCK 1
./out/target/product/*****/obj/KERNEL_OBJ/.config:716:CONFIG_HAS_WAKELOCK=y
/out/target/product/****/obj/KERNEL_OBJ/.config:716:CONFIG_HAS_WAKELOCK=y
3. /proc/wakelocks 格式
示例如下
root@android:/ # cat /proc/wakelocks
cat /proc/wakelocks
name count expire_count wake_count active_since total_time sleep_time max_time last_change
"em" 4899 0 0 0 129695047374 123649908727 2367225591 29445791758668
"alarm_rtc" 2360 474 0 0 1613420206524 1611455462922 1949640000 27899269419825
"bcmpmu_i2c" 244500 0 1876 0 275160569412 268463363255 996054076 29445791728151
"KeyEvents" 11133 0 0 0 1853662043 601848424 231933595 29439508921751
4. 内核锁函数
初始化锁模块
1 static int __init wakelocks_init(void)
2 {
3 int ret;
4 int i;
5
6 // 初始化suspend, idle 两个类型锁的链表
7 for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
8 INIT_LIST_HEAD(&active_wake_locks[i]);
9
10 #ifdef CONFIG_WAKELOCK_STAT
11 wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,
12 "deleted_wake_locks");
13 #endif
//初始化main锁并上锁。何处解main锁?early_suspend()
14 wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
15 wake_lock(&main_wake_lock);
16 //初始化 unknown_wakeups 锁
17 wake_lock_init(&unknown_wakeup,WAKE_LOCK_SUSPEND,"unknown_wakeups");
18 //初始化 suspend_backoff 锁
19 //以上几个都是suspend锁。用户空间锁也是 suspend 类型。
20 wake_lock_init(&suspend_backoff_lock, WAKE_LOCK_SUSPEND,
21 "suspend_backoff");
22
23 ret = platform_device_register(&power_device);
24 if (ret) {
25 pr_err("wakelocks_init: platform_device_register failed\n");
26 goto err_platform_device_register;
27 }
28 ret = platform_driver_register(&power_driver);
29 if (ret) {
30 pr_err("wakelocks_init: platform_driver_register failed\n");
31 goto err_platform_driver_register;
32 }
33
34 suspend_work_queue = create_singlethread_workqueue("suspend");
35 if (suspend_work_queue == NULL) {
36 ret = -ENOMEM;
37 goto err_suspend_work_queue;
38 }
39
40 #ifdef CONFIG_WAKELOCK_STAT
41 //创建 /proc/wakelocks, /proc/active_wakelocks 节点输出锁状态信息
42 proc_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_fops);
43 proc_create("active_wakelocks", S_IRUGO, NULL, &active_wakelocks_fops);
44 #endif
45
46 return 0;
47
48 err_suspend_work_queue:
49 platform_driver_unregister(&power_driver);
50 err_platform_driver_register:
51 platform_device_unregister(&power_device);
52 err_platform_device_register:
53 wake_lock_destroy(&suspend_backoff_lock);
54 wake_lock_destroy(&unknown_wakeup);
55 wake_lock_destroy(&main_wake_lock);
56 #ifdef CONFIG_WAKELOCK_STAT
57 wake_lock_destroy(&deleted_wake_locks);
58 #endif
59 return ret;
60 }
初始化锁
61 void wake_lock_init(struct wake_lock *lock, int type, const char *name)
wake_lock_init() 设置锁的名字,类型,最后将新建的锁挂接到一个专门链接这些非锁状态的链表inactive_locks上。
上锁函数
62 void wake_lock(struct wake_lock *lock)
63 {
64 wake_lock_internal(lock, 0, 0);
65 }
66 EXPORT_SYMBOL(wake_lock);
67
68 void wake_lock_timeout(struct wake_lock *lock, long timeout)
69 {
70 wake_lock_internal(lock, timeout, 1);
71 }
wake_lock()和wake_lock_timeout() 分别对 inactive 的锁进行上锁。后者是超时锁。调用同一个内部函数 wake_lock_internal()
72 static void wake_lock_internal(struct wake_lock *lock, long timeout, int has_timeout)
73 {
74 spin_lock_irqsave(&list_lock, irqflags);
75 type = lock->flags & WAKE_LOCK_TYPE_MASK;
76
77 //上锁前, 检查锁类型和有效性
78 BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
79 BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));
80 #ifdef CONFIG_WAKELOCK_STAT
81 //检查 wait_for_wakeup 标志
82 if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {
83 if (debug_mask & DEBUG_WAKEUP)
84 pr_info("wakeup wake lock: %s\n", lock->name);
85 wait_for_wakeup = 0;
86 lock->stat.wakeup_count++;
87 }
88 //检查超时锁
89 if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
90 (long)(lock->expires - jiffies) <= 0) {
91 wake_unlock_stat_locked(lock, 0);
92 lock->stat.last_time = ktime_get();
93 }
94 #endif
95 //上锁
96 if (!(lock->flags & WAKE_LOCK_ACTIVE)) {
97 lock->flags |= WAKE_LOCK_ACTIVE;
98 #ifdef CONFIG_WAKELOCK_STAT
99 lock->stat.last_time = ktime_get();
100 #endif
101 }
102 //先从链表中摘除该锁
103 list_del(&lock->link);
104 if (has_timeout) {
105 if (debug_mask & DEBUG_WAKE_LOCK)
106 pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",
107 lock->name, type, timeout / HZ,
108 (timeout % HZ) * MSEC_PER_SEC / HZ);
109 lock->expires = jiffies + timeout;
110 lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
111 //超时锁重新加入链表队尾
112 list_add_tail(&lock->link, &active_wake_locks[type]);
113 } else {
114 if (debug_mask & DEBUG_WAKE_LOCK)
115 pr_info("wake_lock: %s, type %d\n", lock->name, type);
116 lock->expires = LONG_MAX;
117 lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
118 //非超时锁直接加入队列
119 list_add(&lock->link, &active_wake_locks[type]);
120 }
121 if (type == WAKE_LOCK_SUSPEND) {
122 current_event_num++;
123 #ifdef CONFIG_WAKELOCK_STAT
124 //针对主锁的检查
125 if (lock == &main_wake_lock)
126 update_sleep_wait_stats_locked(1);
127 else if (!wake_lock_active(&main_wake_lock))
128 update_sleep_wait_stats_locked(0);
129 #endif
130 if (has_timeout)
131 //检查并遍历更新队列中所有超时锁。
132 expire_in = has_wake_lock_locked(type);
133 else
134 //非超时锁
135 expire_in = -1;
136 if (expire_in > 0) {
137 if (debug_mask & DEBUG_EXPIRE)
138 pr_info("wake_lock: %s, start expire timer, "
139 "%ld\n", lock->name, expire_in);
140 mod_timer(&expire_timer, jiffies + expire_in);
141 } else {
142 //无任何超时锁
143 if (del_timer(&expire_timer))
144 if (debug_mask & DEBUG_EXPIRE)
145 pr_info("wake_lock: %s, stop expire timer\n",
146 lock->name);
147 //如果超时锁为0, 且无非超时锁时, expire_in为0, 启动suspend流程
148 if (expire_in == 0)
149 queue_work(suspend_work_queue, &suspend_work);
150 }
151 }
152 spin_unlock_irqrestore(&list_lock, irqflags);
153 }
解锁函数
154 void wake_unlock(struct wake_lock *lock)
155 {
156 int type;
157 unsigned long irqflags;
158 spin_lock_irqsave(&list_lock, irqflags);
159 type = lock->flags & WAKE_LOCK_TYPE_MASK;
160 #ifdef CONFIG_WAKELOCK_STAT
161 wake_unlock_stat_locked(lock, 0);
162 #endif
163 if (debug_mask & DEBUG_WAKE_LOCK)
164 pr_info("wake_unlock: %s\n", lock->name);
165 //去处锁标志
166 lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
167 //该锁从 active列表移除, 加入inactive列表
168 list_del(&lock->link);
169 list_add(&lock->link, &inactive_locks);
170 if (type == WAKE_LOCK_SUSPEND) {
171 //检查是否需要启动suspend流程
172 long has_lock = has_wake_lock_locked(type);
173 if (has_lock > 0) {
174 if (debug_mask & DEBUG_EXPIRE)
175 pr_info("wake_unlock: %s, start expire timer, "
176 "%ld\n", lock->name, has_lock);
177 mod_timer(&expire_timer, jiffies + has_lock);
178 } else {
179 if (del_timer(&expire_timer))
180 if (debug_mask & DEBUG_EXPIRE)
181 pr_info("wake_unlock: %s, stop expire "
182 "timer\n", lock->name);
183 if (has_lock == 0)
184 queue_work(suspend_work_queue, &suspend_work);
185 }
186 if (lock == &main_wake_lock) {
187 if (debug_mask & DEBUG_SUSPEND)
188 print_active_locks(WAKE_LOCK_SUSPEND);
189 #ifdef CONFIG_WAKELOCK_STAT
190 update_sleep_wait_stats_locked(0);
191 #endif
192 }
193 }
194 spin_unlock_irqrestore(&list_lock, irqflags);
195 }
expire_wake_locks() 检查是否所有锁已经超时。 如果has_wake_lock_locked() 返回0,
针对suspend锁,必然是超时锁为0且非超时锁为0, 则启动suspend流程。
expire_wake_locks()是 expire_timer 的超时回调函数。
196 static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
5. suspend函数
197 static void suspend(struct work_struct *work)
198 {
199 int ret;
200 int entry_event_num;
201 struct timespec ts_entry, ts_exit;
202
203 //如果还有suspend锁,直接abort
204 if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
205 if (debug_mask & DEBUG_SUSPEND)
206 pr_info("suspend: abort suspend\n");
207 return;
208 }
209
210 entry_event_num = current_event_num;
211 //内存同步
212 sys_sync();
213 if (debug_mask & DEBUG_SUSPEND)
214 pr_info("suspend: enter suspend\n");
215 getnstimeofday(&ts_entry);
216 //系统suspend
217 ret = pm_suspend(requested_suspend_state);
218 getnstimeofday(&ts_exit);
219
220 if (debug_mask & DEBUG_EXIT_SUSPEND) {
221 struct rtc_time tm;
222 rtc_time_to_tm(ts_exit.tv_sec, &tm);
223 pr_info("suspend: exit suspend, ret = %d "
224 "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
225 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
226 tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec);
227 }
228
229 if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) {
230 ++suspend_short_count;
231
232 if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) {
233 suspend_backoff();
234 suspend_short_count = 0;
235 }
236 } else {
237 suspend_short_count = 0;
238 }
239
240 if (current_event_num == entry_event_num) {
241 if (debug_mask & DEBUG_SUSPEND)
242 pr_info("suspend: pm_suspend returned with no event\n");
243 wake_lock_timeout(&unknown_wakeup, HZ / 2);
244 }
245 }
246 static DECLARE_WORK(suspend_work, suspend);