error=-ENOMEM;
printk(KERN_ERR"PM:
Noenoughmemory\n");
}
}
if(!
error)
return0;
Thaw:
suspend_thaw_processes();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
returnerror;
}
让外设进入休眠
现在,所有的进程(也包括workqueue/kthread)都已经停止了,内核态人物有可能在停止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁,所以在外设的suspend()函数里面作lock/unlock锁要非常小心,这里建议设计的时候就不要在suspend()里面等待锁.而且因为suspend的时候,有一些Log是无法输出的,所以一旦出现问题,非常难调试.
然后kernel在这里会尝试释放一些内存.
最后会调用suspend_devices_and_enter()来把所有的外设休眠,在这个函数中,如果平台注册了suspend_pos(通常是在板级定义中定义和注册),这里就会调用suspend_ops->begin(),然后driver/base/power/main.c中的device_suspend()->dpm_suspend()会被调用,他们会依次调用驱动的suspend()回调来休眠掉所有的设备.
当所有的设备休眠以后,suspend_ops->prepare()会被调用,这个函数通常会作一些准备工作来让板机进入休眠.接下来Linux,在多核的CPU中的非启动CPU会被关掉,通过注释看到是避免这些其他的CPU造成racecondion,接下来的以后只有一个CPU在运行了.
suspend_ops是板级的电源管理操作,通常注册在文件arch/xxx/mach-xxx/pm.c中.
接下来,suspend_enter()会被调用,这个函数会关闭archirq,调用device_power_down(),它会调用suspend_late()函数,这个函数是系统真正进入休眠最后调用的函数,通常会在这个函数中作最后的检查.如果检查没问题,接下来休眠所有的系统设备和总线,并且调用suspend_pos->enter()来使CPU进入省电状态.这时候,就已经休眠了.代码的执行也就停在这里了.
/**
*suspend_devices_and_enter-suspenddevicesandenterthedesiredsystem
*sleepstate.
*@state:
statetoenter
*/
intsuspend_devices_and_enter(suspend_state_tstate)
{
interror,ftrace_save;
if(!
suspend_ops)
return-ENOSYS;
if(suspend_ops->begin){
error=suspend_ops->begin(state);
if(error)
gotoClose;
}
suspend_console();
ftrace_save=__ftrace_enabled_save();
suspend_test_start();
error=device_suspend(PMSG_SUSPEND);
if(error){
printk(KERN_ERR"PM:
Somedevicesfailedtosuspend\n");
gotoRecover_platform;
}
suspend_test_finish("suspenddevices");
if(suspend_test(TEST_DEVICES))
gotoRecover_platform;
if(suspend_ops->prepare){
error=suspend_ops->prepare();
if(error)
gotoResume_devices;
}
if(suspend_test(TEST_PLATFORM))
gotoFinish;
error=disable_nonboot_cpus();
if(!
error&&!
suspend_test(TEST_CPUS))
suspend_enter(state);
enable_nonboot_cpus();
Finish:
if(suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
suspend_test_start();
device_resume(PMSG_RESUME);
suspend_test_finish("resumedevices");
__ftrace_enabled_restore(ftrace_save);
resume_console();
Close:
if(suspend_ops->end)
suspend_ops->end();
returnerror;
Recover_platform:
if(suspend_ops->recover)
suspend_ops->recover();
gotoResume_devices;
}
Resume
如果在休眠中系统被中断或者其他事件唤醒,接下来的代码就会开始执行,这个唤醒的顺序是和休眠的循序相反的,所以系统设备和总线会首先唤醒,使能系统中断,使能休眠时候停止掉的非启动CPU,以及调用suspend_ops->finish(),而且在suspend_devices_and_enter()函数中也会继续唤醒每个设备,使能虚拟终端,最后调用suspend_ops->end().
在返回到enter_state()函数中的,当suspend_devices_and_enter()返回以后,外设已经唤醒了,但是进程和任务都还是冻结状态,这里会调用suspend_finish()来解冻这些进程和任务,而且发出Notify来表示系统已经从suspend状态退出,唤醒终端.
到这里,所有的休眠和唤醒就已经完毕了,系统继续运行了.
Android休眠(suspend)
在一个打过android补丁的内核中,state_store()函数会走另外一条路,会进入到request_suspend_state()中,这个文件在earlysuspend.c中.这些功能都是android系统加的,后面会对earlysuspend和lateresume进行介绍.
涉及到的文件:
∙linux_source/kernel/power/main.c
∙linux_source/kernel/power/earlysuspend.c
∙linux_source/kernel/power/wakelock.c
特性介绍
EarlySuspend
Earlysuspend是android引进的一种机制,这种机制在上游备受争议,这里不做评论.这个机制作用在关闭显示的时候,在这个时候,一些和显示有关的设备,比如LCD背光,比如重力感应器,触摸屏,这些设备都会关掉,但是系统可能还是在运行状态(这时候还有wakelock)进行任务的处理,例如在扫描SD卡上的文件等.在嵌入式设备中,背光是一个很大的电源消耗,所以android会加入这样一种机制.
LateResume
LateResume是和suspend配套的一种机制,是在内核唤醒完毕开始执行的.主要就是唤醒在EarlySuspend的时候休眠的设备.
WakeLock
WakeLock在Android的电源管理系统中扮演一个核心的角色.WakeLock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和内核获得.这个锁可以是有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁.如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠.
AndroidSuspend
当用户写入mem或者standby到/sys/power/state中的时候,state_store()会被调用,然后Android会在这里调用request_suspend_state()而标准的Linux会在这里进入enter_state()这个函数.如果请求的是休眠,那么early_suspend这个workqueue就会被调用,并且进入early_suspend状态.
voidrequest_suspend_state(suspend_state_tnew_state)
{
unsignedlongirqflags;
intold_sleep;
spin_lock_irqsave(&state_lock,irqflags);
old_sleep=state&SUSPEND_REQUESTED;
if(debug_mask&DEBUG_USER_STATE){
structtimespects;
structrtc_timetm;
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.%09luUTC)\n",
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);
}elseif(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);
}
EarlySuspend
在early_suspend()函数中,首先会检查现在请求的状态还是否是suspend,来防止suspend的请求会在这个时候取消掉(因为这个时候用户进程还在运行),如果需要退出,就简单的退出了.如果没有,这个函数就会把earlysuspend中注册的一系列的回调都调用一次,然后同步文件系统,然后放弃掉main_wake_lock,这个wakelock是一个没有超时的锁,如果这个锁不释放,那么系统就无法进入休眠.
staticvoidearly_suspend(structwork_struct*work)
{
structearly_suspend*pos;
unsignedlongirqflags;
intabort=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\n",state);
mutex_unlock(&early_suspend_lock);
gotoabort;
}
if(debug_mask&DEBUG_SUSPEND)
pr_info("early_suspend:
callhandlers\n");
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\n");
sys_sync();
abort:
spin_lock_irqsave(&state_lock,irqflags);
if(state==SUSPEND_REQUESTED_AND_SUSPENDED)
wake_unlock(&main_wake_lock);
spin_unlock_irqrestore(&state_lock,irqflags);
}
LateResume
当所有的唤醒已经结束以后