| From ef6791f347cd72ae5354a827004f57bdb4b6bbd7 Mon Sep 17 00:00:00 2001 |
| From: "Rafael J. Wysocki" <rjw@sisk.pl> |
| Date: Tue, 27 Sep 2011 21:54:52 +0200 |
| Subject: PM / Runtime: Don't run callbacks under lock for power.irq_safe set |
| |
| The rpm_suspend() and rpm_resume() routines execute subsystem or PM |
| domain callbacks under power.lock if power.irq_safe is set for the |
| given device. This is inconsistent with that rpm_idle() does after |
| commit 02b2677 (PM / Runtime: Allow _put_sync() from |
| interrupts-disabled context) and is problematic for subsystems and PM |
| domains wanting to use power.lock for synchronization in their |
| runtime PM callbacks. |
| |
| This change requires the code checking if the device's runtime PM |
| status is RPM_SUSPENDING or RPM_RESUMING to be modified too, to take |
| the power.irq_safe set case into account (that code wasn't reachable |
| before with power.irq_safe set, because it's executed with the |
| device's power.lock held). |
| |
| Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> |
| Reviewed-by: Ming Lei <tom.leiming@gmail.com> |
| Reviewed-by: Kevin Hilman <khilman@ti.com> |
| (cherry picked from commit ad3c36a534bc7b945d7bffdda1c62e13bf93489a) |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| drivers/base/power/runtime.c | 68 ++++++++++++++++++++++++++++-------------- |
| 1 file changed, 46 insertions(+), 22 deletions(-) |
| |
| diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c |
| index 04e18ab..aecb2a8 100644 |
| --- a/drivers/base/power/runtime.c |
| +++ b/drivers/base/power/runtime.c |
| @@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(struct device *dev) |
| } |
| |
| /** |
| + * __rpm_callback - Run a given runtime PM callback for a given device. |
| + * @cb: Runtime PM callback to run. |
| + * @dev: Device to run the callback for. |
| + */ |
| +static int __rpm_callback(int (*cb)(struct device *), struct device *dev) |
| + __releases(&dev->power.lock) __acquires(&dev->power.lock) |
| +{ |
| + int retval; |
| + |
| + if (dev->power.irq_safe) |
| + spin_unlock(&dev->power.lock); |
| + else |
| + spin_unlock_irq(&dev->power.lock); |
| + |
| + retval = cb(dev); |
| + |
| + if (dev->power.irq_safe) |
| + spin_lock(&dev->power.lock); |
| + else |
| + spin_lock_irq(&dev->power.lock); |
| + |
| + return retval; |
| +} |
| + |
| +/** |
| * rpm_idle - Notify device bus type if the device can be suspended. |
| * @dev: Device to notify the bus type about. |
| * @rpmflags: Flag bits. |
| @@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev, int rpmflags) |
| else |
| callback = NULL; |
| |
| - if (callback) { |
| - if (dev->power.irq_safe) |
| - spin_unlock(&dev->power.lock); |
| - else |
| - spin_unlock_irq(&dev->power.lock); |
| - |
| - callback(dev); |
| - |
| - if (dev->power.irq_safe) |
| - spin_lock(&dev->power.lock); |
| - else |
| - spin_lock_irq(&dev->power.lock); |
| - } |
| + if (callback) |
| + __rpm_callback(callback, dev); |
| |
| dev->power.idle_notification = false; |
| wake_up_all(&dev->power.wait_queue); |
| @@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev, int rpmflags) |
| * @dev: Device to run the callback for. |
| */ |
| static int rpm_callback(int (*cb)(struct device *), struct device *dev) |
| - __releases(&dev->power.lock) __acquires(&dev->power.lock) |
| { |
| int retval; |
| |
| if (!cb) |
| return -ENOSYS; |
| |
| - if (dev->power.irq_safe) { |
| - retval = cb(dev); |
| - } else { |
| - spin_unlock_irq(&dev->power.lock); |
| - |
| - retval = cb(dev); |
| + retval = __rpm_callback(cb, dev); |
| |
| - spin_lock_irq(&dev->power.lock); |
| - } |
| dev->power.runtime_error = retval; |
| return retval != -EACCES ? retval : -EIO; |
| } |
| @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *dev, int rpmflags) |
| goto out; |
| } |
| |
| + if (dev->power.irq_safe) { |
| + spin_unlock(&dev->power.lock); |
| + |
| + cpu_relax(); |
| + |
| + spin_lock(&dev->power.lock); |
| + goto repeat; |
| + } |
| + |
| /* Wait for the other suspend running in parallel with us. */ |
| for (;;) { |
| prepare_to_wait(&dev->power.wait_queue, &wait, |
| @@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev, int rpmflags) |
| goto out; |
| } |
| |
| + if (dev->power.irq_safe) { |
| + spin_unlock(&dev->power.lock); |
| + |
| + cpu_relax(); |
| + |
| + spin_lock(&dev->power.lock); |
| + goto repeat; |
| + } |
| + |
| /* Wait for the operation carried out in parallel with us. */ |
| for (;;) { |
| prepare_to_wait(&dev->power.wait_queue, &wait, |
| -- |
| 1.7.10.1.362.g242cab3 |
| |