###1. autosleep
autosleep 基于wakeup source实现, 用于取代android wakelock中的自动休眠功能
autosleep实现代码位于“kernel/power/autosleep.c”
编译内核时, 必须配置CONFIG_PM_AUTOSLEEP选项才能enable autosleep
###2. /sys/power/autosleep
autosleep在sysfs中提供了一个属性文件“/sys/power/autosleep”用以配置autosleep, 类似于“/sys/power/state”文件可以控制suspend到不同的状态, autosleep也可配置不同的状态:
- freeze : auto freeze
- standby : auto standby
- mem : auto mem
- disk : auto disk
- off : disable autosleep
向“/sys/power/autosleep”写入以上5个值的任意一个可将autosleep切换到指定的状态,一旦写入非“off”值, autosleep就开始运行
读取“/sys/power/autosleep”可以获得当前的状态, 特别的, 如果系统不支持autosleep, 读取该文件会输出”error”
“/sys/power/autosleep”的show()/store()方法由pm_autosleep_state()/pm_autosleep_set_state()来实现,在下面会详细解释
###3. pm_autosleep_init
autosleep的初始化函数pm_autosleep_init()在PM的初始化时(kernel/power/main.c:pm_init)被调用:
int __init pm_autosleep_init(void)
{
autosleep_ws = wakeup_source_register("autosleep");
if (!autosleep_ws)
return -ENOMEM;
autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
if (autosleep_wq)
return 0;
wakeup_source_unregister(autosleep_ws);
return -ENOMEM;
}
- 注册一个名为“autosleep”的wakeup source, 用于在autosleep执行关键代码执行时阻止系统休眠
- 分配一个名为“autosleep”的有序workqueue用于调度工作, 触发休眠
autosleep中定义了一个work:
static DECLARE_WORK(suspend_work, try_to_suspend);
void queue_up_suspend_work(void)
{
if (autosleep_state > PM_SUSPEND_ON)
queue_work(autosleep_wq, &suspend_work);
}
可以看到, queue_up_suspend_work()中会调度work, 运行try_to_suspend()来触发休眠, 那么什么时候会调用queue_up_suspend_work()呢? 是在“/sys/power/autosleep”的store()方法pm_autosleep_state()中调用的
###4. pm_autosleep_state()
pm_autosleep_state()的代码比较简单
int pm_autosleep_set_state(suspend_state_t state)
{
#ifndef CONFIG_HIBERNATION
if (state >= PM_SUSPEND_MAX)
return -EINVAL;
#endif
__pm_stay_awake(autosleep_ws);
mutex_lock(&autosleep_lock);
autosleep_state = state;
__pm_relax(autosleep_ws);
if (state > PM_SUSPEND_ON) {
pm_wakep_autosleep_enabled(true);
queue_up_suspend_work();
} else {
pm_wakep_autosleep_enabled(false);
}
mutex_unlock(&autosleep_lock);
return 0;
}
- 首先使用_pm_stay_awake()/__pm_relax()操纵名为“autosleep”的wakelock, 在其中修改autosleep_state为向”/sys/power/autosleep”写入的值, (读取”/sys/power/autosleep”会返回autosleep_state)
- 若是写入“/sys/power/autosleep”的值不为“off”,则调用pm_wakep_autosleep_enabled(true), 然后调用queue_up_suspend_work()触发工作队列“autosleep”开始运行
- 若是写入“/sys/power/autosleep”的值为“off”,则调用pm_wakep_autosleep_enabled(true)
pm_wakep_autosleep_enabled()主要用于更新所有wakeup source的autosleep_enabled标志(为何不使用全局的autosleep_enabled标记?), 对于为active状态的wakeup source,说明它在阻止autosleep, 若此时参数为true, 则更新wakeup source的start_prevent_time(开始组织suspend的时间), 若参数为false,则更新wakeup source的prevent_sleep_time(总共阻止睡眠的时间)
###5. try_to_suspend()
static void try_to_suspend(struct work_struct *work)
{
unsigned int initial_count, final_count;
if (!pm_get_wakeup_count(&initial_count, true))
goto out;
mutex_lock(&autosleep_lock);
if (!pm_save_wakeup_count(initial_count) ||
system_state != SYSTEM_RUNNING) {
mutex_unlock(&autosleep_lock);
goto out;
}
if (autosleep_state == PM_SUSPEND_ON) {
mutex_unlock(&autosleep_lock);
return;
}
if (autosleep_state >= PM_SUSPEND_MAX)
hibernate();
else
pm_suspend(autosleep_state);
mutex_unlock(&autosleep_lock);
if (!pm_get_wakeup_count(&final_count, false))
goto out;
if (final_count == initial_count)
schedule_timeout_uninterruptible(HZ / 2);
queue_up_suspend_work();
}
- 该接口是wakeup count的一个例子, 如wakeup count中所描述, 调用pm_get_wakeup_count(block为true),阻塞等待, 直到没有wakeup event在处理才返回count, 如果读取失败, 则调度workqueue, 重新进入try_to_suspend()
- 将获取wakeup count,保存在initial_count中
- 将initial_count写入saved_count, 如果失败, 说明产生了wakeup event, 则则调度workqueue, 重新进入try_to_suspend()
- 如果成功,且当前系统状态为running,根据autosleep状态,调用hibernate或者pm_suspend,suspend系统
- 运行到mutex_unlock(&autosleep_lock)这一行时,说明是suspend过程中遇到了wakeup event, 或者是suspend之后被唤醒
- 非阻塞情况下在读取wakeup count, 保存到final count中, 如果此时有wakeup event在处理, 则调度workqueue, 重新进入try_to_suspend()
- 如果initial_count和final count相同, 说明suspend之间和resume之后的wakeup count相同, 比较奇怪, 需要等待0.5s后再尝试则调度workqueue, 重新进入try_to_suspend()
在queue_up_suspend_work()中运行try_to_suspend(), try_to_suspend()中又调用queue_up_suspend_work(), 这样就形成了一个环, 尝试suspend, resume后又继续尝试suspend, 只要通过“/sys/power/autosleep”enable autosleep之后, 这个环就开始运行, 那么何时停止autosleep呢, 那就是向“/sys/power/autosleep”写如”off”后, 在try_to_suspend()中判断到autosleep_state == PM_SUSPEND_ON时, 直接返回, 不再调用queue_up_suspend_work(), 这个环就断开了, autosleep也就停止了
autosleep的源码很少, 只有100行左右, 可自行阅读