zoukankan      html  css  js  c++  java
  • linux runtime pm在深入了解的机制

      一:runtime机构简介

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

        主要代码放在Runtime.c (driversasepower)中,同一时候附带的Runtime_pm.txt (documentationpower)有具体

    说明。要使得设备能够进入runtime_idleruntime_suspend必须满足device2个參数usage_countchild_count同一时候为0

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

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

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

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

        

    二:以下重点分析一下rpm_suspendrpm_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_countchild_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机制整体来说就是管理总线结构和主次设备结构的电源,由于体系结构的因素,devicedevice的情况,最后就会形成一个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);//调用设备idlecallback

    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 runtimelog

    <4>[ 22.281829] rpm_suspend name=usb1 usage_count=0 child_count=0  //device nameusb1 父设备名为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_count0

    <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机制。

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

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

  • 相关阅读:
    网络爬虫之Cookies解决
    高性能异步爬虫
    Python csv存储
    pandas ExcelWriter用法及代码示例
    pandas to_excel 用法详解
    pandas read_csv 与 to_csv 方法各参数详解
    pandas read_excel操作
    pandas DataFrame.groupby()所见的各种用法详解
    Pandas 中 DataFrame 基本函数整理
    Python 解析二维码 输出文本
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4904096.html
Copyright © 2011-2022 走看看