目录
2.3 struct platform_suspend_ops
1、模块主要功能介绍
PM Core,SR(suspend/resume)主流程,顾名思义就是把Linux Kernel中各个低功耗模块组合在一起,成为睡眠和唤醒流程的主干道,我们称之为SR主流程。其在内核中特性的开关主要受以下宏控制:
CONFIG_PM、CONFIG_SUSPEND、CONFIG_PM_AUTOSLEEP、CONFIG_FREEZER、CONFIG_PM_SLEEP
相关代码主要在kernel/power/目录下
kernel/power/main.c ----提供用户态接口,以及pm notifier相关接口
kernel/power/suspend.c ----Suspend功能的主流程
kernel/power/console.c ----Suspend过程中对控制台的处理逻辑
kernel/power/process.c ----Suspend过程中对进程的处理逻辑2、主要涉及的数据类型
2.1 suspend_state_t
数据类型定义在include/linux/suspend.h中,表示低功耗要进入的状态,在kernel中主要有4种状态,如下
typedef int __bitwise suspend_state_t;
#define PM_SUSPEND_ON ((__force suspend_state_t) 0)
#define PM_SUSPEND_TO_IDLE ((__force suspend_state_t) 1)
#define PM_SUSPEND_STANDBY ((__force suspend_state_t) 2)
#define PM_SUSPEND_MEM ((__force suspend_state_t) 3)
#define PM_SUSPEND_MIN PM_SUSPEND_TO_IDLE
#define PM_SUSPEND_MAX ((__force suspend_state_t) 4)1)PM_SUSPEND_ON: 设备处于全电源状态,也就是正常工作状态。
2)PM_SUSPEND_TO_IDLE:外设做完susspend回调后,进入到idle回调,不会disable secondary PM_SUSPEND_STANDBY:设备处于省电状态,但还能够接收某些事件,详细的行为取决与详细的设备。
3)PM_SUSPEND_MEM:suspend to memory,设备进入睡眠状态,但全部的数据还保存在内存中,仅仅有某些外部中断才干够唤醒设备。
4)PM_SUSPEND_MAX:字面意思是suspend到disk中,对应kernel中的hibernate。
大多数的Android设备都仅仅支持当中的两种:PM_SUSPEND_ON 和PM_SUSPEND_MEM,所以后续的讨论中说到suspend的地方,均是指PM_SUSPEND_MEM。
2.2 struct suspend_stats
结构体定义在include/linux/suspend.h中,主要记录低功耗流程中的维测数据
struct suspend_stats {
int success; ----表示睡眠失败的次数
int fail; ----表示睡眠失败的次数
int failed_freeze;
int failed_prepare;
int failed_suspend;
int failed_suspend_late;
int failed_suspend_noirq;
int failed_resume;
int failed_resume_early;
int failed_resume_noirq;
#define REC_FAILED_NUM 2
int last_failed_dev;
char failed_devs[REC_FAILED_NUM][40];
int last_failed_errno;
int errno[REC_FAILED_NUM];
int last_failed_step;
enum suspend_stat_step failed_steps[REC_FAILED_NUM];
};2.3 struct platform_suspend_ops
该结构体定义在include/linux/suspend.h中,主要记录和平台相关的低功耗主流程相关回调函数
struct platform_suspend_ops {
int (*valid)(suspend_state_t state);
int (*begin)(suspend_state_t state);
int (*prepare)(void);
int (*prepare_late)(void);
int (*enter)(suspend_state_t state);
void (*wake)(void);
void (*finish)(void);
bool (*suspend_again)(void);
void (*end)(void);
void (*recover)(void);
};1) valid回调:回调以确定平台是否支持给定的系统睡眠状态。有效(即支持的)状态在 /sys/power/state 中公布,即2.1中所描述的suspend_state_t。请注意,如果条件不正确,仍然可能无法进入给定的系统睡眠状态。
2)begin回调:初始化到给定系统睡眠状态的转换,在挂起设备之前执行。@begin传递给平台代码的信息应在@end执行后立刻被忽略掉。如果@begin失败(即返回非零),PM主流程将不会调用@prepare(), @enter() 和@finish()回调。begin回调是可选的,不是一定要实现的。但是一旦实现了,那么传递给@enter()的参数是多余的,应忽略。
3)prepare回调:准备platform进入由@begin() 指示的系统睡眠状态。@begin()在设备挂起之后(即,为每个设备执行了适当的.suspend()方法)和设备驱动程序的late suspend回调之前立即调用。成功时返回0,否则返回负错误代码,在这种情况下,系统无法进入所需的睡眠状态(@prepare_late(),@enter(),和@wake()在这种情况下将不会调用)。
4)prepare_late回调:完成平台准备,以进入@begin()指示的系统睡眠状态。在禁用nonboot CPU之前和执行设备驱动程序的late suspend回调之后调用@prepare_late。成功时返回0,否则返回负错误代码,在这种情况下,系统无法进入所需的睡眠状态(@enter()将不会执行)。
5)Enter回调:进入由@begin() 指示的系统睡眠状态,如果未实现@begin(),则进入由参数表示的系统睡眠状态。此回调是必填的。成功时返回0或一个为负的错误码,在这种情况下,系统无法进入所需的睡眠状态。
6)wake回调:当系统刚刚离开睡眠状态时,在启用nonboot CPU之后,在执行设备驱动程序的early resume回调之前调用。此回调是可选的,但应由实现@prepare_late()的平台实现。如果实现,则它始终在@prepare_late和@enter()之后调用,即使其中一个失败。
7)finish回调:完成平台的唤醒。@finish在调用设备驱动程序的regular suspend回调之前调用。此回调是可选的,但如果这个平台实现了prepare回调,那么平台也应该实现finish回调。如果实现了,则它始终在@enter()和@wake()之后调用,即使其中任何一个失败。它在失败的@prepare后执行。
8)suspend_again回调:返回系统是否应再次suspend(true)或不suspend(false)。如果平台希望在挂起期间轮询传感器或执行某些代码,而不调用用户空间和大多数设备,suspend_again回调是一个假设已经设置了定期唤醒或警报唤醒的绝佳位置。这允许在用户和设备视图中保持挂起的同时执行一些代码。
9)end回调:在恢复设备后立即由PM主流程调用,以向平台指示系统已返回工作状态或向睡眠状态的转换已中止。此回调是可选的,但应由实现@begin()的平台实现。因此,实现@begin()的平台还应提供@end(),该@end() 清理在@enter()之前中止的transitions。
10)recover回调:从挂起失败中恢复平台。如果设备挂起失败,由PM主流程调用。此回调是可选的,仅应由在这种情况下需要特殊恢复操作的平台实施。
3、软件处理流程
前边把pm core的主流程的主要功能及主要结构体做了分析和讲解,那么接下来我们来分析下睡眠主流程的时序,系统是如何进入睡眠的,又是如何唤醒的。
3.1 suspend流程
如果把kernel中的PM特性开关打开的话,会按照如下流程执行:
1)在init.rc中,通过echo mem > /sys/power/autosleep会触发睡眠持锁检查;
2)如果检查没有组件持锁,则继续进入pm core睡眠流程,如果有组件持锁,则停止睡眠流程;
3)pm core睡眠流程中,会先后调用dpm把外设suspend、把secondary cpu停掉、回调syscore回调函数、cpu suspend、ATF中执行安全相关操作、给主控核请求下电。
4)主控核给kernel子系统下电后,需要接管kernel子系统的唤醒中断,以便在需要时,把kernel系统唤醒。
需要说明的一点是,在执行过程中,都可以阻止睡眠,一旦某个环节返回错误,则本次睡眠流程停止并返回。
当前实际的执行要比我们说的会复杂一些,在这里也仅仅是为了帮助大家理解没有细化,在后面的章节中我们会逐个子系统、子框架展开进行详细的讲解。

对于suspend流程的最后阶段,主要涉及以下操作:

3.2 唤醒流程
低功耗主控核接收到kernel子系统的唤醒中断后,会对其进行上电解复位动作,后续走的流程为suspend流程的逆流程,在此不做过多介绍,后边我们会对全流程的每个组成部分进行拆解介绍。
4、PM Core主要函数实现
4.1 pm_suspend
函数原型为:
int pm_suspend(suspend_state_t state)
{
int error;
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
return -EINVAL;
pr_info("suspend entry (%s)\n", mem_sleep_labels[state]);
error = enter_state(state);
if (error) {
suspend_stats.fail++;
dpm_save_failed_errno(error);
} else {
suspend_stats.success++;
}
pr_info("suspend exit\n");
return error;
}该函数为pm core的主入口函数,供autosleep中的suspend_work执行时调用。函数首先会判断入参状态是否为有效状态,然后会进入到下一层函数enter_state中执行处理。该函数同时也会记录睡眠失败和成功的相关维测记录,以备在定位问题时查看。
4.2 enter_state
函数原型为:
static int enter_state(suspend_state_t state)
{
int error;
trace_suspend_resume(TPS("suspend_enter"), state, true);
if (state == PM_SUSPEND_TO_IDLE) {
#ifdef CONFIG_PM_DEBUG
if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
pr_warn("Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n");
return -EAGAIN;
}
#endif
} else if (!valid_state(state)) {
return -EINVAL;
}
if (!mutex_trylock(&system_transition_mutex))
return -EBUSY;
if (state == PM_SUSPEND_TO_IDLE)
s2idle_begin();
if (sync_on_suspend_enabled) {
trace_suspend_resume(TPS("sync_filesystems"), 0, true);
ksys_sync_helper();
trace_suspend_resume(TPS("sync_filesystems"), 0, false);
}
pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]);
pm_suspend_clear_flags();
error = suspend_prepare(state);
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER))
goto Finish;
trace_suspend_resume(TPS("suspend_enter"), state, false);
pm_pr_dbg("Suspending system (%s)\n", mem_sleep_labels[state]);
pm_restrict_gfp_mask();
error = suspend_devices_and_enter(state);
pm_restore_gfp_mask();
Finish:
events_check_enabled = false;
pm_pr_dbg("Finishing wakeup.\n");
suspend_finish();
Unlock:
mutex_unlock(&system_transition_mutex);
return error;
}主要处理过程:
1)enter_state会调用valid_state来判断当前睡眠的ops是否有赋值来满足睡眠流程调用;
static bool valid_state(suspend_state_t state)
{
/*
* PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states need low level
* support and need to be valid to the low level
* implementation, no valid callback implies that none are valid.
*/
return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
}suspend_ops是通过各个芯片平台调用suspend_set_ops来定制化赋值的,每个平台都有自己的实现,通过suspend_set_ops来赋值给suspend_ops供PM Core调用。
void suspend_set_ops(const struct platform_suspend_ops *ops)
{
lock_system_sleep();
suspend_ops = ops;
if (valid_state(PM_SUSPEND_STANDBY)) {
mem_sleep_states[PM_SUSPEND_STANDBY] = mem_sleep_labels[PM_SUSPEND_STANDBY];
pm_states[PM_SUSPEND_STANDBY] = pm_labels[PM_SUSPEND_STANDBY];
if (mem_sleep_default == PM_SUSPEND_STANDBY)
mem_sleep_current = PM_SUSPEND_STANDBY;
}
if (valid_state(PM_SUSPEND_MEM)) {
mem_sleep_states[PM_SUSPEND_MEM] = mem_sleep_labels[PM_SUSPEND_MEM];
if (mem_sleep_default >= PM_SUSPEND_MEM)
mem_sleep_current = PM_SUSPEND_MEM;
}
unlock_system_sleep();
}
EXPORT_SYMBOL_GPL(suspend_set_ops);2)接下来会调用suspend_prepare来执行pm notifier回调,以及suspend_freeze_processes来冻结可以冻结的进程(notifier和进程冻结相关实现后续单独会讲)
static int suspend_prepare(suspend_state_t state)
{
int error;
if (!sleep_state_supported(state))
return -EPERM;
pm_prepare_console();
error = pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND);
if (error)
goto Restore;
trace_suspend_resume(TPS("freeze_processes"), 0, true);
error = suspend_freeze_processes();
trace_suspend_resume(TPS("freeze_processes"), 0, false);
if (!error)
return 0;
suspend_stats.failed_freeze++;
dpm_save_failed_step(SUSPEND_FREEZE);
pm_notifier_call_chain(PM_POST_SUSPEND);
Restore:
pm_restore_console();
return error;
}
都满足条件后,会进入到下一级函数suspend_devices_and_enter中执行更进一步的suspend处理。
4.3 suspend_devices_and_enter
函数原型为:
int suspend_devices_and_enter(suspend_state_t state)
{
int error;
bool wakeup = false;
if (!sleep_state_supported(state))
return -ENOSYS;
pm_suspend_target_state = state;
if (state == PM_SUSPEND_TO_IDLE)
pm_set_suspend_no_platform();
error = platform_suspend_begin(state);
if (error)
goto Close;
suspend_console();
suspend_test_start();
error = dpm_suspend_start(PMSG_SUSPEND);
if (error) {
pr_err("Some devices failed to suspend, or early wake event detected\n");
goto Recover_platform;
}
suspend_test_finish("suspend devices");
if (suspend_test(TEST_DEVICES))
goto Recover_platform;
do {
error = suspend_enter(state, &wakeup);
} while (!error && !wakeup && platform_suspend_again(state));
Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resume devices");
trace_suspend_resume(TPS("resume_console"), state, true);
resume_console();
trace_suspend_resume(TPS("resume_console"), state, false);
Close:
platform_resume_end(state);
pm_suspend_target_state = PM_SUSPEND_ON;
return error;
Recover_platform:
platform_recover(state);
goto Resume_devices;
}主要处理过程:
1)通过调用platform_suspend_begin来执行前边讲解过的平台注册的suspend_ops->begin回调;
2)通过调用dpm_suspend_start来执行DPM的prepare和suspend两个级别的回调处理(DPM后续会有专门章节讲解)。
3)通过suspend_enter来进入下一层级的SR流程;
4)需要注意的是,睡眠流程是对称的,前边是睡眠流程,睡眠流程退出后接着执行的是唤醒流程,从suspend_enter睡下去,醒来后也是从suspend_enter退出来的。
5)suspend_enter退出后表示睡眠结束,会接着调用dpm_resume_end来执行DPM中与prepare和suspend对应的resume和complete回调。
6)然后会通过platform_resume_end来调用平台注册的与suspend_ops->begin对应的suspend_ops->end回调。
4.4 suspend_enter
函数原型为:
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
int error;
error = platform_suspend_prepare(state);
if (error)
goto Platform_finish;
error = dpm_suspend_late(PMSG_SUSPEND);
if (error) {
pr_err("late suspend of devices failed\n");
goto Platform_finish;
}
error = platform_suspend_prepare_late(state);
if (error)
goto Devices_early_resume;
error = dpm_suspend_noirq(PMSG_SUSPEND);
if (error) {
pr_err("noirq suspend of devices failed\n");
goto Platform_early_resume;
}
error = platform_suspend_prepare_noirq(state);
if (error)
goto Platform_wake;
if (suspend_test(TEST_PLATFORM))
goto Platform_wake;
if (state == PM_SUSPEND_TO_IDLE) {
s2idle_loop();
goto Platform_wake;
}
error = suspend_disable_secondary_cpus();
if (error || suspend_test(TEST_CPUS))
goto Enable_cpus;
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
system_state = SYSTEM_SUSPEND;
error = syscore_suspend();
if (!error) {
*wakeup = pm_wakeup_pending();
if (!(suspend_test(TEST_CORE) || *wakeup)) {
trace_suspend_resume(TPS("machine_suspend"),
state, true);
error = suspend_ops->enter(state);
trace_suspend_resume(TPS("machine_suspend"),
state, false);
} else if (*wakeup) {
error = -EBUSY;
}
syscore_resume();
}
system_state = SYSTEM_RUNNING;
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
Enable_cpus:
suspend_enable_secondary_cpus();
Platform_wake:
platform_resume_noirq(state);
dpm_resume_noirq(PMSG_RESUME);
Platform_early_resume:
platform_resume_early(state);
Devices_early_resume:
dpm_resume_early(PMSG_RESUME);
Platform_finish:
platform_resume_finish(state);
return error;
}
函数主要功能为:
1)通过调用platform_suspend_prepare来执行前边讲解过的平台注册的suspend_ops->prepare回调。
2)通过调用DPM接口dpm_suspend_late来执行dpm中suspend_late级别的回调;
3)通过调用DPM接口dpm_suspend_noirq来执行dpm中suspend_noirq级别的回调;
4)通过调用platform_suspend_prepare_noirq来执行前边讲解过的平台注册的suspend_ops->prepare_late回调。
5)通过调用suspend_disable_secondary_cpus来把nonboot cpu停掉;
6)通过arch_suspend_disable_irqs来关闭中断,之后的流程中就不会再响应中断了。
7)通过调用syscore_suspend来回调OS中注册的syscore_suspend回调函数,该级别的回调是在屏蔽中断的上下文中执行的。
8)通过调用 suspend_ops->enter来执行平台注册的最终的平台相关的suspend函数,kernel自身的pm core走到这里基本上就算到底了。
9)需要注意的是,pm core从 suspend_ops->enter睡下去,也需要从 suspend_ops->enter中醒过来。
10)醒来后会按照睡眠流程的对称的resume流程来执行唤醒动作:syscore_resume---> arch_suspend_enable_irqs--->suspend_enable_secondary_cpus--->platform_resume_noirq--->dpm_resume_noirq--->platform_resume_early--->dpm_resume_early--->platform_resume_finish
5、补充说明
在一个系统中,如果要使能pm core,除了第一章节说的宏需要打开外,还需要调用suspend_set_ops来注册 suspend_ops->enter,这个suspend_ops可以各个平台自己实现,也可以使用kernel的psci的框架(PSCI后续会有专门章节讲解)。
