Skip to contentSkip to conversation

PM: runtime: Fix race getting/putting suppliers at probe

PATCH In review Adrian Hunter 2 participants 4 messages Mar 26, 2021

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

cover PM: runtime: Fix race getting/putting suppliers at probe

Hi

SCSI UFS is starting to use device links for power management but has run into issues. Here are 2 patches that seem to help.

Adrian Hunter (2):
PM: runtime: Fix ordering in pm_runtime_get_suppliers()
PM: runtime: Fix race getting/putting suppliers at probe

 drivers/base/power/runtime.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

Regards
Adrian

#1 lore raw

PATCH 1/2 PM: runtime: Fix ordering in pm_runtime_get_suppliers()

rpm_active indicates how many times the supplier usage_count has been incremented. Consequently it must be updated after pm_runtime_get_sync() of the supplier, not before.

Fixes 4c06c4e6cf63 ("driver core: Fix possible supplier PM-usage counter imbalance")
---
 drivers/base/power/runtime.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/base/power/runtime.c +1 -1
index d54e540067bf..4fde37713c58 100644
@@ -1690,8 +1690,8 @@ void pm_runtime_get_suppliers(struct device *dev)
 				device_links_read_lock_held())
 		if (link->flags & DL_FLAG_PM_RUNTIME) {
 			link->supplier_preactivated = true;
-			refcount_inc(&link->rpm_active);
 			pm_runtime_get_sync(link->supplier);
+			refcount_inc(&link->rpm_active);
 		}
 
 	device_links_read_unlock(idx);
Signature · 1 line
2.17.1
#2 lore raw

PATCH 2/2 PM: runtime: Fix race getting/putting suppliers at probe

In reply to Adrian Hunter

pm_runtime_put_suppliers() must not decrement rpm_active unless the consumer is suspended. That is because, otherwise, it could suspend suppliers for an active consumer.

That can happen as follows:

static int driver_probe_device(struct device_driver *drv, struct device *dev)

 {
	int ret = 0;

	if (!device_is_registered(dev))
		return -ENODEV;

	dev->can_match = true;
	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

	pm_runtime_get_suppliers(dev);
	if (dev->parent)
		pm_runtime_get_sync(dev->parent);

At this point, dev can runtime suspend so rpm_put_suppliers() can run, rpm_active becomes 1 (the lowest value).

	pm_runtime_barrier(dev);
	if (initcall_debug)
		ret = really_probe_debug(dev, drv);
	else
		ret = really_probe(dev, drv);

Probe callback can have runtime resumed dev, and then runtime put so dev is awaiting autosuspend, but rpm_active is 2.

	pm_request_idle(dev);

	if (dev->parent)
		pm_runtime_put(dev->parent);

	pm_runtime_put_suppliers(dev);

Now pm_runtime_put_suppliers() will put the supplier i.e. rpm_active 2 -> 1, but consumer can still be active.

	return ret;

}

Fix by checking the runtime status. For any status other than RPM_SUSPENDED, rpm_active can be considered to be "owned" by rpm_[get/put]_suppliers() and pm_runtime_put_suppliers() need do nothing.

Fixes 4c06c4e6cf63 ("driver core: Fix possible supplier PM-usage counter imbalance")
---
 drivers/base/power/runtime.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/base/power/runtime.c +7 -1
index 4fde37713c58..fe1dad68aee4 100644
@@ -1704,6 +1704,8 @@ void pm_runtime_get_suppliers(struct device *dev)
 void pm_runtime_put_suppliers(struct device *dev)
 {
 	struct device_link *link;
+	unsigned long flags;
+	bool put;
 	int idx;
 
 	idx = device_links_read_lock();
@@ -1712,7 +1714,11 @@ void pm_runtime_put_suppliers(struct device *dev)
 				device_links_read_lock_held())
 		if (link->supplier_preactivated) {
 			link->supplier_preactivated = false;
-			if (refcount_dec_not_one(&link->rpm_active))
+			spin_lock_irqsave(&dev->power.lock, flags);
+			put = pm_runtime_status_suspended(dev) &&
+			      refcount_dec_not_one(&link->rpm_active);
+			spin_unlock_irqrestore(&dev->power.lock, flags);
+			if (put)
 				pm_runtime_put(link->supplier);
 		}
 
Signature · 1 line
2.17.1
#3 lore raw
In reply to Adrian Hunter
Quoted 10 lines · Adrian Hunter

Hi

SCSI UFS is starting to use device links for power management but has run into issues. Here are 2 patches that seem to help.

Adrian Hunter (2):
PM: runtime: Fix ordering in pm_runtime_get_suppliers()
PM: runtime: Fix race getting/putting suppliers at probe

Both look good to me, so queuing them up for the next 5.12-rc, thanks!