Re: [patch update] PM: Introduce core framework for run-time PM of I/O devices (rev. 5)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 24 Jun 2009, Rafael J. Wysocki wrote:

> +config PM_RUNTIME
> +	bool "Run-time PM core functionality"
> +	depends on PM
> +	---help---
> +	  Enable functionality allowing I/O devices to be put into energy-saving
> +	  (low power) states at run time (or autosuspended) after a specified
> +	  period of inactivity and woken up in response to a hardware-generated
> +	  wake-up event or a driver's request.
> +
> +	  Hardware support is generally required for this functionality to work
> +	  and the bus type drivers of the buses the devices are on are
> +	  responsibile for the actual handling of the autosuspend requests and

s/ibile/ible/

> @@ -165,6 +168,28 @@ typedef struct pm_message {
>   * It is allowed to unregister devices while the above callbacks are being
>   * executed.  However, it is not allowed to unregister a device from within any
>   * of its own callbacks.
> + *
> + * There also are the following callbacks related to run-time power management
> + * of devices:
> + *
> + * @runtime_suspend: Prepare the device for a condition in which it won't be
> + *	able to communicate with the CPU(s) and RAM due to power management.
> + *	This need not mean that the device should be put into a low power state.
> + *	For example, if the device is behind a link which is about to be turned
> + *	off, the device may remain at full power.  Still, if the device does go

s/Still, if/If/ -- the word "Still" seems a little odd in this context.

> + *	to low power and if device_may_wakeup(dev) is true, remote wake-up
> + *	(i.e. hardware mechanism allowing the device to request a change of its

s/i.e. /i.e., a /

> + *	power state, such as PCI PME) should be enabled for it.
> + *
> + * @runtime_resume: Put the device into the fully active state in response to a
> + *	wake-up event generated by hardware or at a request of software.  If

s/at a request/at the request/

> + *	necessary, put the device into the full power state and restore its
> + *	registers, so that it is fully operational.


> + * RPM_ACTIVE		Device is fully operational, no run-time PM requests are
> + *			pending for it.
> + *
> + * RPM_IDLE		It has been requested that the device be suspended.
> + *			Suspend request has been put into the run-time PM
> + *			workqueue and it's pending execution.
> + *
> + * RPM_SUSPENDING	Device bus type's ->runtime_suspend() callback is being
> + *			executed.
> + *
> + * RPM_SUSPENDED	Device bus type's ->runtime_suspend() callback has
> + *			completed successfully.  The device is regarded as
> + *			suspended.
> + *
> + * RPM_WAKE		It has been requested that the device be woken up.
> + *			Resume request has been put into the run-time PM
> + *			workqueue and it's pending execution.
> + *
> + * RPM_RESUMING		Device bus type's ->runtime_resume() callback is being
> + *			executed.

Remember to add RPM_NOTIFY.


> +/**
> + * __pm_get_child - Increment the counter of unsuspended children of a device.
> + * @dev: Device to handle;
> + */
> +static void __pm_get_child(struct device *dev)
> +{
> +	atomic_inc(&dev->power.child_count);
> +}
> +
> +/**
> + * __pm_put_child - Decrement the counter of unsuspended children of a device.
> + * @dev: Device to handle;
> + */
> +static void __pm_put_child(struct device *dev)
> +{
> +	if (!atomic_add_unless(&dev->power.child_count, -1, 0))
> +		dev_WARN(dev, "Unbalanced counter decrementation");
> +}

I think we don't need this dev_WARN.  It should be straightforward to
verify that the increments and decrements balance correctly, and the
child_count field isn't manipulated by drivers.

In fact, these don't need to be separate routines at all.  Just call
atomic_inc or atomic_dec directly.

> +
> +/**
> + * __pm_runtime_suspend - Run a device bus type's runtime_suspend() callback.
> + * @dev: Device to suspend.
> + * @sync: If unset, the funtion has been called via pm_wq.
> + *
> + * Check if the run-time PM status of the device is appropriate and run the
> + * ->runtime_suspend() callback provided by the device's bus type.  Update the
> + * run-time PM flags in the device object to reflect the current status of the
> + * device.
> + */
> +int __pm_runtime_suspend(struct device *dev, bool sync)
> +{
> +	struct device *parent = NULL;
> +	unsigned long flags;
> +	int error = -EINVAL;

Remove the initializer.

> +
> +	might_sleep();
> +
> +	spin_lock_irqsave(&dev->power.lock, flags);
> +
> + repeat:
> +	if (dev->power.runtime_status == RPM_ERROR) {

Insert:		error = -EINVAL;

> +		goto out;
> +	} else if (dev->power.runtime_status & RPM_SUSPENDED) {

...


> +void pm_runtime_put(struct device *dev)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev->power.lock, flags);
> +
> +	if (!__pm_runtime_put(dev)) {
> +		dev_WARN(dev, "Unbalanced counter decrementation");

"decrementation" isn't a word -- or if it is, it shouldn't be.  :-)  
Just use "decrement".  Similarly in other places.

> +/**
> + * pm_runtime_add - Update run-time PM fields of a device while adding it.
> + * @dev: Device object being added to device hierarchy.
> + */
> +void pm_runtime_add(struct device *dev)
> +{
> +	dev->power.runtime_notify = false;
> +	INIT_DELAYED_WORK(&dev->power.suspend_work, pm_runtime_suspend_work);

Doesn't INIT_DELAYED_WORK belong in pm_runtime_init?
Do we want the bus subsystem to be responsible for doing:

	dev->power.runtime_disabled = false;
	pm_runtime_put(dev);

after calling device_add?  Or should device_add do it?


> Index: linux-2.6/include/linux/pm_runtime.h
> ===================================================================
> --- /dev/null
> +++ linux-2.6/include/linux/pm_runtime.h

> +static inline struct device *suspend_work_to_device(struct work_struct *work)
> +{
> +	struct delayed_work *dw = to_delayed_work(work);
> +	struct dev_pm_info *dpi;
> +
> +	dpi = container_of(dw, struct dev_pm_info, suspend_work);
> +	return container_of(dpi, struct device, power);
> +}

You don't need to iterate container_of like this.  You can do:

	return container_of(dw, struct device, power.suspend_work);

> +
> +static inline struct device *work_to_device(struct work_struct *work)
> +{
> +	struct dev_pm_info *dpi;
> +
> +	dpi = container_of(work, struct dev_pm_info, work);
> +	return container_of(dpi, struct device, power);
> +}

Similarly here.

These two routines aren't used outside of runtime.c.  They should be
moved into that file.  The same goes for pm_children_suspended and
pm_suspend_possible.

> +
> +static inline void __pm_runtime_get(struct device *dev)
> +{
> +	atomic_inc(&dev->power.resume_count);
> +}

Why introduce __pm_runtime_get?  Just make this pm_runtime_get.

> +static inline void pm_runtime_remove(struct device *dev)
> +{
> +	pm_runtime_disable(dev);
> +}

You forgot to decrement the parent's child_count if dev isn't
suspended (and then do a idle_notify on the parent).  Because of this 
additional complexity, don't inline the routine.

> Index: linux-2.6/drivers/base/dd.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/dd.c
> +++ linux-2.6/drivers/base/dd.c
> @@ -23,6 +23,7 @@
>  #include <linux/kthread.h>
>  #include <linux/wait.h>
>  #include <linux/async.h>
> +#include <linux/pm_runtime.h>
>  
>  #include "base.h"
>  #include "power/power.h"
> @@ -202,8 +203,12 @@ int driver_probe_device(struct device_dr
>  	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
>  		 drv->bus->name, __func__, dev_name(dev), drv->name);
>  
> +	pm_runtime_disable(dev);
> +
>  	ret = really_probe(dev, drv);
>  
> +	pm_runtime_enable(dev);
> +

Shouldn't we guarantee that a device isn't probed while it is in a
suspended state?  So this should be

	pm_runtime_get(dev);
	ret = pm_runtime_resume(dev);
	if (ret == 0)
		ret = really_probe(dev, drv);
	pm_runtime_put(dev);	

It might be nice to have a simple combined pm_runtime_get_and_resume
for this sort of situation.


More comments to follow when I get time to review more of the code...

Alan Stern

_______________________________________________
linux-pm mailing list
linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

[Index of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux