aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@linaro.org>2018-05-18 04:48:49 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2018-05-27 06:10:32 -0400
commit1e8378619841ef1d621b130bbd3fc3b7e6739b50 (patch)
treef986241cbcd4db9aaf11e74d2e2776616d79e06e
parent21c73367fc8f265657b24343497ed5135e296c24 (diff)
PM / runtime: Fixup reference counting of device link suppliers at probe
In the driver core, before it invokes really_probe() it runtime resumes the suppliers for the device via calling pm_runtime_get_suppliers(), which also increases the runtime PM usage count for each of the available supplier. This makes sense, as to be able to allow the consumer device to be probed by its driver. However, if the driver decides to add a new supplier link during ->probe(), hence updating the list of suppliers, the following call to pm_runtime_put_suppliers(), invoked after really_probe() in the driver core, we get into trouble. More precisely, pm_runtime_put() gets called also for the new supplier(s), which is wrong as the driver core, didn't trigger pm_runtime_get_sync() to be called for it in the first place. In other words, the new supplier may be runtime suspended even in cases when it shouldn't. Fix this behaviour, by runtime resume suppliers according to the same conditions as managed by the runtime PM core, when runtime resume callbacks are being invoked. Additionally, don't try to runtime suspend any of the suppliers after really_probe(), but instead rely on that to happen via the consumer device, when it becomes runtime suspended. Fixes: 21d5c57b3726 (PM / runtime: Use device links) Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/base/dd.c3
-rw-r--r--drivers/base/power/runtime.c27
-rw-r--r--include/linux/pm_runtime.h6
3 files changed, 6 insertions, 30 deletions
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 10454fe54482..a41c91bfac0e 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -580,7 +580,7 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
580 pr_debug("bus: '%s': %s: matched device %s with driver %s\n", 580 pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
581 drv->bus->name, __func__, dev_name(dev), drv->name); 581 drv->bus->name, __func__, dev_name(dev), drv->name);
582 582
583 pm_runtime_get_suppliers(dev); 583 pm_runtime_resume_suppliers(dev);
584 if (dev->parent) 584 if (dev->parent)
585 pm_runtime_get_sync(dev->parent); 585 pm_runtime_get_sync(dev->parent);
586 586
@@ -591,7 +591,6 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
591 if (dev->parent) 591 if (dev->parent)
592 pm_runtime_put(dev->parent); 592 pm_runtime_put(dev->parent);
593 593
594 pm_runtime_put_suppliers(dev);
595 return ret; 594 return ret;
596} 595}
597 596
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 8bef3cb2424d..6f4f50e196c1 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -1563,37 +1563,16 @@ void pm_runtime_clean_up_links(struct device *dev)
1563} 1563}
1564 1564
1565/** 1565/**
1566 * pm_runtime_get_suppliers - Resume and reference-count supplier devices. 1566 * pm_runtime_resume_suppliers - Resume supplier devices.
1567 * @dev: Consumer device. 1567 * @dev: Consumer device.
1568 */ 1568 */
1569void pm_runtime_get_suppliers(struct device *dev) 1569void pm_runtime_resume_suppliers(struct device *dev)
1570{ 1570{
1571 struct device_link *link;
1572 int idx; 1571 int idx;
1573 1572
1574 idx = device_links_read_lock(); 1573 idx = device_links_read_lock();
1575 1574
1576 list_for_each_entry_rcu(link, &dev->links.suppliers, c_node) 1575 rpm_get_suppliers(dev);
1577 if (link->flags & DL_FLAG_PM_RUNTIME)
1578 pm_runtime_get_sync(link->supplier);
1579
1580 device_links_read_unlock(idx);
1581}
1582
1583/**
1584 * pm_runtime_put_suppliers - Drop references to supplier devices.
1585 * @dev: Consumer device.
1586 */
1587void pm_runtime_put_suppliers(struct device *dev)
1588{
1589 struct device_link *link;
1590 int idx;
1591
1592 idx = device_links_read_lock();
1593
1594 list_for_each_entry_rcu(link, &dev->links.suppliers, c_node)
1595 if (link->flags & DL_FLAG_PM_RUNTIME)
1596 pm_runtime_put(link->supplier);
1597 1576
1598 device_links_read_unlock(idx); 1577 device_links_read_unlock(idx);
1599} 1578}
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h
index f0fc4700b6ff..db5dbbf7a48d 100644
--- a/include/linux/pm_runtime.h
+++ b/include/linux/pm_runtime.h
@@ -56,8 +56,7 @@ extern void pm_runtime_update_max_time_suspended(struct device *dev,
56 s64 delta_ns); 56 s64 delta_ns);
57extern void pm_runtime_set_memalloc_noio(struct device *dev, bool enable); 57extern void pm_runtime_set_memalloc_noio(struct device *dev, bool enable);
58extern void pm_runtime_clean_up_links(struct device *dev); 58extern void pm_runtime_clean_up_links(struct device *dev);
59extern void pm_runtime_get_suppliers(struct device *dev); 59extern void pm_runtime_resume_suppliers(struct device *dev);
60extern void pm_runtime_put_suppliers(struct device *dev);
61extern void pm_runtime_new_link(struct device *dev); 60extern void pm_runtime_new_link(struct device *dev);
62extern void pm_runtime_drop_link(struct device *dev); 61extern void pm_runtime_drop_link(struct device *dev);
63 62
@@ -173,8 +172,7 @@ static inline unsigned long pm_runtime_autosuspend_expiration(
173static inline void pm_runtime_set_memalloc_noio(struct device *dev, 172static inline void pm_runtime_set_memalloc_noio(struct device *dev,
174 bool enable){} 173 bool enable){}
175static inline void pm_runtime_clean_up_links(struct device *dev) {} 174static inline void pm_runtime_clean_up_links(struct device *dev) {}
176static inline void pm_runtime_get_suppliers(struct device *dev) {} 175static inline void pm_runtime_resume_suppliers(struct device *dev) {}
177static inline void pm_runtime_put_suppliers(struct device *dev) {}
178static inline void pm_runtime_new_link(struct device *dev) {} 176static inline void pm_runtime_new_link(struct device *dev) {}
179static inline void pm_runtime_drop_link(struct device *dev) {} 177static inline void pm_runtime_drop_link(struct device *dev) {}
180 178