ARM技术论坛
直播中

建立建利12

8年用户 1380经验值
擅长:可编程逻辑 嵌入式技术
私信 关注
[经验]

Linux电源管理之Generic PM Suspend功能简析

1.前言

Linux内核提供了一个Suspend: Freeze、Standby和STR(Suspend to RAM),在用户向”/sys/power/state”文件分别写入“freeze”、“standby”和“mem”,可以触发他们。

内核中,Suspend及Resume Process PM Core、Device PM freeze、CPU冻结等设备的驱动、Platform PM、CPU的多个模块,涉及到控制台开关、进程、hotplug、wakeup等处理过的点。就让我们跟着核心代码,一见识他们吧。

2.暂停功能相关的代码分发

内核中Suspend功能相关的代码包括PM core、Device PM、Platform PM等几大块,具体如下:

1)PM核心

kernel/power/main.c----提供用户空间接口(/sys/power/state)

kernel/power/suspend.c----Suspend功能的主逻辑

kernel/power/suspend_test.c----Suspend功能的测试逻辑

kernel/powersole.c----暂停过程中对分析/的处理逻辑

kernel/power/process.c----Suspend过程中对进程的处理逻辑

2)设备PM

drivers/base/power/*----具体可参考“ Linux电源管理(4)_电源管理接口”的描述。

设备驱动----具体设备驱动的位置,不再涉及。

3)平台依赖PM

include/linux/suspend.h----定义平台依赖PM相关的操作函数集

arch/xxx/mach-xxx/xxx.c或者

arch/xxx/plat-xxx/xxx.c----平台相关的电源管理操作

3.suspend&resume过程概述

下面图片对Linux suspend&resume过程做了一个概述,读者可以顺着这个阅读说明内核源参考代码。具体的流程,可以后面的代码分析。

4.代码分析

4.1 暂停入口

在用户空间执行如下操作:

回声“冻结”> /sys/power/state
回声“待机”> /sys/电源/状态
回声“内存”> /sys/power/state

会通过 sysfs 触发暂停执行,相应的处理代码如下:

静态ssize_t state_store ( struct kobject * kobj , struct kobj_attribute * attr ,

const char * buf , size_t n )

{

suspend_state_t状态;

诠释错误;

错误= pm_autosleep_lock ();

如果(错误)

返回错误;

如果( pm_autosleep_state () > PM_SUSPEND_ON ) {

错误= - EBUSY ;

出去; _

}

state = decode_state ( buf , n );

如果(状态< PM_SUSPEND_MAX )

错误= pm_suspend (状态);

否则如果(状态== PM_SUSPEND_MAX )

错误=休眠();

别的

错误= - EINVAL ;

出:

pm_autosleep_unlock ();

返回错误?错误:n ;

}

power_attr (状态);

power__store的接口定义了一个名称的属性,存储接口为住_,接口在lockautosep功能后,该接口文件为用户的状态缓存(解析、待机或mem)状态的缓存,转换成状态参数。
state参数的类型为suspend_state_t,在include\linux\suspend.h中定义,为电源管理状态在内核中的表示。具体如下:

typedef int __bitwise suspend_state_t ;

#define PM_SUSPEND_ON (( __force suspend_state_t ) 0 )

#define PM_SUSPEND_FREEZE (( __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_FREEZE

#define PM_SUSPEND_MAX (( __force suspend_state_t ) 4 )

可知的,不是(SUSPEND_MAX,hibernate功能),而是调用state_pm_suspend接口,如果进行状态的处理。
pm_suspend在kernel/power/suspend.c中定义,处理所有的suspend过程。

4.2 pm_suspend & enter_state

pm_suspend 的非常简单,简单的做一下参数判断,直接调用enter_state接口,如下:

int pm_suspend ( suspend_state_t状态)

{

诠释错误;

如果(状态<= PM_SUSPEND_ON ||状态>= PM_SUSPEND_MAX )

返回- EINVAL ;

错误= enter_state (状态);

如果(错误){

暂停统计。失败++;

dpm_save_failed_errno (错误);

}其他{

暂停统计。成功++;

}

返回错误;

}

enter_state代码为:

静态int enter_state ( suspend_state_t状态)

{

诠释错误;

如果(!valid_state (状态))

返回-ENODEV ;_

if (! mutex_trylock (&​​ pm_mutex ))

返回-EBUSY ;_

如果(状态== PM_SUSPEND_FREEZE )

冻结开始();

printk ( KERN_INFO "PM: 同步文件系统 ..." );

sys_sync ();

printk (“完成。\n” );

pr_debug ( "PM: 为 %s 睡眠准备系统\n" , pm_states [ state ]);

错误= suspend_prepare (状态);

如果(错误)

转到解锁;

if ( suspend_test ( TEST_FREEZER ))

转到完成;

pr_debug ( "PM: 进入 %s sleep\n" , pm_states [ state ]);

pm_restrict_gfp_mask ();

错误= suspend_devices_and_enter (状态);

pm_restore_gfp_mask ();

完成:

pr_debug ( "PM: 完成唤醒。\n" );

暂停完成();

解锁:

mutex_unlock (&​​ pm_mutex );

返回错误;

}

主要工作包括:
a)调用valid_state,判断该平台是否支持该电源状态。
暂停的最终目标,并且该系统功能必须有平台才能完成,内核参与提供了相关的悬挂功能(在平台_挂起_操作中),平台代码代码/mach-xxx/pm.c实现,然后由PM核心的适当时机调用。如在这些函数包含有效函数,就是用于指示(PM核心,支持哪些状态)
最后看一下valid_state的实现(去掉了多余的代码):

bool valid_state ( suspend_state_t状态)

{

如果(状态== PM_SUSPEND_FREEZE ){

返回真;

}

/*

  • PM_SUSPEND_STANDBY 和 PM_SUSPEND_MEMORY 状态需要低级
  • 支持且需要对低级有效
  • 实现,没有有效的回调意味着没有一个是有效的。

*/

返回暂停操作&&暂停操作->有效&&暂停操作->有效(状态);

}

是冻结,独立的平台参与代码自动支持,则由我自己等待,需要调用suspend_opvalid回掉,如果由运行的平台代码判断是否支持。
b)加互斥锁,只允许一个实例处理暂停。
c)如果state是freeze,调用freeze_begin to freeze的特殊动作。我会在后面描述相关的特殊动作。同时进行分析freeze的动作,这里暂不
d)打印提示信息,同步文件系统。
e)调用包括suspend_prepare,进行suspend前的准备,主要是switch console和process&thread freeze。如果失败,则终止suspend过程。
f),调用suspend_devices_and_enter接口,然后该接口负责部分挂起和恢复的所有实际操作。前半部分,挂起控制台、挂起设备、关断、调用平台相关的suspend_ops使系统进入低状态。后半部分,在系统中被事件启动后平台,处理相关动作,调用相关的suspend_ops恢复系统、中断、恢复设备、恢复控制台。
g)最后,调用suspend_finish,恢复(或等待恢复)进程和线程,控制台。
4.3 暂停准备
suspend_prepare 的代码如下:

静态int suspend_prepare ( suspend_state_t状态)

{

诠释错误;

if ( need_suspend_ops ( state ) && (! suspend_ops || ! suspend_ops -> enter ))

返回-EPERM ;_

pm_prepare_console ();

错误= pm_notifier_call_chain ( PM_SUSPEND_PREPARE );

如果(错误)

转到完成;

错误= suspend_freeze_processes ();

如果(!错误)

返回0 ;

暂停统计。失败的冻结++;

dpm_save_failed_step ( SUSPEND_FREEZE );

完成:

pm_notifier_call_chain ( PM_POST_SUSPEND );

pm_restore_console ();

返回错误;

}

主要工作为:
a)检查是否提供了。进入拒绝,没有的话,返回错误。
b)该调用pmprepare_cons将当前的一个虚拟控制台和内核的km_g运行一下。更具体的分析,要在分析子系统的分析文章中说明。
c)调用开始的pm_notifier_call_chain,发送暂停消息(PM_SUSPEND_PREPARE),后面会详细描述。
d. 冻结用户空间进程)
e)如果freezing-of-tasks失败,调用pm_restore_console,将console切换回原来的console,并返回错误,以便能够终止suspend。
4.4 suspend_devices_and_enter
suspend_devices_and_enter 的流程复杂,代码实现如下:

int suspend_devices_and_enter ( suspend_state_t状态)

{

诠释错误;

布尔唤醒=假;

如果(需要_suspend_ops (状态)&& !suspend_ops )

返回-ENOSYS ;_

trace_machine_suspend (状态);

如果(需要暂停操作(状态)&&暂停操作->开始){

错误= suspend_ops- >开始(状态);

如果(错误)

转到关闭;

}

挂起控制台();

ftrace_stop ();

暂停测试开始();

错误= dpm_suspend_start ( PMSG_SUSPEND );

如果(错误){

printk ( KERN_ERR "PM: 一些设备挂起失败\n" );

转到恢复平台;

}

suspend_test_finish ( "挂起设备" );

if ( suspend_test ( TEST_DEVICES ))

转到恢复平台;

做{

错误= suspend_enter (状态,&唤醒);

} while (! error && ! wakeup && need_suspend_ops ( state )

&&暂停操作->暂停_再次&&暂停操作->暂停_再次());

简历设备:

暂停测试开始();

dpm_resume_end ( PMSG_RESUME );

suspend_test_finish (“恢复设备” );

ftrace_start ();

恢复控制台();

关闭:

如果(需要暂停操作(状态)&&暂停操作->结束)

暂停操作->结束();

trace_machine_suspend ( PWR_EVENT_EXIT );

返回错误;

恢复平台:

如果(需要暂停操作(状态)&&暂停操作->恢复)

暂停操作->恢复();

转到Resume_devices ;

}

a)再次检查平台代码是否需要提供以及是否提供了suspend_ops。
b)调用的开始其通知等待(有话),平台执行代码,以使可能的处理(需要的话)。
c)该调用suspend_console,挂起console。该由“kernel\printk.c”实现,主要是hold住一个lock,lock会阻止其他代码访问console。
d)调用ftrace_stop,停止ftrace。ftrace是一个很好的功能,后面再介绍。
e)调用d dpend_start,调用所有设备的-和->参考失败需要挂起_挂起功能(具体可描述设备电源管理(4)电源管理接口的),挂起设备可能的。挂起设备可能,,跳至Recover_platform,执行recover操作(suspend_ops->recover)。
f)以上都是suspend前的准备工作,此时,调用suspend_enter接口,使系统进入指定的电源状态。该接口的内容如下:

静态int suspend_enter ( suspend_state_t状态,布尔*唤醒)

{

诠释错误;

如果(需要暂停操作(状态)&&暂停操作->准备){

错误=暂停操作->准备();

如果(错误)

转到Platform_finish ;

}

错误= dpm_suspend_end ( PMSG_SUSPEND );

如果(错误){

printk ( KERN_ERR "PM: 某些设备无法关机\n" );

转到Platform_finish ;

}

如果(需要暂停操作(状态)&&暂停操作->准备延迟){

错误= suspend_ops- > prepare_late ();

如果(错误)

转到Platform_wake ;

}

if ( suspend_test ( TEST_PLATFORM ))

转到Platform_wake ;

/*

  • PM_SUSPEND_FREEZE 等于
  • 冻结的进程 + 挂起的设备 + 空闲的处理器。
  • 因此我们应该在不久之后调用 freeze_enter()

*所有设备都被暂停。

*/

如果(状态== PM_SUSPEND_FREEZE ){

freeze_enter ();

转到Platform_wake ;

}

错误= disable_nonboot_cpus ();

如果(错误|| suspend_test (TEST_CPUS ))

转到Enable_cpus ;

arch_suspend_disable_irqs ();

BUG_ON (! irqs_disabled ());

错误= syscore_suspend ();

如果(!错误){

*唤醒= pm_wakeup_pending ();

如果(!(suspend_test (TEST_CORE )|| *唤醒)){

错误= suspend_ops- >进入(状态);

events_check_enabled = false ;

}

syscore_resume ();

}

arch_suspend_enable_irqs ();

BUG_ON ( irqs_disabled ());

Enable_cpus :

enable_nonboot_cpus ();

平台唤醒:

如果(需要暂停操作(状态)&&暂停操作->唤醒)

暂停操作->唤醒();

dpm_resume_start ( PMSG_RESUME );

平台完成:

如果(需要暂停操作(状态)&&暂停操作->完成)

暂停操作->完成();

返回错误;

}

f1)该结束后,会通过返回值反馈输入是否成功,同时通过唤醒指针,反馈调用者,唤醒事件是否发生,导致电源切换失败。
    f2)调用ops的出现准备挂起(有_ops的表示,通知平台,以便让一些代码在重新进行状态切换时,做处理(需要的话)。可能会失败(平台代码拒绝),失败,需要跳到Platform_finish处,调用操作的finishsuspend,执行操作恢复。
    f3)调用d暂停设备,调用所有的->suspend_late和->suspend_noir设备和pm_suspend功能(具体可参考“ Linux管理(4)_电源管理接口”的描述),暂停延迟设备和需要在关闭下暂停的需要说明,这里的noirq,通过所有的执行断线形式,而不是通过关闭的可能的方式,操作会失败。
    f4)调用等待等待的prepare_一些平台指令(有代码的话),通知以使其处于关头,重新做处理(需要)。最终可能导致失败(平台代码出现失败),失败,需要跳platform_wake,调用suspend_ops的唤醒、调用,执行设备的resume_opfinish指令,执行操作。
    f5)如果是suspend to freeze,执行相应的操作状态,包括暂停进程、暂停设备(参数为_SUSPEND_FREEZE)、cpu进入idle。如果有事件使CPU​​从idle,跳至Platform_wake,执行wake操作。
    f6)调用disable_nonboot_cpus,杜绝所有的非boot cpu。同样失败,执行操作即可。
    f7)调用暂停_disable_irqs,则关闭为中断。
    f8)调用syscore_suspend,暂停系统核心。同样会失败,执行恢复操作。有关syscore,我会在另一篇文章中可以详细描述。
    f9),如果早点成功,一下子,切换很可能吧。
    f1如果一切状态顺利,调用suspend_ops的进入……,系统进行切换。
    f11)暂停过程中,事件触发,系统启动,然后继续执行,并返回。恢复动作原是暂停的动作,就不再是最终的系统了。
    f12)或者,由于意外,暂停,该函数也返回。

g)suspend_enter 返回,如果返回的原因不是发生的,并且不是唤醒事件的错误,而且不是唤醒事件的错误。suspend_ops_again 了。则要调用再次挂起呢。是否需要再次挂起呢?需要具体看的平台,谁。
h)继续resume操作,resume device、start ftrace、resume console、suspend_ops->end等。
i)该函数返回后,表示系统已经恢复。
4.5 挂起_完成
比较简单:

静态无效suspend_finish (无效)

{

暂停解冻进程();

pm_notifier_call_chain ( PM_POST_SUSPEND );

pm_restore_console ();

}

a)恢复所有的用户空间进程和内核线程。
b)发送暂停结束的通知。
c)将控制台切换回原来的。

五、重要知识点回顾

5.1 VT 开关

情况下,过程控制在这个问题(驱动程序\tty\一个问题)中,重新分配一个console,然后在恢复时,通常是旧的console。 VT开关是一种很容易使用的开关功能,因此控制内核提供了不同的功能:
1)提供一个接口功能pm_set_vt_switch(drivers\tty\vt\vt_ioctl.c),方便其他内核模块从整体上关闭或者开启VT switch功能。
2)VT开关切换不同的相关描述开关时,可以参考kernel\power\console.c的条件,即可以进行VT开关
a)有console driver调用pm_vt_switch_required接口,显式要求能VT switch。
b) 系统禁止在suspend的过程中暂停控制台(由kernel/printk.c中的console_suspend_enabled需要有参数控制)。有可能使用consolesus过程,以便使控制台不混乱,需要进行VT切换。
c.sole driver是否需要VT switch或没有任何旧的VT开关或没有任何 VT 开关pm调用功能。

因此,暂停过程对控制台的处理分为4 个步骤:
prepare console:负责在需要VT swich时,将当前的console切换到SUSPEND console。

int pm_prepare_console (无效)

{

如果(!pm_vt_switch ())

返回0 ;

orig_fgconsole = vt_move_to_console ( SUSPEND_CONSOLE , 1 );

如果( orig_fgconsole < 0 )

返回1 ;

orig_kmsg = vt_kmsg_redirect ( SUSPEND_CONSOLE );

返回0 ;

}

suspend console:挂起console,由kernel/printk.c实现,主要是hold住console用的互斥锁,使别人无法使用console。
resume console:对console解锁。
restore console:将console恢复为最初的console。

无效pm_restore_console (无效)

{

如果(!pm_vt_switch ())

返回;

如果(orig_fgconsole >= 0 ){

vt_move_to_console ( orig_fgconsole , 0 );

vt_kmsg_redirect ( orig_kmsg );

}

}

留着,您会问,为什么要切换 VT?先留着这个问题吧,等到分析时再回答。

5.2 冻结任务

进程的冻结功能,是suspend、hibernate等电源管理功能的组成部分,在新版本作为核心中,它被独立出来的电源管理状态(freeze)。该功能的目的,是电源管理中的状态切换过程中,确保有关进程和部分内核负责所有用户应该稳定的空间。

5.3 下午通知

PM notifier 是基于内核阻塞通知器消息功能实现的。blocking notifier提供了一种内核内部的消息通知机制,接受者通过notifier注册的,方式注册一个消息函数,关注发送者发出通知器。 ,产生的消息可以通过调用者接受调用,调用者的消息。
那暂停功能为什么使用通知程序呢?原因可能有多种,我举一个例子,这是我们日常开发中可能会遇到的。
由之前的描述,过程中,suspend device发生在进程被freeze,resume device在进程中发生。
1)如果某些设备需要在freeze进程之前暂停怎么办?
2)如果恢复操作者需要执行什么操作,或者设备需要什么时间等待,那么设备要在恢复进程之前,如果不是触发所有进程的恢复,那么是否可以触发所有进程?等待进程的数据执行能力,如何?
再来看suspend_prepare和suspend_finish中的处理:

静态intsuspend_prepare (suspend_state_t状态){ _

错误= pm_notifier_call_chain ( PM_SUSPEND_PREPARE );

如果(错误)

转到完成;

错误= suspend_freeze_processes ();

}

静态无效suspend_finish (无效)

{

暂停解冻进程();

pm_notifier_call_chain ( PM_POST_SUSPEND );

pm_restore_console ();

}

PM原来不是设备模型的框架,是在查看后门那些特殊的驱动程序,执行器可以比较设备的发送,直接PM接收暂停信息,以便自己的暂停操作。特别是恢复时可以在其他进程都工作的时候,只让暂停进程驱动的恢复。
那位读者,可以围观一下下面活生生的
驱动程序\视频\omap2\dss\core.c

5.4 device PM ops和platform PM ops的调用时机

对Linux驱动工程师来说,设备 PM ops就是电源管理(所有的,只要在适当的地方,实现适当的功能)管理和能够实现系统的电源。内核提供的这两个数据结构也很复杂,再回顾一下,如下:

结构dev_pm_ops {

int (* prepare )( struct device * dev );

无效(完整)(结构设备开发);

int (* suspend )( struct device * dev );

int (* resume )( struct device * dev );

int (* freeze )( struct device * dev );

int (* thaw )( struct device * dev );

int (* poweroff )( struct device * dev );

int (* restore )( struct device * dev );

int (* suspend_late )(结构设备* dev );

int (* resume_early )( struct device * dev );

int (* freeze_late )(结构设备* dev );

int (* thaw_early )( struct device * dev );

int (* poweroff_late )( struct device * dev );

int (* restore_early )( struct device * dev );

int (* suspend_noirq )(结构设备* dev );

int (* resume_noirq )(结构设备* dev );

int (* freeze_noirq )(结构设备* dev );

int (* thaw_noirq )(结构设备* dev );

int (* poweroff_noirq )( struct device * dev );

int (* restore_noirq )( struct device * dev );

int (* runtime_suspend )( struct device * dev );

int (* runtime_resume )( struct device * dev );

int (* runtime_idle )( struct device * dev );

};

结构平台_suspend_ops {

int (*有效)( suspend_state_t state );

int (* begin )( suspend_state_t state );

int (*准备)(无效);

int (* prepare_late )( void );

int (* enter )( suspend_state_t state );

无效(*唤醒)(无效);

无效(*完成)(无效);

bool (* suspend_again )( void );

无效(*结束)(无效);

无效(*恢复)(无效);

};

这个内核的注释相当详细,到底应该实现什么错误?但这些我们的应用场景是什么方法? 。可以总结一下在电源状态切换时,除了这个调用时机,从帮助。

5.5 暂停过程的同步和PM wakeup

最重要的事情,如果过程中,有过程的暂停或特殊处理?在内核都不能的内部,这也很好地解决了这个问题。
因此,在同步的时间里,暂停大部分的电视节目,(因为这个问题的主要作用是影响时代的热点)。但新的推出,事件变了,Android用暂停作日常的追踪(这个模块就在接下来的即将结束的过程中完成了?分析,这里就不再继续了。

原作者:wowo 蜗窝科技

更多回帖

发帖
×
20
完善资料,
赚取积分