linux电源管理-pm core主流程

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

目录

1、模块主要功能介绍

2、主要涉及的数据类型

2.1 suspend_state_t

2.2 struct suspend_stats 

2.3 struct platform_suspend_ops

3、软件处理流程

3.1 suspend流程

3.2 唤醒流程

4、PM Core主要函数实现

4.1 pm_suspend

4.2 enter_state

4.3 suspend_devices_and_enter

4.4 suspend_enter

5、补充说明


1、模块主要功能介绍

       PM Core,SR(suspend/resume)主流程,顾名思义就是把Linux Kernel中各个低功耗模块组合在一起,成为睡眠和唤醒流程的主干道,我们称之为SR主流程。其在内核中特性的开关主要受以下宏控制:

CONFIG_PM、CONFIG_SUSPEND、CONFIG_PM_AUTOSLEEP、CONFIG_FREEZER、CONFIG_PM_SLEEP

相关代码主要在kernel/power/目录下


   
  1. kernel/power/ main .c ----提供用户态接口,以及pm notifier相关接口
  2. kernel/power/suspend .c -- --Suspend功能的主流程
  3. kernel/power/console .c -- --Suspend过程中对控制台的处理逻辑
  4. kernel/power/process .c -- --Suspend过程中对进程的处理逻辑

2、主要涉及的数据类型

2.1 suspend_state_t

       数据类型定义在include/linux/suspend.h中,表示低功耗要进入的状态,在kernel中主要有4种状态,如下


   
  1. typedef int __bitwise suspend_state_t;
  2. #define PM_SUSPEND_ON ((__force suspend_state_t) 0)
  3. #define PM_SUSPEND_TO_IDLE ((__force suspend_state_t) 1)
  4. #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 2)
  5. #define PM_SUSPEND_MEM ((__force suspend_state_t) 3)
  6. #define PM_SUSPEND_MIN PM_SUSPEND_TO_IDLE
  7. #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中,主要记录低功耗流程中的维测数据


   
  1. struct suspend_stats {
  2. int success; ----表示睡眠失败的次数
  3. int fail; ----表示睡眠失败的次数
  4. int failed_freeze;
  5. int failed_prepare;
  6. int failed_suspend;
  7. int failed_suspend_late;
  8. int failed_suspend_noirq;
  9. int failed_resume;
  10. int failed_resume_early;
  11. int failed_resume_noirq;
  12. #define REC_FAILED_NUM 2
  13. int last_failed_dev;
  14. char failed_devs[REC_FAILED_NUM][ 40];
  15. int last_failed_errno;
  16. int errno[REC_FAILED_NUM];
  17. int last_failed_step;
  18. enum suspend_stat_step failed_steps[REC_FAILED_NUM];
  19. };

2.3 struct platform_suspend_ops

 该结构体定义在include/linux/suspend.h中,主要记录和平台相关的低功耗主流程相关回调函数


   
  1. struct platform_suspend_ops {
  2. int (*valid)(suspend_state_t state);
  3. int (*begin)(suspend_state_t state);
  4. int (*prepare)( void);
  5. int (*prepare_late)( void);
  6. int (*enter)(suspend_state_t state);
  7. void (*wake)( void);
  8. void (*finish)( void);
  9. bool (*suspend_again)( void);
  10. void (*end)( void);
  11. void (*recover)( void);
  12. };

 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

函数原型为:


   
  1. int pm_suspend(suspend_state_t state)
  2. {
  3. int error;
  4. if ( state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
  5. return -EINVAL;
  6. pr_info( "suspend entry (%s)\n", mem_sleep_labels[ state]);
  7. error = enter_state( state);
  8. if (error) {
  9. suspend_stats.fail++;
  10. dpm_save_failed_errno(error);
  11. } else {
  12. suspend_stats.success++;
  13. }
  14. pr_info( "suspend exit\n");
  15. return error;
  16. }

       该函数为pm core的主入口函数,供autosleep中的suspend_work执行时调用。函数首先会判断入参状态是否为有效状态,然后会进入到下一层函数enter_state中执行处理。该函数同时也会记录睡眠失败和成功的相关维测记录,以备在定位问题时查看。

4.2 enter_state

函数原型为:


   
  1. static int enter_state(suspend_state_t state)
  2. {
  3. int error;
  4. trace_suspend_resume( TPS( "suspend_enter"), state, true);
  5. if (state == PM_SUSPEND_TO_IDLE) {
  6. #ifdef CONFIG_PM_DEBUG
  7. if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
  8. pr_warn( "Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n");
  9. return -EAGAIN;
  10. }
  11. #endif
  12. } else if (! valid_state(state)) {
  13. return -EINVAL;
  14. }
  15. if (! mutex_trylock(&system_transition_mutex))
  16. return -EBUSY;
  17. if (state == PM_SUSPEND_TO_IDLE)
  18. s2idle_begin();
  19. if (sync_on_suspend_enabled) {
  20. trace_suspend_resume( TPS( "sync_filesystems"), 0, true);
  21. ksys_sync_helper();
  22. trace_suspend_resume( TPS( "sync_filesystems"), 0, false);
  23. }
  24. pm_pr_dbg( "Preparing system for sleep (%s)\n", mem_sleep_labels[state]);
  25. pm_suspend_clear_flags();
  26. error = suspend_prepare(state);
  27. if (error)
  28. goto Unlock;
  29. if ( suspend_test(TEST_FREEZER))
  30. goto Finish;
  31. trace_suspend_resume( TPS( "suspend_enter"), state, false);
  32. pm_pr_dbg( "Suspending system (%s)\n", mem_sleep_labels[state]);
  33. pm_restrict_gfp_mask();
  34. error = suspend_devices_and_enter(state);
  35. pm_restore_gfp_mask();
  36. Finish:
  37. events_check_enabled = false;
  38. pm_pr_dbg( "Finishing wakeup.\n");
  39. suspend_finish();
  40. Unlock:
  41. mutex_unlock(&system_transition_mutex);
  42. return error;
  43. }

主要处理过程:

1)enter_state会调用valid_state来判断当前睡眠的ops是否有赋值来满足睡眠流程调用;


   
  1. static bool valid_state(suspend_state_t state)
  2. {
  3. /*
  4. * PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states need low level
  5. * support and need to be valid to the low level
  6. * implementation, no valid callback implies that none are valid.
  7. */
  8. return suspend_ops && suspend_ops ->valid && suspend_ops -> valid(state);
  9. }

 suspend_ops是通过各个芯片平台调用suspend_set_ops来定制化赋值的,每个平台都有自己的实现,通过suspend_set_ops来赋值给suspend_ops供PM Core调用。


   
  1. void suspend_set_ops( const struct platform_suspend_ops *ops)
  2. {
  3. lock_system_sleep();
  4. suspend_ops = ops;
  5. if ( valid_state( PM_SUSPEND_STANDBY)) {
  6. mem_sleep_states[ PM_SUSPEND_STANDBY] = mem_sleep_labels[ PM_SUSPEND_STANDBY];
  7. pm_states[ PM_SUSPEND_STANDBY] = pm_labels[ PM_SUSPEND_STANDBY];
  8. if (mem_sleep_default == PM_SUSPEND_STANDBY)
  9. mem_sleep_current = PM_SUSPEND_STANDBY;
  10. }
  11. if ( valid_state( PM_SUSPEND_MEM)) {
  12. mem_sleep_states[ PM_SUSPEND_MEM] = mem_sleep_labels[ PM_SUSPEND_MEM];
  13. if (mem_sleep_default >= PM_SUSPEND_MEM)
  14. mem_sleep_current = PM_SUSPEND_MEM;
  15. }
  16. unlock_system_sleep();
  17. }
  18. EXPORT_SYMBOL_GPL(suspend_set_ops);

2)接下来会调用suspend_prepare来执行pm notifier回调,以及suspend_freeze_processes来冻结可以冻结的进程(notifier和进程冻结相关实现后续单独会讲)


   
  1. static int suspend_prepare(suspend_state_t state)
  2. {
  3. int error;
  4. if (!sleep_state_supported(state))
  5. return -EPERM;
  6. pm_prepare_console();
  7. error = pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND);
  8. if ( error)
  9. goto Restore;
  10. trace_suspend_resume(TPS( "freeze_processes"), 0, true);
  11. error = suspend_freeze_processes();
  12. trace_suspend_resume(TPS( "freeze_processes"), 0, false);
  13. if (! error)
  14. return 0;
  15. suspend_stats.failed_freeze++;
  16. dpm_save_failed_step(SUSPEND_FREEZE);
  17. pm_notifier_call_chain(PM_POST_SUSPEND);
  18. Restore:
  19. pm_restore_console();
  20. return error;
  21. }

       都满足条件后,会进入到下一级函数suspend_devices_and_enter中执行更进一步的suspend处理。

4.3 suspend_devices_and_enter

函数原型为:


   
  1. int suspend_devices_and_enter(suspend_state_t state)
  2. {
  3. int error;
  4. bool wakeup = false;
  5. if (!sleep_state_supported(state))
  6. return -ENOSYS;
  7. pm_suspend_target_state = state;
  8. if (state == PM_SUSPEND_TO_IDLE)
  9. pm_set_suspend_no_platform();
  10. error = platform_suspend_begin(state);
  11. if ( error)
  12. goto Close;
  13. suspend_console();
  14. suspend_test_start();
  15. error = dpm_suspend_start(PMSG_SUSPEND);
  16. if ( error) {
  17. pr_err( "Some devices failed to suspend, or early wake event detected\n");
  18. goto Recover_platform;
  19. }
  20. suspend_test_finish( "suspend devices");
  21. if (suspend_test(TEST_DEVICES))
  22. goto Recover_platform;
  23. do {
  24. error = suspend_enter(state, &wakeup);
  25. } while (! error && !wakeup && platform_suspend_again(state));
  26. Resume_devices:
  27. suspend_test_start();
  28. dpm_resume_end(PMSG_RESUME);
  29. suspend_test_finish( "resume devices");
  30. trace_suspend_resume(TPS( "resume_console"), state, true);
  31. resume_console();
  32. trace_suspend_resume(TPS( "resume_console"), state, false);
  33. Close:
  34. platform_resume_end(state);
  35. pm_suspend_target_state = PM_SUSPEND_ON;
  36. return error;
  37. Recover_platform:
  38. platform_recover(state);
  39. goto Resume_devices;
  40. }

主要处理过程:

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

函数原型为:


   
  1. static int suspend_enter(suspend_state_t state, bool *wakeup)
  2. {
  3. int error;
  4. error = platform_suspend_prepare( state);
  5. if (error)
  6. goto Platform_finish;
  7. error = dpm_suspend_late(PMSG_SUSPEND);
  8. if (error) {
  9. pr_err( "late suspend of devices failed\n");
  10. goto Platform_finish;
  11. }
  12. error = platform_suspend_prepare_late( state);
  13. if (error)
  14. goto Devices_early_resume;
  15. error = dpm_suspend_noir q(PMSG_SUSPEND);
  16. if (error) {
  17. pr_err( "noirq suspend of devices failed\n");
  18. goto Platform_early_resume;
  19. }
  20. error = platform_suspend_prepare_noir q(state);
  21. if (error)
  22. goto Platform_wake;
  23. if (suspend_test(TEST_PLATFORM))
  24. goto Platform_wake;
  25. if ( state == PM_SUSPEND_TO_IDLE) {
  26. s2idle_loop();
  27. goto Platform_wake;
  28. }
  29. error = suspend_disable_secondary_cpus();
  30. if (error || suspend_test(TEST_CPUS))
  31. goto Enable_cpus;
  32. arch_suspend_disable_irqs();
  33. BUG_ON(!irqs_disabled());
  34. system_state = SYSTEM_SUSPEND;
  35. error = syscore_suspend();
  36. if (!error) {
  37. *wakeup = pm_wakeup_pending();
  38. if (!(suspend_test(TEST_CORE) || *wakeup)) {
  39. trace_suspend_resume(TPS( "machine_suspend"),
  40. state, true);
  41. error = suspend_ops->enter( state);
  42. trace_suspend_resume(TPS( "machine_suspend"),
  43. state, false);
  44. } else if (*wakeup) {
  45. error = -EBUSY;
  46. }
  47. syscore_resume();
  48. }
  49. system_state = SYSTEM_RUNNING;
  50. arch_suspend_enable_irqs();
  51. BUG_ON(irqs_disabled());
  52. Enable_cpus:
  53. suspend_enable_secondary_cpus();
  54. Platform_wake:
  55. platform_resume_noir q(state);
  56. dpm_resume_noir q(PMSG_RESUME);
  57. Platform_early_resume:
  58. platform_resume_early( state);
  59. Devices_early_resume:
  60. dpm_resume_early(PMSG_RESUME);
  61. Platform_finish:
  62. platform_resume_finish( state);
  63. return error;
  64. }

函数主要功能为:

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后续会有专门章节讲解)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值