Linux电源管理(3)
CPU热插拔
CPU热插拔功能在linux中已经存在很久了,一般来讲,在用户空间可以通过/sys/devices/system/cpu/cpun/online节点来操作一个CPU的在线与离线。
1 | # echo 0 > /sys/devices/system/cpu/cpu1/online |
在嵌入式系统中,CPU热插拔可以作为一种省电的方式,在需要时再开启更多的CPU。对于芯片公司来说,也针对热插拔功能加入架构上的支持。多核心的处理器通常包含为高性能设计的G核和为低功耗设计的LP核。在内核中,会根据运行负载进行G核与LP核的集群切换。
要实现CPU热插拔通常需要关注如下这几个问题:
- 如何判断自己是什么核?
- G核和LP核集群的切换时机?
- G核何时进行动态热插拔?
判断自己的核
每个核都可以通过调用is_lp_cluster()来判断当前正在执行的CPU是LP还是G处理器:
1 | static inline unsigned int is_lp_cluster(void) |
G核和LP核集群的切换时机
- 从LP核切换到G核: 当前执行于LP集群,CPUFreq驱动判断出LP核需要增加频率到超过高值门限
- 从G核切换到LP核:当前执行于G集群,CPUFreq驱动判断出G核需要降低频率到低于低值门限
- G核的动态插拔1:当执行于G集群,CPUFreq驱动判断出G核需要降低频率到低于低值门限,且最慢的CPUID小于nr_cpu_ids,关闭最慢的CPU。
- G核的动态插拔2:当执行于G集群,CPUFreq驱动判断出某G核需要设置频率大于高值门限,根据负载平衡状态,可以再开一个核或关闭一个核。
挂起到RAM
Linux支持STANDBY、挂起到RAM、挂起到硬盘等形式的待机。一般的嵌入式产品只实现了挂起到RAM(也称s2ram或STR),即将系统状态保存于内存,并将SDRAM置于自刷新状态。而挂起到硬盘(STD)则是把系统状态保存于硬盘,然后关闭整个系统。
在内核中,大致的挂起到RAM的挂起和恢复流程如下所示:
在内核的device_driver结构中,有一个pm成员,它是一个dev_pm_ops结构体指针,其中封装了挂起到RAM和挂起的硬盘所需要的所有回调函数。
1 | struct dev_pm_ops { |
在各个具体设备的驱动实现中,通常会实现相关的suspend及resume接口,并赋值给dev_pm_ops结构体指针,用于对设备的挂起等操作。除此外,linux中,总线驱动上仍然保留着过时的suspend和resume等接口。
在调试过程中,可以使能内核的PM_DEBUG选项,同时在Bootloader传递给内核的bootargs中设置标志no_console_suspend,即可看到内核的相关打印信息。
在将linux移植到一个新的ARM SoC的过程中,最终系统挂起的入口需要由芯片供应商在相应的arch/arm/mach-xxx中实现platform_suspend_ops的成员函数,一般主要实现其中的enter和valid成员,具体可参考相应目录下的pm.c示例。
运行时的PM
dev_pm_ops结构中包含3个以runtime开头的成员: runtime_suspend、runtime_resume和runtime_idle,它们是辅助设备完成运行时的电源管理的。
运行时的PM与前文挂起操作不太一样,它是针对单个设备,指系统在非睡眠状态下的情况下,某个设备在空闲时可以进入运行时挂起状态,而在忙时执行运行时恢复使得设备进入正常工作状态。
我们可以这样理解Linux的运行时PM机制,每个设备都有引用计数usage_count和活跃子设备计数child_count,当两个计数都为0时,就进入空闲状态,调用pm_request_idle(dev)。当设备进入空闲状态,与pm_request_idle(dev)对应的PM核并不一定直接调用设备驱动的runtime_suspend(),它实际上在多数情况下是调用与该设备对应的bus_type的runtime_idle()。
1 | static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset) |
据此可知,bus_type级的回调可以被pm_domain、type、class覆盖掉,这些都统称为子系统。bus_type等子系统级别的runtime_idle行为完全由相应的总线类型、设备分类和pm_domain因素决定,但一般的行为是子系统级别的runtime_idle()会调度设备驱动的runtime_suspend()。
在具体设备驱动中,一般的用法则是在设备驱动的probe()时运行pm_runtime_enable()使能运行时PM支持,在运行过程中动态执行”pm_runtime_get_xxx()->做工作->pm_runtime_put_xxx()”的序列。可参考drivers/watchdog/omap_wdt.c中运行时PM的实现。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yxhlfx@163.com
文章标题:Linux电源管理(3)
本文作者:红尘追风
发布时间:2017-01-28, 18:51:30
原始链接:http://www.micernel.com/2017/01/28/Linux%E7%94%B5%E6%BA%90%E7%AE%A1%E7%90%86(3)/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。