aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@linaro.org>2015-11-18 05:48:39 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-11-30 08:50:05 -0500
commit5de85b9d57aba3ed2e04759e6db3b9e826dd0b06 (patch)
treee35d33b62926955f32e631dc65ace3465e401d32
parent31ade3b83e1821da5fbb2f11b5b3d4ab2ec39db8 (diff)
PM / runtime: Re-init runtime PM states at probe error and driver unbind
There are two common expectations among several subsystems/drivers that deploys runtime PM support, but which isn't met by the driver core. Expectation 1) At ->probe() the subsystem/driver expects the runtime PM status of the device to be RPM_SUSPENDED, which is the initial status being assigned at device registration. This expectation is especially common among some of those subsystems/ drivers that manages devices with an attached PM domain, as those requires the ->runtime_resume() callback at the PM domain level to be invoked during ->probe(). Moreover these subsystems/drivers entirely relies on runtime PM resources being managed at the PM domain level, thus don't implement their own set of runtime PM callbacks. These are two scenarios that suffers from this unmet expectation. i) A failed ->probe() sequence requests probe deferral: ->probe() ... pm_runtime_enable() pm_runtime_get_sync() ... err: pm_runtime_put() pm_runtime_disable() ... As there are no guarantees that such sequence turns the runtime PM status of the device into RPM_SUSPENDED, the re-trying ->probe() may start with the status in RPM_ACTIVE. In such case the runtime PM core won't invoke the ->runtime_resume() callback because of a pm_runtime_get_sync(), as it considers the device to be already runtime resumed. ii) A driver re-bind sequence: At driver unbind, the subsystem/driver's >remove() callback invokes a sequence of runtime PM APIs, to undo actions during ->probe() and to put the device into low power state. ->remove() ... pm_runtime_put() pm_runtime_disable() ... Similar as in the failing ->probe() case, this sequence don't guarantee the runtime PM status of the device to turn into RPM_SUSPENDED. Trying to re-bind the driver thus causes the same issue as when re-trying ->probe(), in the probe deferral scenario. Expectation 2) Drivers that invokes the pm_runtime_irq_safe() API during ->probe(), triggers the runtime PM core to increase the usage count for the device's parent and permanently make it runtime resumed. The usage count is only dropped at device removal, which also allows it to be runtime suspended again. A re-trying ->probe() repeats the call to pm_runtime_irq_safe() and thus once more triggers the usage count of the device's parent to be increased. This leads to not only an imbalance issue of the usage count of the device's parent, but also to keep it runtime resumed permanently even if ->probe() fails. To address these issues, let's change the policy of the driver core to meet these expectations. More precisely, at ->probe() failures and driver unbind, restore the initial states of runtime PM. Although to still allow subsystem's to control PM for devices that doesn't ->probe() successfully, don't restore the initial states unless runtime PM is disabled. Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Reviewed-by: Kevin Hilman <khilman@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/base/dd.c2
-rw-r--r--drivers/base/power/power.h2
-rw-r--r--drivers/base/power/runtime.c26
3 files changed, 24 insertions, 6 deletions
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index a641cf3ccad6..cd2d79b1bf01 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -340,6 +340,7 @@ probe_failed:
340 dev_set_drvdata(dev, NULL); 340 dev_set_drvdata(dev, NULL);
341 if (dev->pm_domain && dev->pm_domain->dismiss) 341 if (dev->pm_domain && dev->pm_domain->dismiss)
342 dev->pm_domain->dismiss(dev); 342 dev->pm_domain->dismiss(dev);
343 pm_runtime_reinit(dev);
343 344
344 switch (ret) { 345 switch (ret) {
345 case -EPROBE_DEFER: 346 case -EPROBE_DEFER:
@@ -695,6 +696,7 @@ static void __device_release_driver(struct device *dev)
695 dev_set_drvdata(dev, NULL); 696 dev_set_drvdata(dev, NULL);
696 if (dev->pm_domain && dev->pm_domain->dismiss) 697 if (dev->pm_domain && dev->pm_domain->dismiss)
697 dev->pm_domain->dismiss(dev); 698 dev->pm_domain->dismiss(dev);
699 pm_runtime_reinit(dev);
698 700
699 klist_remove(&dev->p->knode_driver); 701 klist_remove(&dev->p->knode_driver);
700 if (dev->bus) 702 if (dev->bus)
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 998fa6b23084..8b06193d4a5e 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -18,6 +18,7 @@ static inline void pm_runtime_early_init(struct device *dev)
18} 18}
19 19
20extern void pm_runtime_init(struct device *dev); 20extern void pm_runtime_init(struct device *dev);
21extern void pm_runtime_reinit(struct device *dev);
21extern void pm_runtime_remove(struct device *dev); 22extern void pm_runtime_remove(struct device *dev);
22 23
23struct wake_irq { 24struct wake_irq {
@@ -84,6 +85,7 @@ static inline void pm_runtime_early_init(struct device *dev)
84} 85}
85 86
86static inline void pm_runtime_init(struct device *dev) {} 87static inline void pm_runtime_init(struct device *dev) {}
88static inline void pm_runtime_reinit(struct device *dev) {}
87static inline void pm_runtime_remove(struct device *dev) {} 89static inline void pm_runtime_remove(struct device *dev) {}
88 90
89static inline int dpm_sysfs_add(struct device *dev) { return 0; } 91static inline int dpm_sysfs_add(struct device *dev) { return 0; }
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index e1a10a03df8e..ab3fcd9f6c98 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -1390,18 +1390,32 @@ void pm_runtime_init(struct device *dev)
1390} 1390}
1391 1391
1392/** 1392/**
1393 * pm_runtime_reinit - Re-initialize runtime PM fields in given device object.
1394 * @dev: Device object to re-initialize.
1395 */
1396void pm_runtime_reinit(struct device *dev)
1397{
1398 if (!pm_runtime_enabled(dev)) {
1399 if (dev->power.runtime_status == RPM_ACTIVE)
1400 pm_runtime_set_suspended(dev);
1401 if (dev->power.irq_safe) {
1402 spin_lock_irq(&dev->power.lock);
1403 dev->power.irq_safe = 0;
1404 spin_unlock_irq(&dev->power.lock);
1405 if (dev->parent)
1406 pm_runtime_put(dev->parent);
1407 }
1408 }
1409}
1410
1411/**
1393 * pm_runtime_remove - Prepare for removing a device from device hierarchy. 1412 * pm_runtime_remove - Prepare for removing a device from device hierarchy.
1394 * @dev: Device object being removed from device hierarchy. 1413 * @dev: Device object being removed from device hierarchy.
1395 */ 1414 */
1396void pm_runtime_remove(struct device *dev) 1415void pm_runtime_remove(struct device *dev)
1397{ 1416{
1398 __pm_runtime_disable(dev, false); 1417 __pm_runtime_disable(dev, false);
1399 1418 pm_runtime_reinit(dev);
1400 /* Change the status back to 'suspended' to match the initial status. */
1401 if (dev->power.runtime_status == RPM_ACTIVE)
1402 pm_runtime_set_suspended(dev);
1403 if (dev->power.irq_safe && dev->parent)
1404 pm_runtime_put(dev->parent);
1405} 1419}
1406 1420
1407/** 1421/**