Commit f384497a authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

PM: sleep: core: Fix runtime PM enabling in device_resume_early()



Runtime PM should only be enabled in device_resume_early() if it has
been disabled for the given device by device_suspend_late().  Otherwise,
it may cause runtime PM callbacks to run prematurely in some cases
which leads to further functional issues.

Make two changes to address this problem.

First, reorder device_suspend_late() to only disable runtime PM for a
device when it is going to look for the device's callback or if the
device is a "syscore" one.  In all of the other cases, disabling runtime
PM for the device is not in fact necessary.  However, if the device's
callback returns an error and the power.is_late_suspended flag is not
going to be set, enable runtime PM so it only remains disabled when
power.is_late_suspended is set.

Second, make device_resume_early() only enable runtime PM for the
devices with the power.is_late_suspended flag set.

Fixes: 443046d1 ("PM: sleep: Make suspend of devices more asynchronous")
Reported-by: default avatarRose Wu <ya-jou.wu@mediatek.com>
Closes: https://lore.kernel.org/linux-pm/70b25dca6f8c2756d78f076f4a7dee7edaaffc33.camel@mediatek.com/


Cc: 6.16+ <stable@vger.kernel.org> # 6.16+
Reviewed-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://patch.msgid.link/12784270.O9o76ZdvQC@rafael.j.wysocki
parent 6a23ae0a
Loading
Loading
Loading
Loading
+16 −9
Original line number Diff line number Diff line
@@ -888,12 +888,15 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
	TRACE_DEVICE(dev);
	TRACE_RESUME(0);

	if (dev->power.syscore || dev->power.direct_complete)
	if (dev->power.direct_complete)
		goto Out;

	if (!dev->power.is_late_suspended)
		goto Out;

	if (dev->power.syscore)
		goto Skip;

	if (!dpm_wait_for_superior(dev, async))
		goto Out;

@@ -926,11 +929,11 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy

Skip:
	dev->power.is_late_suspended = false;
	pm_runtime_enable(dev);

Out:
	TRACE_RESUME(error);

	pm_runtime_enable(dev);
	complete_all(&dev->power.completion);

	if (error) {
@@ -1615,12 +1618,6 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
	TRACE_DEVICE(dev);
	TRACE_SUSPEND(0);

	/*
	 * Disable runtime PM for the device without checking if there is a
	 * pending resume request for it.
	 */
	__pm_runtime_disable(dev, false);

	dpm_wait_for_subordinate(dev, async);

	if (READ_ONCE(async_error))
@@ -1631,9 +1628,18 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
		goto Complete;
	}

	if (dev->power.syscore || dev->power.direct_complete)
	if (dev->power.direct_complete)
		goto Complete;

	/*
	 * Disable runtime PM for the device without checking if there is a
	 * pending resume request for it.
	 */
	__pm_runtime_disable(dev, false);

	if (dev->power.syscore)
		goto Skip;

	if (dev->pm_domain) {
		info = "late power domain ";
		callback = pm_late_early_op(&dev->pm_domain->ops, state);
@@ -1664,6 +1670,7 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
		WRITE_ONCE(async_error, error);
		dpm_save_failed_dev(dev_name(dev));
		pm_dev_err(dev, state, async ? " async late" : " late", error);
		pm_runtime_enable(dev);
		goto Complete;
	}
	dpm_propagate_wakeup_to_parent(dev);