Linux PM机制
概述
RPM是Runtime power management的缩写,Linux提供的一套电源管理框架。核心思想是为所有device提供一种相对独立的电源管理方案,将具体的控制策略和控制权力下放到各个驱动, 驱动可以自行决定何时打开或者关闭电源。Linux的suspend/resume机制也可以在合适的时机统一执行设备的休眠和唤醒动作,进而形成系统级电源管理方案。
Android上提出一种电源管理机制Opportunistic suspend。但目前Linux kernel用的比较多的还是RPM。
核心机制
- 为每个设备维护一个引用计数(device->power.usage_count),用于指示该设备的使用状态。
- 需要使用设备时,device driver调用pm_runtime_get(或pm_runtime_get_sync)接口,增加引用计数;
- 不再使用设备时,device driver调用pm_runtime_put(或pm_runtime_put_sync)接口,减少引用计数。
- 每一次put,RPM core都会判断引用计数值。如果为零,表示该设备不再使用(idle)了,则使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_idle回调函数。
- 每一次get, RPM core都会判断引用计数值。如果0->1, 则表示该设备即将被使用,
- runtime_idle的存在,是为了在idle和suspend之间加一个缓冲,避免频繁的suspend/resume操作。因此它的职责是:判断设备是否具备suspend的条件,如果具备,在合适的时机,suspend设备。
1 可以不提供,RPM core会使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_suspend回调函数,suspend设备,同时记录设备的PM状态,
2 可以调用RPM core提供helper函数(pm_runtime_autosuspend_expiration、pm_runtime_autosuspend、pm_request_autosuspend),要求在指定的时间后,suspend设备。
- pm_runtime_autosuspend、pm_request_autosuspend等接口,会起一个timer,并在timer到期后,使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_suspend回调函数,suspend设备,同时记录设备的PM状态。
- 每一次get,RPM core都会判断设备的PM状态,如果不是active,则会使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_resume回调函数,resume设备。
注1 :Runtime PM中的“suspend”,不一定要求设备必须进入低功耗状态,而是要求设备在suspend后,不再处理数据,不再和CPUs、RAM进行任何的交互,直到设备的.runtime_resume被调用。因为此时设备的parent(如bus controller)、CPU是、RAM等,都有可能因为suspend而不再工作,如果设备再有任何动作,都会造成不可预期的异常。下面是“Documentation\power\runtime_pm.rst”中的解释,供大家参考:
* Once the subsystem-level suspend callback (or the driver suspend callback,
if invoked directly) has completed successfully for the given device, the PM
core regards the device as suspended, which need not mean that it has been
put into a low power state. It is supposed to mean, however, that the
device will not process data and will not communicate with the CPU(s) and
RAM until the appropriate resume callback is executed for it. The runtime
PM status of a device after successful execution of the suspend callback is
'suspended'.
* If the suspend callback returns -EBUSY or -EAGAIN, the device's runtime PM
status remains 'active', which means that the device _must_ be fully
operational afterwards.
* If the suspend callback returns an error code different from -EBUSY and
-EAGAIN, the PM core regards this as a fatal error and will refuse to run
the helper functions described in Section 4 for the device until its status
is directly set to either 'active', or 'suspended' (the PM core provides
special helper functions for this purpose).
注2 :回忆一下wakeup events和wakeup lock,Runtime PM和它们在本质上是一样的,都是实时的向PM core报告“我不工作了,可以睡了”、“我要工作了,不能睡(或醒来吧)”。不同的是:wakeup events和RPM的报告者是内核空间drivers,而wakeup lock是用户空间进程;wakeup events和wakelock涉及的睡眠对象是整个系统,包括CPU和所有的devices,而RPM是一个一个独立的device(CPU除外,它由cpu idle模块处理,可看作RPM的特例)。
PM的API汇总
到目前为止,linux kernel的runtime PM接口定义在“include\linux\pm_runtime.h”中,有将近50个接口。实际使用中并非全部会用到。
RPM提供的API位于“include/linux/pm_runtime.h”中,常用的接口如下:
1 extern int __pm_runtime_idle(struct device *dev, int rpmflags);
2 extern int __pm_runtime_suspend(struct device *dev, int rpmflags);
3 extern int __pm_runtime_resume(struct device *dev, int rpmflags);
这三个函数是RPM的idle、put/suspend、get/resume等操作的基础,根据rpmflag,有着不同的操作逻辑。后续很多API,都是基于它们三个。一般不会在设备驱动中直接使用。
1 extern int pm_schedule_suspend(struct device *dev, unsigned int delay);
在指定的时间后(delay,单位是ms),suspend设备。该接口为异步调用,不会更改设备的引用计数,可在driver的.rpm_idle中调用,免去driver自己再启一个timer的烦恼。
1 extern void pm_runtime_enable(**struct **device *dev);
2 extern void pm_runtime_disable(**struct **device *dev);
设备RPM功能的enable/disable,可嵌套调用,会使用一个变量(dev->power.disable_depth)记录disable的深度。只要disable_depth大于零,就意味着RPM功能不可使用,很多的API调用(如suspend/reesume/put/get等)会返回失败。RPM初始化时,会将所有设备的disable_depth置为1,也就是disable状态,driver初始化完毕后,要根据设备的时机状态,调用这两个函数,将RPM状态设置正确。一般在probe函数结尾调用pm_runtime_enable,在remove函数结尾调用pm_runtime_disable禁用probe函数中
1 extern void pm_runtime_allow(struct device *dev);
2 extern void pm_runtime_forbid(struct device *dev);
RPM core通过sysfs(drivers/base/power/sysfs.c),为每个设备提供一个“/sys/devices/.../power/control”文件,通过该文件可让用户空间程序直接访问device的RPM功能。这两个函数用来控制是否开启该功能(默认开启)。
1 extern int **pm_runtime_barrier(**struct **device *dev);**
这名字起的!!!由3.3的描述可知,很多RPM请求都是异步的,这些请求会挂到一个名称为“pm_wq”的工作队列上,这个函数的目的,就是清空这个队列,另外如果有resume请求,同步等待resume完成。好复杂,希望driver永远不要用到它!!
1 extern int pm_generic_runtime_idle(struct device *dev);
2 extern int pm_generic_runtime_suspend(struct device *dev);
3 extern int pm_generic_runtime_resume(struct device *dev);
几个通用的函数,一般给subsystem的RPM driver使用,直接调用devie driver的相应的callback函数。
1 extern void **pm_runtime_no_callbacks(**struct *device dev);
告诉RPM core自己没有回调函数,不用再调用了(或者调用都是成功的),真啰嗦。
1 extern void **pm_runtime_irq_safe(**struct *device dev);
告诉RPM core,如下函数可以在中断上下文调用: pm_runtime_idle() pm_runtime_suspend() pm_runtime_autosuspend() pm_runtime_resume() pm_runtime_get_sync() pm_runtime_put_sync() pm_runtime_put_sync_suspend() pm_runtime_put_sync_autosuspend()
1 static inline int pm_runtime_idle(struct device *dev)
2 static inline int pm_runtime_suspend(struct device *dev)
3 static inline int pm_runtime_resume(struct device *dev)
直接使用同步的方式,尝试idle/suspend/resume设备,如果条件许可,就会执行相应的callback函数。driver尽量不要使用它们。
1 static inline int pm_request_idle(struct device *dev)
2 static inline int pm_request_resume(struct device *dev)
和上面类似,不过调用方式为异步。尽量不要使用它们。
1 static inline int pm_runtime_get(struct device *dev)
2 static inline int pm_runtime_put(struct device *dev)
增加/减少设备的使用计数,并判断是否为0,如果为零,尝试调用设备的idle callback,如果不为零,尝试调用设备的resume callback。这两个接口是RPM的正统接口啊,多多使用!
1 static inline int pm_runtime_get_sync(struct device *dev)
2 static inline int pm_runtime_put_sync(struct device *dev)
3 static inline int pm_runtime_put_sync_suspend(struct device *dev)
和上面类似,只不过为同步调用。另外提供了一个可直接调用suspend的put接口,何必的!
1 static inline int pm_runtime_autosuspend(struct device *dev)
2 static inline int pm_request_autosuspend(struct device *dev)
3 static inline int pm_runtime_put_autosuspend(struct device *dev)
4 static inline int pm_runtime_put_sync_autosuspend(struct device *dev)
autosuspend相关接口。所谓的autosuspend,就是在suspend的基础上,增加一个timer,还是觉得有点啰嗦。不说了。
1 static inline void pm_runtime_use_autosuspend(struct device *dev)
2 static inline void pm_runtime_dont_use_autosuspend(struct device *dev)
3 extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
4 extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev);
控制是否使用autosuspend功能,以及设置/获取autosuspend的超时值。
20220602
本文来自博客园,作者:追随技术,转载请注明原文链接:https://www.cnblogs.com/545235abc/p/16338050.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~