linux autosleep实现

目录

1、linux autosleep实现

1.1 模块功能

1.2 功能属性

1.3 主要函数实现

1.3.1 pm_autosleep_init

1.3.2 queue_up_suspend_work

1.3.2 pm_autosleep_set_state

1.3.4 try_to_suspend

1.4 函数工作时序


1、linux autosleep实现

1.1 模块功能

在Linux kernel中,autosleep是睡眠流程的触发点和入口点,pm core的睡眠流程入口pm_suspend函数就是被autosleep的睡眠工作队列调用而进入睡眠的。该套机制由Rafael J. Wysocki在2012年合入到Kernel主干,从此之后便一直作为Linux kernel低功耗睡眠的触发入口机制存在。在本章节我们会对该套机制做实现上的分析,并借鉴该机制实现我们自己定制化的autosleep机制。

1.2 功能属性

1)特性功能受宏CONFIG_PM_AUTOSLEEP控制,需要打开该特性的话,CONFIG_PM_AUTOSLEEP必须设置为y。

2)相关实现在kernel\power\autosleep.c文件中

3)通过写"mem, disk, standby, freeze"到/sys/power/autosleep可以开启autosleep。

4)通过写"off"到/sys/power/autosleep就可以关闭autosleep。

1.3 主要函数实现

1.3.1 pm_autosleep_init

1)函数说明

函数原型

int __init pm_autosleep_init(void)

描述

1、创建autosleep流程中投票睡眠的睡眠锁autosleep_ws

2、创建睡眠工作队列autosleep_wq

参数

返回值

0

初始化成功

其他

初始化失败

2)函数实现

其中autosleep_ws和autosleep_wq都是定义的全局变量,方便在本文件其他函数中调用。


   
   
  1. int __init pm_autosleep_init(void)
  2. {
  3. autosleep_ws = wakeup_source_register( "autosleep");
  4. if (!autosleep_ws)
  5. return -ENOMEM;
  6. autosleep_wq = alloc_ordered_workqueue( "autosleep", 0);
  7. if (autosleep_wq)
  8. return 0;
  9. wakeup_source_unregister(autosleep_ws);
  10. return -ENOMEM;
  11. }

1.3.2 queue_up_suspend_work

1)函数说明

函数原型

void queue_up_suspend_work(void)

描述

启动autosleep_wq运行

参数

返回值

2)函数实现


   
   
  1. static DECLARE_WORK(suspend_work, try_to_suspend);
  2. void queue_up_suspend_work( void)
  3. {
  4. if (autosleep_state > PM_SUSPEND_ON)
  5. queue_work(autosleep_wq, &suspend_work);
  6. }

函数queue_up_suspend_work被调用后,会触发工作队列运行,进入到对应的work_handler函数try_to_suspend中执行;关于工作队列的实现原理和用法,推荐大家阅读《Linux 内核设计与实现》一书的8.4节中对工作队列的详细介绍,本书中不做过多冗余的说明。

1.3.2 pm_autosleep_set_state

1)函数说明

函数原型

pm_autosleep_set_state

描述

提供给文件节点autosleep使用,在init.rc中往此节点写入mem状态,触发autosleep运行

参数

suspend_state_t state

要写入的suspend状态

返回值

0

成功

其他

失败

2)函数实现


   
   
  1. int pm_autosleep_set_state(suspend_state_t state)
  2. {
  3. #ifndef CONFIG_HIBERNATION
  4. if (state >= PM_SUSPEND_MAX)
  5. return -EINVAL;
  6. #endif
  7. __pm_stay_awake(autosleep_ws);
  8. mutex_lock(&autosleep_lock);
  9. autosleep_state = state;
  10. __pm_relax(autosleep_ws);
  11. if (state > PM_SUSPEND_ON) {
  12. pm_wakep_autosleep_enabled( true);
  13. queue_up_suspend_work();
  14. } else {
  15. pm_wakep_autosleep_enabled( false);
  16. }
  17. mutex_unlock(&autosleep_lock);
  18. return 0;
  19. }


函数中会判断入参state的值,只要大于PM_SUSPEND_ON,就表示开启低功耗相关特性,然后会做2件事,一是更新所有wakesource的autosleep的标记为使能,以便其做相关维测记录;另一件是调用queue_up_suspend_work触发进入睡眠流程。如果不大于PM_SUSPEND_ON,则说明没有使能低功耗特性,同理所有wakesource的autosleep的标记为去使能,结束相关维测信息的记录。关于PM_SUSPEND_ON我们在PM Core章节做过介绍,这里也不再赘述。

1.3.4 try_to_suspend

1)函数说明

函数原型

static void try_to_suspend(struct work_struct *work)

描述

提供给文件工作队列suspend_work的work_handler,当suspend_work被触发运行时,实际执行的函数体为本函数;主要根据当前系统中的持锁状态和autosleep_state来判断是否进入PM Core睡眠主流程。

参数

struct work_struct *work

对应的工作队列,实际实现中并未使用上

返回值

2)函数实现


   
   
  1. static void try_to_suspend(struct work_struct *work)
  2. {
  3. unsigned int initial_count, final_count;
  4. if (! pm_get_wakeup_count(&initial_count, true))
  5. goto out;
  6. mutex_lock(&autosleep_lock);
  7. if (! pm_save_wakeup_count(initial_count) ||
  8. system_state != SYSTEM_RUNNING) {
  9. mutex_unlock(&autosleep_lock);
  10. goto out;
  11. }
  12. if (autosleep_state == PM_SUSPEND_ON) {
  13. mutex_unlock(&autosleep_lock);
  14. return;
  15. }
  16. if (autosleep_state >= PM_SUSPEND_MAX)
  17. hibernate();
  18. else
  19. pm_suspend(autosleep_state);
  20. mutex_unlock(&autosleep_lock);
  21. if (! pm_get_wakeup_count(&final_count, false))
  22. goto out;
  23. /*
  24. * If the wakeup occurred for an unknown reason, wait to prevent the
  25. * system from trying to suspend and waking up in a tight loop.
  26. */
  27. if (final_count == initial_count)
  28. schedule_timeout_uninterruptible(HZ / 2);
  29. out:
  30. queue_up_suspend_work();
  31. }

函数的名字为try_to_suspend,顾名思义,既然是try,就意味着并不是每次执行都能顺利进入到睡眠中并真正睡下去,如果能睡下去还好,睡不下去的话,会重新调度工作队列等待下次执行。

在这里重点说下函数中的两个关键点:

1) initial_count和final_count,initial_count是在函数入口处获取的wakeup events,而final_count是在睡眠流程退出后获取的wakeup events,wakeup events只会在释放锁时才会增加,为什么在函数倒数第4行要再检查一次wakeup events的值呢?这样做其实也是一个优化措施,如果退出低功耗睡眠流程不是因为持锁状态的改变导致的,那么在这个地方可以快速的再次尝试进入睡眠流程,如果不做这个优化的话,那么重新调度下次再次进入睡眠的话耗费的周期可能会比较长。

2)判断进入hibernate()还是进入pm_suspend(autosleep_state),hibernate()是suspend到了nand flash,下次唤醒重新把镜像加载到DDR,在嵌入式系统中通常不会使用此功能,因为这个和掉电重启其实差别不大,而且耗时还会比较久,可能PC或者工作站等对时间没那么敏感的会使用此功能;对于嵌入式设备来讲,基本上都是使用的pm_suspend(autosleep_state)这个分支,即PM Core的实现。

1.4 函数工作时序

 

前提条件:

CONFIG_PM_AUTOSLEEP特性开关打开,使能autosleep功能。

工作步骤:

1)在init.rc中,往autosleep节点写入功耗控制的state,通常是写入mem,格式为:write /sys/power/autosleep mem;也可以在控制台中输入:echo mem > /sys/power/autosleep来触发。2)写文件节点触发调用autosleep文件中的pm_autosleep_set_state函数,该函数进行参数的判断,系统状态的更新以及调用queue_up_suspend_work来触发autosleep的工作队列。

3)queue_up_suspend_work被调用后触发工作队列suspend_work运行。

4)工作队列suspend_work运行进入函数体try_to_suspend执行,该函数会根据持锁条件决策是否进入PM Core睡眠主流程。

5)try_to_suspend通过调用pm_suspend来进入PM Core流程。

6)pm_suspend退出后回到try_to_suspend,try_to_suspend会再次触发任务或者工作队列调度,期待进入下次的睡眠流程。

7)try_to_suspend通过调用queue_up_suspend_work来再次调度autosleep的工作队列suspend_work。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值