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 power
states 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编译标志。
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. 内核锁函数
初始化锁模块
static int __init wakelocks_init(void)
{
int ret;
int i;
// 初始化suspend, idle 两个类型锁的链表
for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
INIT_LIST_HEAD(&active_wake_locks[i]);
#ifdef CONFIG_WAKELOCK_STAT
wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,
"deleted_wake_locks");
#endif
//初始化 main 锁并上锁。 何处解main锁?early_suspend()
wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
wake_lock(&main_wake_lock);
//初始化 unknown_wakeups 锁
wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups");
//初始化 suspend_backoff 锁
//以上几个都是suspend 锁。 用户空间锁也是 suspend 类型。
wake_lock_init(&suspend_backoff_lock, WAKE_LOCK_SUSPEND,
"suspend_backoff");
ret = platform_device_register(&power_device);
if (ret) {
pr_err("wakelocks_init: platform_device_register failed\n");
goto err_platform_device_register;
}
ret = platform_driver_register(&power_driver);
if (ret) {
pr_err("wakelocks_init: platform_driver_register failed\n");
goto err_platform_driver_register;
}
suspend_work_queue = create_singlethread_workqueue("suspend");
if (suspend_work_queue == NULL) {
ret = -ENOMEM;
goto err_suspend_work_queue;
}
#ifdef CONFIG_WAKELOCK_STAT
//创建 /proc/wakelocks, /proc/active_wakelocks 节点输出锁状态信息
proc_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_fops);
proc_create("active_wakelocks", S_IRUGO, NULL, &active_wakelocks_fops);
#endif
return 0;
err_suspend_work_queue:
platform_driver_unregister(&power_driver);
err_platform_driver_register:
platform_device_unregister(&power_device);
err_platform_device_register:
wake_lock_destroy(&suspend_backoff_lock);
wake_lock_destroy(&unknown_wakeup);
wake_lock_destroy(&main_wake_lock);
#ifdef CONFIG_WAKELOCK_STAT
wake_lock_destroy(&deleted_wake_locks);
#endif
return ret;
}
初始化锁
void wake_lock_init(struct wake_lock *lock, int type, const char *name)
wake_lock_init() 设置锁的名字,类型,最后将新建的锁挂接到一个专门链接这些非锁状态的链表inactive_locks上。
上锁函数
void wake_lock(struct wake_lock *lock)
{
wake_lock_internal(lock, 0, 0);
}
EXPORT_SYMBOL(wake_lock);
void wake_lock_timeout(struct wake_lock *lock, long timeout)
{
wake_lock_internal(lock, timeout, 1);
}
wake_lock()和wake_lock_timeout() 分别对 inactive 的锁进行上锁。后者是超时锁。调用同一个内部函数 wake_lock_internal()
static void wake_lock_internal(struct wake_lock *lock, long timeout, int has_timeout)
{
spin_lock_irqsave(&list_lock, irqflags);
type = lock->flags & WAKE_LOCK_TYPE_MASK;
//上锁前, 检查锁类型和有效性
BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));
#ifdef CONFIG_WAKELOCK_STAT
//检查 wait_for_wakeup 标志
if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {
if (debug_mask & DEBUG_WAKEUP)
pr_info("wakeup wake lock: %s\n", lock->name);
wait_for_wakeup = 0;
lock->stat.wakeup_count++;
}
//检查超时锁
if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
(long)(lock->expires - jiffies) <= 0) {
wake_unlock_stat_locked(lock, 0);
lock->stat.last_time = ktime_get();
}
#endif
//上锁
if (!(lock->flags & WAKE_LOCK_ACTIVE)) {
lock->flags |= WAKE_LOCK_ACTIVE;
#ifdef CONFIG_WAKELOCK_STAT
lock->stat.last_time = ktime_get();
#endif
}
//先从链表中摘除该锁
list_del(&lock->link);
if (has_timeout) {
if (debug_mask & DEBUG_WAKE_LOCK)
pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",
lock->name, type, timeout / HZ,
(timeout % HZ) * MSEC_PER_SEC / HZ);
lock->expires = jiffies + timeout;
lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
//超时锁重新加入链表队尾
list_add_tail(&lock->link, &active_wake_locks[type]);
} else {
if (debug_mask & DEBUG_WAKE_LOCK)
pr_info("wake_lock: %s, type %d\n", lock->name, type);
lock->expires = LONG_MAX;
lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
//非超时锁直接加入队列
list_add(&lock->link, &active_wake_locks[type]);
}
if (type == WAKE_LOCK_SUSPEND) {
current_event_num++;
#ifdef CONFIG_WAKELOCK_STAT
//针对主锁的检查
if (lock == &main_wake_lock)
update_sleep_wait_stats_locked(1);
else if (!wake_lock_active(&main_wake_lock))
update_sleep_wait_stats_locked(0);
#endif
if (has_timeout)
//检查并遍历更新队列中所有超时锁。
expire_in = has_wake_lock_locked(type);
else
//非超时锁
expire_in = -1;
if (expire_in > 0) {
if (debug_mask & DEBUG_EXPIRE)
pr_info("wake_lock: %s, start expire timer, "
"%ld\n", lock->name, expire_in);
mod_timer(&expire_timer, jiffies + expire_in);
} else {
//无任何超时锁
if (del_timer(&expire_timer))
if (debug_mask & DEBUG_EXPIRE)
pr_info("wake_lock: %s, stop expire timer\n",
lock->name);
//如果超时锁为0, 且无非超时锁时, expire_in为0, 启动suspend流程
if (expire_in == 0)
queue_work(suspend_work_queue, &suspend_work);
}
}
spin_unlock_irqrestore(&list_lock, irqflags);
}
解锁函数
void wake_unlock(struct wake_lock *lock)
{
int type;
unsigned long irqflags;
spin_lock_irqsave(&list_lock, irqflags);
type = lock->flags & WAKE_LOCK_TYPE_MASK;
#ifdef CONFIG_WAKELOCK_STAT
wake_unlock_stat_locked(lock, 0);
#endif
if (debug_mask & DEBUG_WAKE_LOCK)
pr_info("wake_unlock: %s\n", lock->name);
//去处锁标志
lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
//该锁从 active列表移除, 加入inactive列表
list_del(&lock->link);
list_add(&lock->link, &inactive_locks);
if (type == WAKE_LOCK_SUSPEND) {
//检查是否需要启动suspend流程
long has_lock = has_wake_lock_locked(type);
if (has_lock > 0) {
if (debug_mask & DEBUG_EXPIRE)
pr_info("wake_unlock: %s, start expire timer, "
"%ld\n", lock->name, has_lock);
mod_timer(&expire_timer, jiffies + has_lock);
} else {
if (del_timer(&expire_timer))
if (debug_mask & DEBUG_EXPIRE)
pr_info("wake_unlock: %s, stop expire "
"timer\n", lock->name);
if (has_lock == 0)
queue_work(suspend_work_queue, &suspend_work);
}
if (lock == &main_wake_lock) {
if (debug_mask & DEBUG_SUSPEND)
print_active_locks(WAKE_LOCK_SUSPEND);
#ifdef CONFIG_WAKELOCK_STAT
update_sleep_wait_stats_locked(0);
#endif
}
}
spin_unlock_irqrestore(&list_lock, irqflags);
}
expire_wake_locks() 检查是否所有锁已经超时。 如果has_wake_lock_locked() 返回0,
针对suspend锁,必然是超时锁为0且非超时锁为0, 则启动suspend流程。
expire_wake_locks()是 expire_timer 的超时回调函数。
static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
5. suspend函数
static void suspend(struct work_struct *work)
{
int ret;
int entry_event_num;
struct timespec ts_entry, ts_exit;
//如果还有suspend锁,直接abort
if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
if (debug_mask & DEBUG_SUSPEND)
pr_info("suspend: abort suspend\n");
return;
}
entry_event_num = current_event_num;
//内存同步
sys_sync();
if (debug_mask & DEBUG_SUSPEND)
pr_info("suspend: enter suspend\n");
getnstimeofday(&ts_entry);
//系统suspend
ret = pm_suspend(requested_suspend_state);
getnstimeofday(&ts_exit);
if (debug_mask & DEBUG_EXIT_SUSPEND) {
struct rtc_time tm;
rtc_time_to_tm(ts_exit.tv_sec, &tm);
pr_info("suspend: exit suspend, ret = %d "
"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec);
}
if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) {
++suspend_short_count;
if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) {
suspend_backoff();
suspend_short_count = 0;
}
} else {
suspend_short_count = 0;
}
if (current_event_num == entry_event_num) {
if (debug_mask & DEBUG_SUSPEND)
pr_info("suspend: pm_suspend returned with no event\n");
wake_lock_timeout(&unknown_wakeup, HZ / 2);
}
}
static DECLARE_WORK(suspend_work, suspend);