linux runtime pm在深入了解的机制
一:runtime机构简介
何为runtime机制?也就是系统在非睡眠状态,设备在空暇时能够进入runtime suspend状态同一时候不依赖系统wake_lock机制。非空暇时运行runtime resume使得设备进入正常工作状态。
主要代码放在Runtime.c (drivers\base\power)中,同一时候附带的Runtime_pm.txt (documentation\power)有具体
说明。个參数usage_count与child_count同一时候为。
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机制。
转载请注明原文地址。谢谢。
版权声明:本文博主原创文章,博客,未经同意不得转载。
linux runtime pm在深入了解的机制的更多相关文章
- Linux Runtime PM介绍【转】
转自:http://blog.csdn.net/wlwl0071986/article/details/42677403 一.Runtime PM引言 1. 背景 (1)display的需求 (2)系 ...
- linux驱动程序之电源管理之Run-time PM 详解(4)
Run-time PM. 每个device或者bus都会向run-time PM core注册3个callback struct dev_pm_ops { ... int (*runtime_su ...
- Runtime PM 处理不当导致的 external abort on non-linefetch 案例分享
硬件平台:某ARM SoC 软件平台:Linux 1 Runtime PM 简介 在介绍 Runtime PM 之前,不妨先看看传统的电源管理.传统的电源管理机制,称之为 System PM(Syst ...
- Linux内存管理Swap和Buffer Cache机制
Linux内存管理Swap和Buffer Cache机制 一个完整的Linux系统主要有存储管理,内存管理,文件系统和进程管理等几方面组成,贴出一些以前学习过的一个很好的文章.与大家共享!以下主要说明 ...
- 【深入浅出Linux网络编程】 “基础 -- 事件触发机制”
回顾一下“"开篇 -- 知其然,知其所以然"”中的两段代码,第一段虽然只使用1个线程但却也只能处理一个socket,第二段虽然能处理成百上千个socket但却需要创建同等数量的线程 ...
- iOS运行时编程(Runtime Programming)和Java的反射机制对比
运行时进行编程,类似Java的反射.运行时编程和Java反射的对比如下: 1.相同点 都可以实现的功能:获取类信息.属性设置获取.类的动态加载(NSClassFromString(@“clas ...
- windows和linux套接字中的select机制浅析
先来谈谈为什么会出现select函数,也就是select是解决什么问题的? 平常使用的recv函数时阻塞的,也就是如果没有数据可读,recv就会一直阻塞在那里,这是如果有另外一个连接过来,就得一直等待 ...
- Linux页快速缓存与回写机制分析
參考 <Linux内核设计与实现> ******************************************* 页快速缓存是linux内核实现的一种主要磁盘缓存,它主要用来降低 ...
- linux下数据同步、回写机制分析
一.前言在linux2.6.32之前,linux下数据同步是基于pdflush线程机制来实现的,在linux2.6.32以上的版本,内核彻底删掉了pdflush机制,改为了基于per-bdi线程来实现 ...
随机推荐
- IE7,8,9兼容性处理
在IE7根据假设高度文本框中设置,则光标将不会被中心的方法如以下: 添加属性,如:style="line-height:32px\9";能够 假设一个页面有多个TAB交换的物品.而 ...
- 《深入了解mybatis原则》 MyBatis架构设计和案例研究
MyBatis这是现在很流行ORM框架,这是非常强大.事实上现却比較简单.优雅. 本文主要讲述MyBatis的架构设计思路,而且讨论MyBatis的几个核心部件.然后结合一个select查询实例.深入 ...
- effective c++ 条款7 declare virtual destructor for polymophyc base class
这似乎很明显. 如果base class的destructor不是virtual,当其derived class作为基类使用,析构的时候derived class的数据成员将不会被销毁. 举个例子 我 ...
- c++爱问的面试问题
1.static_cast,dynamic_cast,reinterpret_cast,const_cast四种转换. 2.const行为 3.malloc/free, new/delete差额 4. ...
- SorlNet
Solr学习 上一篇已经讲到了Solr 查询的相关的参数.这里在讲讲C#是如何通过客户端请求和接受solr服务器的数据, 这里推荐使用SolrNet,主要是:SolrNet使用非常方便,而且用户众多, ...
- C++使用对象指针
//定义结构 Box.h: #ifndef BOX_H #define BOX_H struct Box{ double length; double width; double height; do ...
- 学校oj平台上不去
学校oj平台上不去,我的作业咋办啊
- Eclipse4.4设备egit插件提交本地项目代码到远程仓库
一.设备egit 打开Eclipse的Marketplace.在搜索框中输入egit就可以,能够看到Eclipse4.4已经默认安装了egit,当然假设有新版本号的egit公布的话,也能够在下图上点击 ...
- 左右AjaxFileUpload背景返回Json治
项目中用到图片的无刷新上传,因此想到用ajaxUpLoadFile来解决. 第一步,先在上传图片的页面引入你下载到本地的ajaxfileupload.js文件. 文件下载地址:http://downl ...
- AndroidMainifest标签说明2——<activity>
格公式: <activity android:allowTaskReparenting=["true" | "false"] android:always ...