linux runtime pm在深入了解的机制

一:runtime机构简介

何为runtime机制?也就是系统在非睡眠状态,设备在空暇时能够进入runtime suspend状态同一时候不依赖系统wake_lock机制。非空暇时运行runtime resume使得设备进入正常工作状态。

主要代码放在Runtime.c (drivers\base\power)中,同一时候附带的Runtime_pm.txt (documentation\power)有具体

说明。要使得设备能够进入runtime_idle与runtime_suspend必须满足device的2个參数usage_count与child_count同一时候为0。

1:操作usage_count參数通常在HOST控制器驱动中,使用辅助runtime函数来完毕。

2:操作child_count通常由子设备来完毕父设备child_count的添加与降低。

child_count能够理解该设备活跃的子设备的个数。

通常由子设备睡后来让父设备进入休眠,依次递归进行。

二:以下重点分析一下rpm_suspend与rpm_resume

static int rpm_suspend(struct device *dev, int rpmflags)

__releases(&dev->power.lock) __acquires(&dev->power.lock)

{

int (*callback)(struct device *);

struct device *parent = NULL;

struct rpm_qos_data qos;

int retval;

trace_rpm_suspend(dev, rpmflags);

repeat:

retval = rpm_check_suspend_allowed(dev);   //在这里检查usage_count与child_count。当非0存在时将使得该函数直接return。

if (retval < 0)

; /* Conditions are wrong. */

/* Synchronous suspends are not allowed in the RPM_RESUMING state. */

else if (dev->power.runtime_status == RPM_RESUMING &&

!(rpmflags & RPM_ASYNC))

retval = -EAGAIN;

if (retval)

goto out;

----------

/* Carry out an asynchronous or a synchronous suspend. */

if (rpmflags & RPM_ASYNC) {   //异步情况,提交一个工作队列后退出

dev->power.request = (rpmflags & RPM_AUTO) ?

RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND;//当该suspend发起者支持auto suspend时rpmflags为RPM_REQ_AUTOSUSPEND,通常auto带有delay
time时延。

if (!dev->power.request_pending) {

dev->power.request_pending = true;

queue_work(pm_wq, &dev->power.work);

}

goto out;

}

--------------

/*

以下是选择回调函数。假设设备是挂在platform以下的,将运行pm_generic_runtime_suspend()后运行device runtime suspend callback

这里的callback也就是我们在驱动里面注冊的idle,suspend,resume callback;

static const struct dev_pm_ops platform_dev_pm_ops = {

.runtime_suspend = pm_generic_runtime_suspend,

.runtime_resume = pm_generic_runtime_resume,

.runtime_idle = pm_generic_runtime_idle,

USE_PLATFORM_PM_SLEEP_OPS

};

int pm_generic_runtime_suspend(struct device *dev)

{

const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

int ret;

ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0;

return ret;

}

*/

if (dev->pm_domain)

callback = dev->pm_domain->ops.runtime_suspend;

else if (dev->type && dev->type->pm)

callback = dev->type->pm->runtime_suspend;

else if (dev->class && dev->class->pm)

callback = dev->class->pm->runtime_suspend;

else if (dev->bus && dev->bus->pm)

callback = dev->bus->pm->runtime_suspend;

else

callback = NULL;

if (!callback && dev->driver && dev->driver->pm)

callback = dev->driver->pm->runtime_suspend;

retval = rpm_callback(callback, dev);//运行回调函数。

if (retval)

goto fail;

no_callback:

__update_runtime_status(dev, RPM_SUSPENDED);

pm_runtime_deactivate_timer(dev);

if (dev->parent) {

parent = dev->parent;

atomic_add_unless(&parent->power.child_count, -1, 0);

}//假设该设备父设备存在,因为上面该设备已经运行到runtime suspend callback了,那么这时将父设备的子设备减一。

----------

/* Maybe the parent is now able to suspend. */

if (parent && !parent->power.ignore_children && !dev->power.irq_safe) {

spin_unlock(&dev->power.lock);

spin_lock(&parent->power.lock);

rpm_idle(parent, RPM_ASYNC);

spin_unlock(&parent->power.lock);

spin_lock(&dev->power.lock);

}//尝试让父设备进入idle。

-------------

}

static int rpm_resume(struct device *dev, int rpmflags)

__releases(&dev->power.lock) __acquires(&dev->power.lock)

{

int (*callback)(struct device *);

struct device *parent = NULL;

int retval = 0;

trace_rpm_resume(dev, rpmflags);

repeat:

if (dev->power.runtime_error)

retval = -EINVAL;

else if (dev->power.disable_depth > 0)//进行条件推断,disable_depth为禁止runtime,在pm_runtime_enable()时会置为0;

retval = -EACCES;

if (retval)

goto out;

--------

if (dev->power.runtime_status == RPM_ACTIVE) {//当前状态为active时直接推出;

retval = 1;

goto out;

}

---------

/* Carry out an asynchronous or a synchronous resume. */

if (rpmflags & RPM_ASYNC) {

dev->power.request = RPM_REQ_RESUME;

if (!dev->power.request_pending) {

dev->power.request_pending = true;

queue_work(pm_wq, &dev->power.work);//异步情况,提交一个工作队列后退出

}

retval = 0;

goto out;

}

if (!parent && dev->parent) {

/*

* Increment the parent‘s usage counter and resume it if

* necessary.  Not needed if dev is irq-safe; then the

* parent is permanently resumed.

*/

parent = dev->parent;

if (dev->power.irq_safe)

goto skip_parent;

spin_unlock(&dev->power.lock);

pm_runtime_get_noresume(parent);//在该设备resume前要先resume父设备。There Increment the parent‘s usage counter;

spin_lock(&parent->power.lock);

/*

* We can resume if the parent‘s runtime PM is disabled or it

* is set to ignore children.

*/

if (!parent->power.disable_depth

&& !parent->power.ignore_children) {

rpm_resume(parent, 0); //resume父设备

if (parent->power.runtime_status != RPM_ACTIVE)

retval = -EBUSY;

}

spin_unlock(&parent->power.lock);

spin_lock(&dev->power.lock);

if (retval)

goto out;

goto repeat;

}

skip_parent:

if (dev->power.no_callbacks)

goto no_callback; /* Assume success. */

dev->power.suspend_time = ktime_set(0, 0);

dev->power.max_time_suspended_ns = -1;

__update_runtime_status(dev, RPM_RESUMING);

/*选择同一时候运行设备resume callback*/

if (dev->pm_domain)

callback = dev->pm_domain->ops.runtime_resume;

else if (dev->type && dev->type->pm)

callback = dev->type->pm->runtime_resume;

else if (dev->class && dev->class->pm)

callback = dev->class->pm->runtime_resume;

else if (dev->bus && dev->bus->pm)

callback = dev->bus->pm->runtime_resume;

else

callback = NULL;

if (!callback && dev->driver && dev->driver->pm)

callback = dev->driver->pm->runtime_resume;

retval = rpm_callback(callback, dev);

if (retval) {

__update_runtime_status(dev, RPM_SUSPENDED);

pm_runtime_cancel_pending(dev);

} else {

no_callback:

__update_runtime_status(dev, RPM_ACTIVE);

if (parent)

atomic_inc(&parent->power.child_count);//该设备唤醒后添加父设备的子设备活跃数 child_count+1;

}

wake_up_all(&dev->power.wait_queue);

if (!retval)

rpm_idle(dev, RPM_ASYNC);

out:

if (parent && !dev->power.irq_safe) {

spin_unlock_irq(&dev->power.lock);

pm_runtime_put(parent);  //减去刚才添加的the parent‘s usage counter;

spin_lock_irq(&dev->power.lock);

}

trace_rpm_return_int(dev, _THIS_IP_, retval);

return retval;

}

RPM机制整体来说就是管理总线结构和主次设备结构的电源,由于体系结构的因素,device套device的情况,最后就会形成一个device大树。runtime这套机制就能够从根到树顶都能管理得到。

通过上面分析我们知道当休眠时,先由子设备睡眠再让父设备休眠,递归进行,由下而上休眠。当唤醒时,是由上而下,递归唤醒。

三:实例分析

1:以msm_sdcc.c为例分析使用runtime辅助函数来实现device runtime功能:

在sdio数据读写完毕后会调用到msmsdcc_disable()。之前会调用到msmsdcc_enable()。

以此来实现runtime功能。

static int msmsdcc_disable(struct mmc_host *mmc)

{

---------

if (host->plat->disable_runtime_pm)

return -ENOTSUPP;

rc = pm_runtime_put_sync(mmc->parent); //使用runtime辅助函数。先将usage_count减一,再让设备进入idle

---------

}

static inline int pm_runtime_put_sync(struct device *dev)

{

return __pm_runtime_idle(dev, RPM_GET_PUT);

}

int pm_generic_runtime_idle(struct device *dev)

{

const struct dev_pm_ops *pm = dev->driver ?

dev->driver->pm : NULL;

if (pm && pm->runtime_idle) {

int ret = pm->runtime_idle(dev);//调用设备idle的callback

if (ret)

return ret;

}

pm_runtime_suspend(dev);//发起runtime suspend动作

return 0;

}

static int msmsdcc_enable(struct mmc_host *mmc)

{

--------

rc = pm_runtime_get_sync(dev);//使用runtime辅助函数,先将usage_count加一,再让设备进入resume

--------

}

static inline int pm_runtime_get_sync(struct device *dev)

{

return __pm_runtime_resume(dev, RPM_GET_PUT);

}

2:分析一段关于usb runtime的log:

<4>[ 22.281829] rpm_suspend name=usb1 usage_count=0 child_count=0  //device name为usb1 父设备名为msm_hsic_host,进入rpm_suspend

<4>[ 22.281860] rpm_suspend name=usb1 parent_name=msm_hsic_host p_usage_count=0 p_child_count=1 //在这里usb还没有进入suspend。故打印出的父设备的活跃子设备为1。

<4>[ 22.281890] rpm_suspend dev parent‘s child_count -1 //msm_hsic_host 的child_count -1;

<4>[ 22.281890] rpm_suspend name=usb1 usage_count=0 child_count=0

<4>[ 22.281921] rpm_suspend name=usb1 parent_name=msm_hsic_host p_usage_count=0 p_child_count=0 //msm_hsic_host 的child_count为0。

<4>[ 22.281951] rpm_suspend try dev parent going to sleep //让父设备进入休眠

<4>[ 22.281951] rpm_suspend name=usb1 usage_count=0 child_count=0

<4>[ 22.281982] rpm_suspend name=usb1 parent_name=msm_hsic_host p_usage_count=0 p_child_count=0

<4>[ 22.282135] rpm_suspend name=msm_hsic_host usage_count=0 child_count=0  //msm_hsic_host进入rpm_suspend,由上面发起

<4>[ 22.282165] rpm_suspend name=msm_hsic_host parent_name=platform p_usage_count=0 p_child_count=6 //msm_hsic_host的父设备还有6个活跃的子设备

<4>[ 22.282165] rpm_suspend dev parent‘s child_count -1  //msm_hsic_host已经休眠,让父设备的活跃的子设备减一;

<4>[ 22.282196] rpm_suspend name=msm_hsic_host usage_count=0 child_count=0

<4>[ 22.282226] rpm_suspend name=msm_hsic_host parent_name=platform p_usage_count=0 p_child_count=5

<4>[ 22.282226] rpm_suspend try dev parent going to sleep //试图让platform进入休眠

<4>[ 22.282257] rpm_suspend name=msm_hsic_host usage_count=0 child_count=0

<4>[ 22.282257] rpm_suspend name=msm_hsic_host parent_name=platform p_usage_count=0 p_child_count=5

<4>[ 22.635589] rpm_resume name=msm_hsic_host usage_count=1 child_count=0  //设备名为msm_hsic_host的进入rpm_resume,usage_count=1是由发起者添加的。

<4>[ 22.635620] rpm_resume name=msm_hsic_host parent_name=platform p_usage_count=0 p_child_count=5

<4>[ 22.635650] rpm_resume add parent usage count

<4>[ 22.635650] rpm_resume name=msm_hsic_host usage_count=1 child_count=0

<4>[ 22.635650] rpm_resume name=msm_hsic_host parent_name=platform p_usage_count=1 p_child_count=5

<4>[ 22.635772] rpm_resume name=msm_hsic_host usage_count=0 child_count=0

<4>[ 22.635772] rpm_resume name=msm_hsic_host parent_name=platform p_usage_count=1 p_child_count=5

<4>[ 22.635803] rpm_resume add parent child_count +1

<4>[ 22.635803] rpm_resume name=msm_hsic_host usage_count=0 child_count=0

<4>[ 22.635833] rpm_resume name=msm_hsic_host parent_name=platform p_usage_count=1 p_child_count=6

<4>[ 22.635833] rpm_resume over name=msm_hsic_host usage_count=0 child_count=0

<4>[ 22.635864] rpm_resume over name=msm_hsic_host parent_name=platform p_usage_count=0 p_child_count=6

<4>[ 22.635864] rpm_resume name=usb1 usage_count=1 child_count=0   //设备为usb1进入rpm_resume

<4>[ 22.635894] rpm_resume name=usb1 parent_name=msm_hsic_host p_usage_count=0 p_child_count=0

<4>[ 22.635894] rpm_resume add parent usage count

<4>[ 22.635894] rpm_resume name=usb1 usage_count=1 child_count=0

<4>[ 22.635925] rpm_resume name=usb1 parent_name=msm_hsic_host p_usage_count=1 p_child_count=0

<4>[ 22.671600] rpm_resume name=usb1 usage_count=1 child_count=0

<4>[ 22.671630] rpm_resume name=usb1 parent_name=msm_hsic_host p_usage_count=1 p_child_count=0

<4>[ 22.671630] rpm_resume add parent child_count +1

<4>[ 22.671661] rpm_resume name=usb1 usage_count=1 child_count=0

<4>[ 22.671661] rpm_resume name=usb1 parent_name=msm_hsic_host p_usage_count=1 p_child_count=1

<4>[ 22.671691] rpm_resume over name=usb1 usage_count=1 child_count=0

<4>[ 22.671691] rpm_resume over name=usb1 parent_name=msm_hsic_host p_usage_count=0 p_child_count=1

上面log充分说明了runtime_suspend是从子设备到父设备,递归休眠的。而runtime_resume则是相反的过程。

深入理解runtime pm机制。将会为调试设备不能进入runtime suspend打下坚实基础,通常linux中通讯总线都会採用runtime
电源管理机制,当I/O非busy时自己主动休眠,也就是通讯没有数据交互的时候的总线自己主动进入suspend。电源管理採用不用则停的方略,以此来降低功耗。

通常AP通过USB来外挂第三方MODEM,肯定会遇见休眠问题。因为usb通信速率能达到480Mbps,而sdio 100Mbps hs uart 4M,非常明显usb通信非常有优势,尤其在4G越来越普及的情况下。如今就是越来越来的AP+MODEM方案通过usb来通信的。

兴许有空再结合数据收发详尽分析usb休眠机制,Linux usb suspend机制。

转载请注明原文地址。谢谢。

版权声明:本文博主原创文章,博客,未经同意不得转载。

时间: 2024-10-13 21:28:51

linux runtime pm在深入了解的机制的相关文章

linux runtime pm机制的深入理解

一:runtime机制说明 何为runtime机制?也就是系统在非睡眠状态,设备在空闲时可以进入runtime suspend状态同时不依赖系统wake_lock机制,非空闲时执行runtime resume使得设备进入正常工作状态. 主要代码放在Runtime.c (drivers\base\power)中,同时附带的Runtime_pm.txt (documentation\power)有详细 说明.要使得设备可以进入runtime_idle与runtime_suspend必须满足devic

Linux Runtime PM介绍

一.Runtime PM引言 1. 背景 (1)display的需求 (2)系统整机动态功耗优化的需求 (3)upstream 2. 解决方案 (1)引入debounce (2)使用统一的workqueue来管理任务 (3)实时地关闭不需要工作的device (4)当device作为parent时,所有的child不工作时,关闭该device (5)引入pm_rutime 3. 性能指标 (1)快速开关屏场景,亮屏速度提升 (2)动态功耗,更为稳定:唤醒而不亮屏的场景,功耗更低 (3)有助于降低

linux驱动程序之电源管理之Run-time PM 详解(4)

Run-time PM. 每个device或者bus都会向run-time PM core注册3个callback struct dev_pm_ops { ... int (*runtime_suspend)(struct device *dev); int (*runtime_resume)(struct device *dev); int (*runtime_idle)(struct device *dev); ... }; 每个device或者bus都会有2个计数器,一个是device的u

一个Linux平台PM功能初探

WatchDog 用户空间节点 /dev/watchdog /sys/bus/platform/devices/zx29_ap_wdt.0 /sys/bus/platform/drivers/zx29_ap_wdt 时钟资源 arch/arm/mach-zx297520v3/include/mach/iomap.h #define ZX_LSP_CRPM_BASE                       (ZX_LSP_BASE) drivers/clk/zte/clk-zx297520v3

windows和linux套接字中的select机制浅析

先来谈谈为什么会出现select函数,也就是select是解决什么问题的? 平常使用的recv函数时阻塞的,也就是如果没有数据可读,recv就会一直阻塞在那里,这是如果有另外一个连接过来,就得一直等待,这样实时性就不是太好. 这个问题的几个解决方法:1. 使用ioctlsocket函数,将recv函数设置成非阻塞的,这样不管套接字上有没有数据都会立刻返回,可以重复调用recv函数,这种方式叫做轮询(polling),但是这样效率很是问题,因为,大多数时间实际上是无数据可读的,花费时间不断反复执行

linux网络编程学习笔记之五 -----并发机制与线程?

进程线程分配方式 简述下常见的进程和线程分配方式:(好吧,我仅仅是举几个样例作为笔记...并发的水太深了,不敢妄谈...) 1.进程线程预分配 简言之,当I/O开销大于计算开销且并发量较大时,为了节省每次都要创建和销毁进程和线程的开销.能够在请求到达前预先进行分配. 2.进程线程延迟分配 预分配节省了处理时的负担,但操作系统管理这些进程线程也会带来一定的开销.由此,有个折中的方法是,当某个处理须要花费较长时间的时候,我们创建一个并发的进程或线程来处理该请求.实现也非常easy,在主线程中定时,定

嵌入式 Linux网络编程(四)——Select机制

嵌入式 Linux网络编程(四)--Select机制 一.select工作机制 poll和select,都是基于内核函数sys_poll实现的,不同在于在linux中select是从BSD Unix系统继承而来,poll则是从SYSTEM V Unix系统继承而来,因此两种方式相差不大.poll函数没有最大文件描述符数量的限制.poll和 select与一样,大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,开销随着文件描述符数量的增加而线性增大. select需要驱动程序的支持,驱动

嵌入式 Linux网络编程(五)——epoll机制

嵌入式 Linux网络编程(五)--epoll机制 一.epoll简介 epoll是在2.6内核中提出的,是select和poll的增强版本.epoll更加灵活,没有描述符限制,使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中. 1.epoll函数 #include <sys/epoll.h> int epoll_create(int size); 创建一个epoll的句柄,size表示监听的文件描述的数量 int epoll_ctl(int epfd,

linux网络编程学习笔记之五 -----并发机制与线程池

进程线程分配方式 简述下常见的进程和线程分配方式:(好吧,我只是举几个例子作为笔记...并发的水太深了,不敢妄谈...) 1.进程线程预分配 简言之,当I/O开销大于计算开销且并发量较大时,为了节省每次都要创建和销毁进程和线程的开销.可以在请求到达前预先进行分配. 2.进程线程延迟分配 预分配节省了处理时的负担,但操作系统管理这些进程线程也会带来一定的开销.由此,有个折中的方法是,当某个处理需要花费较长时间的时候,我们创建一个并发的进程或线程来处理该请求.实现也很简单,在主线程中定时,定时到期,