目录
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_noir
q(PMSG_SUSPEND);
-
if (error) {
-
pr_err(
"noirq suspend of devices failed\n");
-
goto Platform_early_resume;
-
}
-
error = platform_suspend_prepare_noir
q(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_noir
q(state);
-
dpm_resume_noir
q(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后续会有专门章节讲解)。


本文深入剖析Linux内核PM Core(Power Management Core)模块,包括其主要功能、数据类型如`suspend_state_t`、`suspend_stats`和`platform_suspend_ops`,以及关键流程如suspend和唤醒流程。PM Core负责整合低功耗模块,提供系统睡眠和唤醒的主干道。文章详细解释了软件处理流程,如设备挂起、唤醒的步骤,并展示了主要函数如`pm_suspend`、`enter_state`等的实现。此外,介绍了如何通过`suspend_set_ops`注册平台特定的低功耗操作。

2426

被折叠的 条评论
为什么被折叠?



