[PATCH 1/3] PM / Runtime: Add runtime PM statistics (v3)

Amit Kucheria amit.kucheria at linaro.org
Thu Sep 9 11:30:47 UTC 2010


Yong,

You forgot to add a note as to why you need these patches applied :)

Reviewers - these patches expose some new information related to
runtime PM that powertop can show.

This patch -1/3, is already in 2.6.36-rc. The other two aren't upstream.

Please consider applying to the Ubuntu kernel.

Regards,
Amit

On Thu, Sep 9, 2010 at 2:20 PM,  <yong.shen at linaro.org> wrote:
> From: Arjan van de Ven <arjan at linux.intel.com>
>
> In order for PowerTOP to be able to report how well the new runtime PM is
> working for the various drivers, the kernel needs to export some basic
> statistics in sysfs.
>
> This patch adds two sysfs files in the runtime PM domain that expose the
> total time a device has been active, and the time a device has been
> suspended.
>
> With this PowerTOP can compute the activity percentage
>
> Active %age = 100 * (delta active) / (delta active + delta suspended)
>
> and present the information to the user.
>
> I've written the PowerTOP code (slated for version 1.12) already, and the
> output looks like this:
>
> Runtime Device Power Management statistics
> Active  Device name
>  10.0%  06:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8101E/RTL8102E PCI Express Fast Ethernet controller
>
> [version 2: fix stat update bugs noticed by Alan Stern]
> [version 3: rebase to -next and move the sysfs declaration]
>
> Signed-off-by: Arjan van de Ven <arjan at linux.intel.com>
> Signed-off-by: Rafael J. Wysocki <rjw at sisk.pl>
> ---
>  drivers/base/power/runtime.c |   54 ++++++++++++++++++++++++++++++----
>  drivers/base/power/sysfs.c   |   65 +++++++++++++++++++++++++++++++++++++++++-
>  include/linux/pm.h           |    6 ++++
>  3 files changed, 117 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
> index b0ec0e9..b78c401 100644
> --- a/drivers/base/power/runtime.c
> +++ b/drivers/base/power/runtime.c
> @@ -123,6 +123,45 @@ int pm_runtime_idle(struct device *dev)
>  }
>  EXPORT_SYMBOL_GPL(pm_runtime_idle);
>
> +
> +/**
> + * update_pm_runtime_accounting - Update the time accounting of power states
> + * @dev: Device to update the accounting for
> + *
> + * In order to be able to have time accounting of the various power states
> + * (as used by programs such as PowerTOP to show the effectiveness of runtime
> + * PM), we need to track the time spent in each state.
> + * update_pm_runtime_accounting must be called each time before the
> + * runtime_status field is updated, to account the time in the old state
> + * correctly.
> + */
> +void update_pm_runtime_accounting(struct device *dev)
> +{
> +       unsigned long now = jiffies;
> +       int delta;
> +
> +       delta = now - dev->power.accounting_timestamp;
> +
> +       if (delta < 0)
> +               delta = 0;
> +
> +       dev->power.accounting_timestamp = now;
> +
> +       if (dev->power.disable_depth > 0)
> +               return;
> +
> +       if (dev->power.runtime_status == RPM_SUSPENDED)
> +               dev->power.suspended_jiffies += delta;
> +       else
> +               dev->power.active_jiffies += delta;
> +}
> +
> +static void __update_runtime_status(struct device *dev, enum rpm_status status)
> +{
> +       update_pm_runtime_accounting(dev);
> +       dev->power.runtime_status = status;
> +}
> +
>  /**
>  * __pm_runtime_suspend - Carry out run-time suspend of given device.
>  * @dev: Device to suspend.
> @@ -197,7 +236,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq)
>                goto repeat;
>        }
>
> -       dev->power.runtime_status = RPM_SUSPENDING;
> +       __update_runtime_status(dev, RPM_SUSPENDING);
>        dev->power.deferred_resume = false;
>
>        if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
> @@ -228,7 +267,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq)
>        }
>
>        if (retval) {
> -               dev->power.runtime_status = RPM_ACTIVE;
> +               __update_runtime_status(dev, RPM_ACTIVE);
>                if (retval == -EAGAIN || retval == -EBUSY) {
>                        if (dev->power.timer_expires == 0)
>                                notify = true;
> @@ -237,7 +276,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq)
>                        pm_runtime_cancel_pending(dev);
>                }
>        } else {
> -               dev->power.runtime_status = RPM_SUSPENDED;
> +               __update_runtime_status(dev, RPM_SUSPENDED);
>                pm_runtime_deactivate_timer(dev);
>
>                if (dev->parent) {
> @@ -381,7 +420,7 @@ int __pm_runtime_resume(struct device *dev, bool from_wq)
>                goto repeat;
>        }
>
> -       dev->power.runtime_status = RPM_RESUMING;
> +       __update_runtime_status(dev, RPM_RESUMING);
>
>        if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) {
>                spin_unlock_irq(&dev->power.lock);
> @@ -411,10 +450,10 @@ int __pm_runtime_resume(struct device *dev, bool from_wq)
>        }
>
>        if (retval) {
> -               dev->power.runtime_status = RPM_SUSPENDED;
> +               __update_runtime_status(dev, RPM_SUSPENDED);
>                pm_runtime_cancel_pending(dev);
>        } else {
> -               dev->power.runtime_status = RPM_ACTIVE;
> +               __update_runtime_status(dev, RPM_ACTIVE);
>                if (parent)
>                        atomic_inc(&parent->power.child_count);
>        }
> @@ -848,7 +887,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
>        }
>
>  out_set:
> -       dev->power.runtime_status = status;
> +       __update_runtime_status(dev, status);
>        dev->power.runtime_error = 0;
>  out:
>        spin_unlock_irqrestore(&dev->power.lock, flags);
> @@ -1077,6 +1116,7 @@ void pm_runtime_init(struct device *dev)
>        dev->power.request_pending = false;
>        dev->power.request = RPM_REQ_NONE;
>        dev->power.deferred_resume = false;
> +       dev->power.accounting_timestamp = jiffies;
>        INIT_WORK(&dev->power.work, pm_runtime_work);
>
>        dev->power.timer_expires = 0;
> diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
> index a4c33bc..7ee064f 100644
> --- a/drivers/base/power/sysfs.c
> +++ b/drivers/base/power/sysfs.c
> @@ -6,6 +6,7 @@
>  #include <linux/string.h>
>  #include <linux/pm_runtime.h>
>  #include <asm/atomic.h>
> +#include <linux/jiffies.h>
>  #include "power.h"
>
>  /*
> @@ -108,6 +109,66 @@ static ssize_t control_store(struct device * dev, struct device_attribute *attr,
>  }
>
>  static DEVICE_ATTR(control, 0644, control_show, control_store);
> +
> +static ssize_t rtpm_active_time_show(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int ret;
> +       spin_lock_irq(&dev->power.lock);
> +       update_pm_runtime_accounting(dev);
> +       ret = sprintf(buf, "%i\n", jiffies_to_msecs(dev->power.active_jiffies));
> +       spin_unlock_irq(&dev->power.lock);
> +       return ret;
> +}
> +
> +static DEVICE_ATTR(runtime_active_time, 0444, rtpm_active_time_show, NULL);
> +
> +static ssize_t rtpm_suspended_time_show(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int ret;
> +       spin_lock_irq(&dev->power.lock);
> +       update_pm_runtime_accounting(dev);
> +       ret = sprintf(buf, "%i\n",
> +               jiffies_to_msecs(dev->power.suspended_jiffies));
> +       spin_unlock_irq(&dev->power.lock);
> +       return ret;
> +}
> +
> +static DEVICE_ATTR(runtime_suspended_time, 0444, rtpm_suspended_time_show, NULL);
> +
> +static ssize_t rtpm_status_show(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       const char *p;
> +
> +       if (dev->power.runtime_error) {
> +               p = "error\n";
> +       } else if (dev->power.disable_depth) {
> +               p = "unsupported\n";
> +       } else {
> +               switch (dev->power.runtime_status) {
> +               case RPM_SUSPENDED:
> +                       p = "suspended\n";
> +                       break;
> +               case RPM_SUSPENDING:
> +                       p = "suspending\n";
> +                       break;
> +               case RPM_RESUMING:
> +                       p = "resuming\n";
> +                       break;
> +               case RPM_ACTIVE:
> +                       p = "active\n";
> +                       break;
> +               default:
> +                       return -EIO;
> +               }
> +       }
> +       return sprintf(buf, p);
> +}
> +
> +static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL);
> +>>>>>>> 8d4b9d1... PM / Runtime: Add runtime PM statistics (v3):drivers/base/power/sysfs.c
>  #endif
>
>  static ssize_t
> @@ -228,6 +289,9 @@ static DEVICE_ATTR(async, 0644, async_show, async_store);
>  static struct attribute * power_attrs[] = {
>  #ifdef CONFIG_PM_RUNTIME
>        &dev_attr_control.attr,
> +       &dev_attr_runtime_status.attr,
> +       &dev_attr_runtime_suspended_time.attr,
> +       &dev_attr_runtime_active_time.attr,
>  #endif
>        &dev_attr_wakeup.attr,
>  #ifdef CONFIG_PM_ADVANCED_DEBUG
> @@ -235,7 +299,6 @@ static struct attribute * power_attrs[] = {
>  #ifdef CONFIG_PM_RUNTIME
>        &dev_attr_runtime_usage.attr,
>        &dev_attr_runtime_active_kids.attr,
> -       &dev_attr_runtime_status.attr,
>        &dev_attr_runtime_enabled.attr,
>  #endif
>  #endif
> diff --git a/include/linux/pm.h b/include/linux/pm.h
> index 8e258c7..dca597f 100644
> --- a/include/linux/pm.h
> +++ b/include/linux/pm.h
> @@ -476,9 +476,15 @@ struct dev_pm_info {
>        enum rpm_request        request;
>        enum rpm_status         runtime_status;
>        int                     runtime_error;
> +       unsigned long           active_jiffies;
> +       unsigned long           suspended_jiffies;
> +       unsigned long           accounting_timestamp;
>  #endif
>  };
>
> +extern void update_pm_runtime_accounting(struct device *dev);
> +
> +
>  /*
>  * The PM_EVENT_ messages are also used by drivers implementing the legacy
>  * suspend framework, based on the ->suspend() and ->resume() callbacks common
> --
> 1.6.3.3
>
>




More information about the kernel-team mailing list