Android休眠与唤醒
android是在传统的
linux内核电源管理设计的基础上,结合手机设计的实际需求而进化出的一套电源管理系统,其核心内容有:
wakelock、
early_suspend与
late_resume。
wakelock在
Android的电源管理系统中扮演一个核心的角 {MOD}。
wakelock是一种锁的机制,
只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和内核获得。这个锁可以是有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠。
当系统在启动完毕后,会自己去加一把名为“
main“的锁,而当系统有意愿去睡眠时则会先去释放这把“
main”锁,在
android中,在
early_suspend的最后一步会去释放“
main”锁(
wake_unlock:main)。释放完后则会去检查是否还有其他存在的锁,如果没有则直接进入睡眠过程。
它的缺点是,如果有某一应用获锁而不释放或者因一直在执行某种操作而没时间来释放的话,则会导致系统一直进入不了睡眠状态,功耗过大。
early_suspend:先于
linux内核的睡眠过程被调用。一般在手机系统的设计中对背光的操作等采用此类方法,因为背光需要的能耗过大。当然此操作与
late_resume是配套使用的。一些在内核中要预先进行处理的事件可以先注册上
early_suspend函数,当系统要进入睡眠之前会首先调用这些注册的函数。
本文中,
linuxkernel版本为
linux-3.0.21,
android版本为
android
4.0.4
与
android休眠唤醒主要相关的文件主要有:
-
linux_source/kernel/power/main.c
-
linux_source/kernel/power/earlysuspend.c
-
linux_source/kernel/power/wakelock.c
-
linux_source/kernel/power/process.c
-
linux_source/driver/base/power/main.c
-
linux_source/arch/arm/mach-msm/pm-8x60.c
Android休眠过程如下:
当用户读写
/sys/power/state时,
/kernel/power/main.c中的
state_store()函数会被调用。其中,
android的
early_suspend会执行
request_suspend_state(state);而标准的
linux休眠则执行
error=
enter_state(state);
static ssize_tstate_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf,size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_EARLYSUSPEND
suspend_state_t state =PM_SUSPEND_ON;
#else
suspend_state_t state =PM_SUSPEND_STANDBY;
#endif
const char * const *s;
#endif
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '
', n);
len = p ? p - buf : n;
/* First, check if we arerequested to hibernate */
if (len == 4 &&!strncmp(buf, "disk", len)) {
error = hibernate();
goto Exit;
}
#ifdef CONFIG_SUSPEND
for (s =&pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
if (*s && len ==strlen(*s) && !strncmp(buf, *s, len))
break;
}
if (state
#ifdefCONFIG_EARLYSUSPEND
//android的linux内核会定义该宏,首先进入eralysuspend模式
if (state ==PM_SUSPEND_ON || valid_state(state)) {
error = 0;
request_suspend_state(state);
}
#else
//标准linux内核直接enter_state()函数
error=enter_state(state);
#endif
#endif
Exit:
return error ? error : n;
}
在request_suspend_state(state)函数中,会调用early_suspend_work的工作队列,从而进入early_suspend()函数中。
staticDECLARE_WORK(early_suspend_work, early_suspend);
voidrequest_suspend_state(suspend_state_t new_state)
{
unsigned long irqflags;
int old_sleep;
spin_lock_irqsave(&state_lock,irqflags);
old_sleep = state &SUSPEND_REQUESTED;
if (debug_mask &DEBUG_USER_STATE) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec,&tm);
pr_info("request_suspend_state:%s (%d->%d) at %lld "
"(%d-%02d-%02d%02d:%02d:%02d.%09lu UTC)
",
new_state !=PM_SUSPEND_ON ? "sleep" : "wakeup",
requested_suspend_state,new_state,
ktime_to_ns(ktime_get()),
tm.tm_year + 1900,tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min,tm.tm_sec, ts.tv_nsec);
}
if (!old_sleep &&new_state != PM_SUSPEND_ON) {
state |=SUSPEND_REQUESTED;
queue_work(suspend_work_queue,&early_suspend_work);
} else if (old_sleep &&new_state == PM_SUSPEND_ON) {
state &=~SUSPEND_REQUESTED;
wake_lock(&main_wake_lock);
queue_work(suspend_work_queue,&late_resume_work);
}
requested_suspend_state =new_state;
spin_unlock_irqrestore(&state_lock,irqflags);
}
在early_suspend()函数中,首先要判断当前请求的状态是否还是suspend,若不是,则直接退出了;若是,函数会调用已经注册的early_suspend的函数。然后同步文件系统,最后释放main_wake_lock。
static voidearly_suspend(struct work_struct *work)
{
struct early_suspend*pos;
unsigned long irqflags;
int abort = 0;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock,irqflags);
if(state
==SUSPEND_REQUESTED)
state |= SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock,irqflags);
if (abort) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("early_suspend:abort, state %d
", state);
mutex_unlock(&early_suspend_lock);
goto abort;
}
if (debug_mask &DEBUG_SUSPEND)
pr_info("early_suspend:call handlers
");
list_for_each_entry(pos,&early_suspend_handlers,link)
{
if(pos->suspend != NULL)
pos->suspend(pos);
}
mutex_unlock(&early_suspend_lock);
if (debug_mask &DEBUG_SUSPEND)
pr_info("early_suspend:sync
");
suspend_sys_sync_queue();
abort:
spin_lock_irqsave(&state_lock,irqflags);
if(state
==SUSPEND_REQUESTED_AND_SUSPENDED)
wake_unlock(&main_wake_lock);
spin_unlock_irqrestore(&state_lock,irqflags);
}
在wake_unlock()中,删除链表中wake_lock节点,判断当前是否存在wake_lock,若wake_lock的数目为0,则调用工作队列suspend_work,进入suspend状态。
staticDECLARE_WORK(suspend_work, suspend);
void wake_unlock(structwake_lock *lock)
{
int type;
unsigned long irqflags;
spin_lock_irqsave(&list_lock,irqflags);
type = lock->flags &WAKE_LOCK_TYPE_MASK;
#ifdefCONFIG_WAKELOCK_STAT
wake_unlock_stat_locked(lock,0);
#endif
if (debug_mask &DEBUG_WAKE_LOCK)
pr_info("wake_unlock:%s
", lock->name);
lock->flags &=~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
list_del(&lock->link);
list_add(&lock->link,&inactive_locks);
if (type ==WAKE_LOCK_SUSPEND) {
longhas_lock
=has_wake_lock_locked(type);
if (has_lock > 0) {
if (debug_mask &DEBUG_EXPIRE)
pr_info("wake_unlock:%s, start expire timer, "
"%ld
",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
",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);
#ifdefCONFIG_WAKELOCK_STAT
update_sleep_wait_stats_locked(0);
#endif
}
}
spin_unlock_irqrestore(&list_lock,irqflags);
}
在suspend()函数中,先判断当前是否有wake_lock,若有,则退出;然后同步文件系统,最后调用pm_suspend()函数。
static void suspend(structwork_struct *work)
{
int ret;
int entry_event_num;
if(has_wake_lock(WAKE_LOCK_SUSPEND)){
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend:abort suspend
");
return;
}
entry_event_num =current_event_num;
suspend_sys_sync_queue();
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend:enter suspend
");
ret=pm_suspend(requested_suspend_state);
if (debug_mask &DEBUG_EXIT_SUSPEND) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec,&tm);
pr_info("suspend:exit suspend, ret = %d "
"(%d-%02d-%02d%02d:%02d:%02d.%09lu UTC)
", ret,
tm.tm_year + 1900,tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min,tm.tm_sec, ts.tv_nsec);
}
if (current_event_num ==entry_event_num) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend:pm_suspend returned with no event
");
wake_lock_timeout(&unknown_wakeup,HZ / 2);
}
}
在pm_suspend()函数中,enter_state()函数被调用,从而进入标准linux休眠过程。
intpm_suspend(suspend_state_t state)
{
if (state >PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
returnenter_state(state);
return -EINVAL;
}
在enter_state()函数中,首先检查一些状态参数,再同步文件系统,然后调用suspend_prepare()来冻结进程,最后调用suspend_devices_and_enter()让外设进入休眠。
static intenter_state(suspend_state_t state)
{
int error;
if (!valid_state(state))
return -ENODEV;
if(!mutex_trylock(&pm_mutex))
return -EBUSY;
printk(KERN_INFO "PM:Syncing filesystems ... ");
suspend_sys_sync_queue();
printk("done.
");
pr_debug("PM:Preparing system for %s sleep
", pm_states[state]);
error=suspend_prepare();
if (error)
goto Unlock;
if(suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM:Entering %s sleep
", pm_states[state]);
error=suspend_devices_and_enter(state);
Finish:
pr_debug("PM:Finishing wakeup.
");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
在suspend_prepare()函数中,先通过pm_prepare_console();给suspend分配一个虚拟终端来输出信息,再广播一个系统进入suspend的通报,关闭用户态的helper进程,然后调用suspend_freeze_processes()来冻结进程,最后会尝试释放一些内存。
static intsuspend_prepare(void)
{
int error;
unsigned int free_pages;
if (!suspend_ops ||!suspend_ops->enter)
return -EPERM;
pm_prepare_console();
error=pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
goto Finish;
error= usermodehelper_disable();
if (error)
goto Finish;
if(suspend_freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
free_pages =global_page_state(NR_FREE_PAGES);
if (free_pages
pr_debug("PM: freesome memory
");
shrink_all_memory(FREE_PAGE_NUMBER- free_pages);
if (nr_free_pages()
error = -ENOMEM;
printk(KERN_ERR "PM:No enough memory
");
}
}
if (!error)
return 0;
Thaw:
suspend_thaw_processes();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
在suspend_freeze_processes()函数中调用了freeze_processes()函数,而freeze_processes()函数中又调用了try_to_freeze_tasks()来完成冻结任务。在冻结过程中,会判断当前进程是否有wake_lock,若有,则冻结失败,函数会放弃冻结。
static inttry_to_freeze_tasks(bool sig_only)
{
struct task_struct *g,*p;
unsigned long end_time;
unsigned int todo;
struct timeval start,end;
u64 elapsed_csecs64;
unsigned intelapsed_csecs;
unsigned int wakeup = 0;
do_gettimeofday(&start);
end_time = jiffies +TIMEOUT;
do {
todo = 0;
read_lock(&tasklist_lock);
do_each_thread(g,p)
{
if (frozen(p) ||!freezeable(p))
continue;
if(!freeze_task(p,sig_only))
continue;
/*
* Now that we've doneset_freeze_flag, don't
* perturb a task inTASK_STOPPED or TASK_TRACED.
* It is "frozenenough". If the task does wake
* up, it willimmediately call try_to_freeze.
*/
if(!task_is_stopped_or_traced(p) &&
!freezer_should_skip(p))
todo++;
} while_each_thread(g,p);
read_unlock(&tasklist_lock);
yield(); /* Yield isokay here */
if(todo
&&has_wake_lock(WAKE_LOCK_SUSPEND)){
wakeup= 1;
break;
}
if (time_after(jiffies,end_time))
break;
} while (todo);
do_gettimeofday(&end);
elapsed_csecs64 =timeval_to_ns(&end) - timeval_to_ns(&start);
do_div(elapsed_csecs64,NSEC_PER_SEC / 100);
elapsed_csecs =elapsed_csecs64;
if (todo) {
/* This does notunfreeze processes that are already frozen
* (we have slightlyugly calling convention in that respect,
* and caller must callthaw_processes() if something fails),
* but it cleans upleftover PF_FREEZE requests.
*/
if(wakeup) {
printk("
");
printk(KERN_ERR"Freezing of %s aborted
",
sig_only ? "userspace " : "tasks ");
}
else {
printk("
");
printk(KERN_ERR"Freezing of tasks failed after %d.%02d seconds "
"(%d tasksrefusing to freeze):
",
elapsed_csecs / 100,elapsed_csecs % 100, todo);
show_state();
}
read_lock(&tasklist_lock);
do_each_thread(g, p) {
task_lock(p);
if (freezing(p) &&!freezer_should_skip(p))
printk(KERN_ERR "%s
", p->comm);
cancel_freezing(p);
task_unlock(p);
} while_each_thread(g,p);
read_unlock(&tasklist_lock);
} else {
printk("(elapsed%d.%02d seconds) ", elapsed_csecs / 100,
elapsed_csecs % 100);
}
return todo ? -EBUSY : 0;
}
到现在,所有的进程(也包括workqueue/kthread)都已经停止了,内核态进程有可能在停止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁,所以在外设suspend()函数里面作lock/unlock锁要非常小心,建议不要在外设的suspend()里面等待锁。而且suspend的过程中,有一些log是无法输出的,所以一旦出现问题,非常难调试。
回到enter_state()函数中,再冻结进程完成后,调用suspend_devices_and_enter()函数让外设进入休眠。该函数中,首先休眠串口(之后不能再显示log,解决方法为在kernel配置选项的cmd_line中,添加”no_console_suspend”选项),再通过device_suspend()函数调用各驱动的suspend函数。
当外设进入休眠后,suspend_ops->prepare()被调用,suspend_ops是板级的PM操作(本文中粉红 {MOD}的函数,依赖于具体的平台),以高通8960为例,其注册在kernel/arch/arm/mach-msm/pm-8x60.c中,只定义了suspend_ops->enter()函数。
static structplatform_suspend_ops msm_pm_ops = {
.enter = msm_pm_enter,
.valid =suspend_valid_only_mem,
};
接下来,
intsuspend_devices_and_enter(suspend_state_t state)
{
int error;
if (!suspend_ops)
return -ENOSYS;
trace_machine_suspend(state);
if (suspend_ops->begin){
error =suspend_ops->begin(state);
if (error)
goto Close;
}
suspend_console();
suspend_test_start();
error =dpm_suspend_start(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM:Some devices failed to suspend
");
goto Recover_platform;
}
suspend_test_finish("suspenddevices");
if(suspend_test(TEST_DEVICES))
goto Recover_platform;
error =suspend_enter(state);
Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resumedevices");
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
trace_machine_suspend(PWR_EVENT_EXIT);
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices; }
接下来suspend_enter()被调用,其中多CPU中的非启动CPU被关闭。关闭IRQ。最后调用suspend_pos->enter()来使CPU进入省电状态。这时候,整个休眠过程完成,代码的执行也就停在这里了。
static intsuspend_enter(suspend_state_t state)
{
int error;
if (suspend_ops->prepare){
error =suspend_ops->prepare();
if (error)
goto Platform_finish;
}
error =dpm_suspend_noirq(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM:Some devices failed to power down
");
goto Platform_finish;
}
if(suspend_ops->prepare_late) {
error =suspend_ops->prepare_late();
if (error)
goto Platform_wake;
}
if(suspend_test(TEST_PLATFORM))
goto Platform_wake;
error= disable_nonboot_cpus();
if (error ||suspend_test(TEST_CPUS))
goto Enable_cpus;
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
error =syscore_suspend();
if (!error) {
if(!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {
error =suspend_ops->enter(state);
events_check_enabled =false;
}
syscore_resume();
}
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
Enable_cpus:
enable_nonboot_cpus();
Platform_wake:
if (suspend_ops->wake)
suspend_ops->wake();
dpm_resume_noirq(PMSG_RESUME);
Platform_finish:
if (suspend_ops->finish)
suspend_ops->finish();
return error;
}
在suspend_pos->enter()所对应的函数中,对共享资源的操作后,调用msm_pm_power_collapse。
static intmsm_pm_enter(suspend_state_t state)
{
boolallow[MSM_PM_SLEEP_MODE_NR];
int i;
int64_t period = 0;
int64_t time =msm_pm_timer_enter_suspend(&period);
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s
",__func__);
if (smp_processor_id()) {
__WARN();
goto enter_exit;
}
for (i = 0; i
structmsm_pm_platform_data *mode;
mode =&msm_pm_sleep_modes[MSM_PM_MODE(0, i)];
allow[i] =mode->suspend_supported && mode->suspend_enabled;
}
#ifdef CONFIG_SHSYS_CUST
if (sh_pm_debug_mask &SH_PM_DEBUG_SUSPEND_WFI) {
allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]= false;
allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]= false;
}
#endif
if(allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) {
struct msm_rpmrs_limits*rs_limits;
int ret;
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s: powercollapse
", __func__);
#ifdefCONFIG_SHSYS_CUST_DEBUG
if (sh_pm_debug_mask &SH_PM_DEBUG_IDLE_CLK)
pr_info( "%s():Execute clock_debug_print_enabled().
", __func__ );
#endif
clock_debug_print_enabled();
#ifdefCONFIG_MSM_SLEEP_TIME_OVERRIDE
if(msm_pm_sleep_time_override > 0) {
int64_t ns =NSEC_PER_SEC *
(int64_t)msm_pm_sleep_time_override;
msm_pm_set_max_sleep_time(ns);
msm_pm_sleep_time_override= 0;
}
#endif /*CONFIG_MSM_SLEEP_TIME_OVERRIDE */
if(MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask)
msm_rpmrs_show_resources();
rs_limits= msm_rpmrs_lowest_limits(false,
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,-1, -1);
if((MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask) &&
rs_limits)
pr_info("%s: limit%p: pxo %d, l2_cache %d, "
"vdd_mem %d,vdd_dig %d
",
__func__, rs_limits,
rs_limits->pxo,rs_limits->l2_cache,
rs_limits->vdd_mem,rs_limits->vdd_dig);
if (rs_limits) {
#ifdef CONFIG_SHSYS_CUST
if(sleep_test_is_enabled()== true)
{
pm_bms_masked_disable();
}
#endif
ret= msm_rpmrs_enter_sleep(
msm_pm_max_sleep_time,rs_limits, false, true);
if (!ret) {
int collapsed =msm_pm_power_collapse(false);
msm_rpmrs_exit_sleep(rs_limits,false, true,
collapsed);
}
} else {
pr_err("%s: cannotfind the lowest power limit
",
__func__);
}
time =msm_pm_timer_exit_suspend(time, period);
msm_pm_add_stat(MSM_PM_STAT_SUSPEND,time);
} else if(allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s:standalone power collapse
", __func__);
msm_pm_power_collapse_standalone(false);
} else if(allow[MSM_PM_SLEEP_MODE_RETENTION]) {
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s:retention
", __func__);
msm_pm_retention();
} else if(allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s:swfi
", __func__);
msm_pm_swfi();
}
在msm_pm_power_collapse函数中,保存睡眠前的硬件信息,更改CLOCK,调用msm_pm_spm_power_collapse,向