Linux电源管理9

Linux电源管理9(基于Linux6.6)---Runtime PM介绍


一、概述

运行时电源管理(Runtime Power Management,简称 Runtime PM)是 Linux 内核中一种用于动态管理硬件设备功耗的机制。它通过在不使用时自动调整硬件设备的电源状态,来提高系统的能效,从而延长电池寿命,降低能源消耗。尤其在嵌入式系统和移动设备(如智能手机、笔记本电脑等)中,运行时电源管理尤为重要。

1.1、基本概念

在 Linux 中,设备的电源管理通常由两部分组成:

  • 系统级电源管理(System-wide PM):涉及整个系统的电源管理,通常是通过系统挂起(suspend)或休眠(hibernate)来管理。
  • 设备级电源管理(Device-level PM):针对单个设备的电源管理。运行时电源管理正是设备级电源管理的一部分,目的是在设备不活跃时将其电源状态调至最低,以减少不必要的能源消耗。

1.2、设备电源状态

设备在运行时可以处于不同的电源状态,这些状态通常分为以下几类:

  • D0(Fully Powered):设备处于全功率状态,正在正常工作。
  • D1 和 D2(Partial Power):设备处于部分工作状态,能耗较低,但仍然可以快速恢复。
  • D3(Off):设备处于关闭状态,能耗最低,设备不再活动,恢复可能需要较长时间。

Linux 内核会通过调用相应的电源管理接口来管理设备的电源状态,在设备不需要工作时,将其置于低功耗模式,减少能耗。

1.3、运行时电源管理的工作原理

运行时电源管理的核心思想是“按需激活”。具体而言,当设备处于空闲状态且没有任何任务时,内核会主动将其置于较低的功耗状态,而在需要时再将其激活。这个过程是透明的,对于应用程序和用户而言,设备的电源管理是自动的,无需手动干预。

工作流程

  1. 设备空闲检测:Linux 内核会根据设备的使用情况来判断设备是否处于空闲状态。如果设备处于空闲且不再被访问的状态,内核会考虑将设备进入低功耗模式。

  2. 设备电源状态切换:内核通过设备的电源管理接口(如 pm_runtime_* API)来控制设备电源状态的切换。具体来说,内核会请求设备驱动将设备电源状态从 D0 转到 D1、D2 或 D3 状态。

  3. 恢复设备:当系统需要重新访问该设备时,内核会将设备从低功耗模式恢复到正常工作状态。这一过程通常是快速的,尤其对于 D1 和 D2 状态。

  4. 自动管理:内核通过 pm_runtime 框架来自动管理设备的电源状态。设备驱动程序会根据设备的使用情况通知内核是否需要进入低功耗模式。

二、Runtime PM的软件框架

从设计思路上讲,它确实简单。下面是一个大概的软件框架:

device driver(或者driver所在的bus、class等)需要提供3个回调函数,runtime_suspend、runtime_resume和runtime_idle,分别用于suspend device、resume device和idle device。它们一般由RPM core在合适的时机调用,以便降低device的power consumption。

而调用的时机,最终是由device driver决定的。driver会在适当的操作点,调用RPM core提供的put和get系列的helper function,汇报device的当前状态。RPM core会为每个device维护一个引用计数,get时增加计数值,put时减少计数值,当计数为0时,表明device不再被使用,可以立即或一段时间后suspend,以节省功耗。

 

三、Runtime PM的运行机制

3.1、核心机制

RPM的核心机制是这样的:

1)为每个设备维护一个引用计数(device->power.usage_count),用于指示该设备的使用状态。

2)需要使用设备时,device driver调用pm_runtime_get(或pm_runtime_get_sync)接口,增加引用计数;不再使用设备时,device driver调用pm_runtime_put(或pm_runtime_put_sync)接口,减少引用计数。

3)每一次put,RPM core都会判断引用计数的值。如果为零,表示该设备不再使用(idle)了,则使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_idle回调函数。

4)runtime_idle的存在,是为了在idle和suspend之间加一个缓冲,避免频繁的suspend/resume操作。因此它的职责是:判断设备是否具备suspend的条件,如果具备,在合适的时机,suspend设备。

5)pm_runtime_autosuspend、pm_request_autosuspend等接口,会起一个timer,并在timer到期后,使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_suspend回调函数,suspend设备,同时记录设备的PM状态。

6)每一次get,RPM core都会判断设备的PM状态,如果不是active,则会使用异步(ASYNC)或同步(SYNC)的方式,调用设备的.runtime_resume回调函数,resume设备。

 

3.2、get和put的时机

device idle的判断标准是什么?

对“Opportunistic suspend”而言,suspend时机的判断是相当困难的,因为整机的运行环境比较复杂。而每一个具体设备的idle,就容易多了,这就是Runtime PM的优势。回到这个话题上,对device而言,什么是idle?

device是通过用户程序为用户提供服务的,而服务的方式分为两种:接受指令,做事情(被动);上报事件(主动,一般通过中断的方式)。因此,设备active的时间段,包括【接受指令,完成指令】和【事件到达,由driver记录下来】两个。除此之外的时间,包括driver从用户程序获得指令(以及相关的数据)、driver将事件(以及相关的数据)交给应用程序,都是idle时间。

那idle时间是否应立即suspend以节省功耗?不一定,要具体场景具体对待:例如网络传输,如果网络连接正常,那么在可预期的、很短的时间内,设备又会active(传输网络数据),如果频繁suspend,会降低性能,且不会省电;比如用户按键,具有突发性,因而可以考虑suspend;等等。

由于get和put正是设备idle状态的切换点,因此get和put的时机就容易把握了:

1)主动访问设备时,如写寄存器、发起数据传输等等,get,增加引用计数,告诉RPM core设备active;访问结束后,put,减小引用计数,告诉RPM core设备可能idle。

2)设备有事件通知时,get(可能在中断处理中);driver处理完事件后,put。

3.3、异步(ASYNC)和同步(SYNC)

设备驱动代码可在进程和中断两种上下文执行,因此put和get等接口,要么是由用户进程调用,要么是由中断处理函数调用。由于这些接口可能会执行device的.runtime_xxx回调函数,而这些接口的执行时间是不确定的,有些可能还会睡眠等待。这对用户进程或者中断处理函数来说,是不能接受的。

因此,RPM core提供的默认接口(pm_runtime_get/pm_runtime_put等),采用异步调用的方式(由ASYNC flag表示),启动一个work queue,在单独的线程中,调用.runtime_xxx回调函数,这可以保证设备驱动之外的其它模块正常运行。

另外,如果设备驱动清楚地知道自己要做什么,也可以使用同步接口(pm_runtime_get_sync/pm_runtime_put_sync等),它们会直接调用.runtime_xxx回调函数,不过,后果自负!

3.4 Runtime PM过程中的同步问题

由于.runtime_xxx回调函数可能采用异步的形式调用,以及Generic PM suspend和RPM并存的现状,要求RPM要小心处理同步问题,包括:

多个.runtime_suspend请求之间的同步;
多个.runtime_resume请求之间的同步;
多个.runtime_idle请求之间的同步;
.runtime_suspend请求和.runtime_resume请求之间的同步;
.runtime_suspend请求和system suspend之间的同步;
.runtime_resume请求和system resume之间的同步;
等等。

由此可知,RPM core将会有相当一部分代码,用来处理同步问题。

3.5、级联设备之间的Runtime PM

struct device结构中,有一个parent指针,指向该设备的父设备(没有的话为空)。父设备通常是Bus、host controller,设备的正常工作,依赖父设备。体现在RPM中,就是如下的行为:

1)parent设备下任何一个设备处于active状态,parent必须active。

2)parent设备下任何一个设备idle了,要通知parent,parent以此记录处于active状态的child设备个数。

3)parent设备下所有子设备都idle了,parent才可以idle。

以上行为RPM core会自动处理,不需要驱动工程师太过操心。

3.6 device的runtime status及其初始状态

在Runtime Power Management的过程中,device可处于四种状态:RPM_ACTIVE、RPM_RESUMING、RPM_SUSPENDED和RPM_SUSPENDING。

RPM_ACTIVE,设备处于正常工作的状态,表示设备的.runtime_resume回调函数执行成功;

RPM_SUSPENDED,设备处于suspend状态,表示设备.runtime_suspend回调函数执行成功;

RPM_RESUMING,设备的.runtime_resume正在被执行;

RPM_SUSPENDING,设备的.runtime_suspend正在被执行。

注4:前面说过,.runtime_idle只是suspend前的过渡,因此runtime status和idle无关。

device注册时,设备模型代码会调用pm_runtime_init接口,将设备的runtime status初始化为RPM_SUSPENDED,而kernel并不知道某个设备初始化时的真正状态,因此设备驱动需要根据实际情况,调用RPM的helper函数,将自身的status设置正确。

四、runtime PM的API汇整

Runtime Power Management (Runtime PM) 的 API 允许设备驱动程序动态管理设备的电源状态。以下是一些常用的 Runtime PM API,用于设备电源管理,包括如何启用、禁用、获取、释放、空闲等操作。具体 API 的汇整如下:

4.1、启用与禁用 Runtime PM

pm_runtime_enable()

用于启用设备的运行时电源管理。调用此函数会使设备驱动支持运行时电源管理,并注册相关的回调。

void pm_runtime_enable(struct device *dev);
  • 参数dev - 目标设备。
  • 说明:启用设备的运行时电源管理,启用后,系统会在适当时机自动切换设备的电源状态。

pm_runtime_disable()

用于禁用设备的运行时电源管理。如果禁用后,设备将不会再进行自动的电源管理。

void pm_runtime_disable(struct device *dev);
  • 参数dev - 目标设备。
  • 说明:禁用设备的运行时电源管理,设备会恢复为默认的电源管理模式。

4.2、获取与释放设备电源

pm_runtime_get()

请求设备进入工作状态(D0)。如果设备已经在工作状态,则该调用会更新设备的状态。

int pm_runtime_get(struct device *dev);
  • 参数dev - 目标设备。
  • 返回值:返回 0 表示成功,负值表示失败。
  • 说明:请求设备恢复到工作状态。如果设备处于低功耗模式,调用该函数将恢复设备的工作状态。

pm_runtime_put()

请求设备进入低功耗状态(D1/D2/D3)。调用此函数会将设备的电源状态降至低功耗模式。

int pm_runtime_put(struct device *dev);
  • 参数dev - 目标设备。
  • 返回值:返回 0 表示成功,负值表示失败。
  • 说明:请求设备进入低功耗状态,设备不再使用时将被置于低功耗模式。

pm_runtime_put_sync()

pm_runtime_put() 类似,但此函数会等待设备真正进入低功耗模式后才返回。

int pm_runtime_put_sync(struct device *dev);
  • 参数dev - 目标设备。
  • 返回值:返回 0 表示成功,负值表示失败。
  • 说明:请求设备进入低功耗状态并等待设备完成切换。

pm_runtime_get_sync()

pm_runtime_get() 类似,但此函数会等待设备恢复到工作状态后才返回。

int pm_runtime_get_sync(struct device *dev);
  • 参数dev - 目标设备。
  • 返回值:返回 0 表示成功,负值表示失败。
  • 说明:请求设备恢复到工作状态并等待设备完成恢复。

4.3、设备空闲与空闲状态管理

pm_runtime_idle()

标记设备处于空闲状态,内核可以将其电源状态切换到较低功耗模式。

int pm_runtime_idle(struct device *dev);
  • 参数dev - 目标设备。
  • 返回值:返回 0 表示成功,负值表示失败。
  • 说明:标记设备为空闲状态,允许内核根据设备的使用情况将其置于低功耗模式。

pm_runtime_suspend()

请求设备进入低功耗模式,通常在设备处于空闲状态时调用。

int pm_runtime_suspend(struct device *dev);
  • 参数dev - 目标设备。
  • 返回值:返回 0 表示成功,负值表示失败。
  • 说明:请求设备进入挂起状态(通常为 D1/D2/D3 状态)。

pm_runtime_resume()

恢复设备到工作状态(D0)。

int pm_runtime_resume(struct device *dev);
  • 参数dev - 目标设备。
  • 返回值:返回 0 表示成功,负值表示失败。
  • 说明:恢复设备的工作状态。

4.4、自动电源管理

pm_runtime_set_active()

用于显式地将设备设置为活动状态(D0),通常在设备准备使用之前调用。

void pm_runtime_set_active(struct device *dev);
  • 参数dev - 目标设备。
  • 说明:显式将设备设置为活动状态。

pm_runtime_no_callbacks()

禁用设备电源管理的回调函数,通常用于驱动层面进行特殊的电源管理操作。

void pm_runtime_no_callbacks(struct device *dev);
  • 参数dev - 目标设备。
  • 说明:禁用设备的回调,防止运行时电源管理的操作影响设备状态。

4.5、其他相关函数

pm_runtime_mark_last_busy()

标记设备为“最后一次忙碌”,通常用于设备进入低功耗状态之前,表示设备刚刚被使用过。c

void pm_runtime_mark_last_busy(struct device *dev);
  • 参数dev - 目标设备。
  • 说明:标记设备为最后一次忙碌的状态,有助于避免过早的电源状态转换。

pm_runtime_use_autosuspend()

启用自动挂起模式,设备将在空闲一定时间后自动进入低功耗状态。

void pm_runtime_use_autosuspend(struct device *dev);
  • 参数dev - 目标设备。
  • 说明:启用设备的自动挂起功能。

pm_runtime_put_noidle()

标记设备不需要进入空闲模式,避免不必要的挂起。

void pm_runtime_put_noidle(struct device *dev);
  • 参数dev - 目标设备。
  • 说明:标记设备不再空闲,避免系统自动挂起。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值