From: Charan Teja Kalla quic_charante@quicinc.com
[ Upstream commit 40d3b40dce375d6f1c1dbf08d79eed3aed6c691d ]
pm_runtime_put_autosuspend() schedules a hrtimer to expire at "dev->power.timer_expires". If the hrtimer's callback, pm_suspend_timer_fn(), observes that the current time equals "dev->power.timer_expires", it unexpectedly bails out instead of proceeding with runtime suspend.
pm_suspend_timer_fn():
if (expires > 0 && expires < ktime_get_mono_fast_ns()) { dev->power.timer_expires = 0; rpm_suspend(..) }
Additionally, as ->timer_expires is not cleared, all the future auto suspend requests will not schedule hrtimer to perform auto suspend.
rpm_suspend():
if ((rpmflags & RPM_AUTO) &&...) { if (!(dev->power.timer_expires && ...) { <-- this will fail. hrtimer_start_range_ns(&dev->power.suspend_timer,...); } }
Fix this by as well checking if current time reaches the set expiration.
Co-developed-by: Patrick Daly quic_pdaly@quicinc.com Signed-off-by: Patrick Daly quic_pdaly@quicinc.com Signed-off-by: Charan Teja Kalla quic_charante@quicinc.com Link: https://patch.msgid.link/20250515064125.1211561-1-quic_charante@quicinc.com Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Signed-off-by: Sasha Levin sashal@kernel.org ---
Now, based on my analysis, let me provide the decision: **YES** This commit should be backported to stable kernel trees. Here's my extensive analysis: ## Bug Analysis This is a clear **logical bug fix** in the PM runtime autosuspend mechanism that affects device power management. The issue is in the `pm_suspend_timer_fn()` function at `drivers/base/power/runtime.c:1006`. ## The Problem The current code uses a strict less-than comparison: ```c if (expires > 0 && expires < ktime_get_mono_fast_ns()) { ``` However, this creates a race condition where: 1. When `pm_runtime_put_autosuspend()` schedules an hrtimer to expire at exactly `dev->power.timer_expires` 2. If the timer callback `pm_suspend_timer_fn()` observes that the current time **equals** `dev->power.timer_expires` (not less than), it incorrectly bails out 3. The timer expires but the device is not suspended 4. Since `timer_expires` is not cleared, all future auto-suspend requests fail because the condition `if (!(dev->power.timer_expires && ...))` in `rpm_suspend()` (line 596-597) will always be false ## The Fix The fix correctly changes the comparison to `<=` (less than or equal): ```c if (expires > 0 && expires <= ktime_get_mono_fast_ns()) { ``` This ensures that the suspend is triggered when the current time equals or exceeds the expiration time. ## Why This Should Be Backported 1. **Fixes a Real User-Affecting Bug**: Devices may fail to auto-suspend, leading to increased power consumption and potential battery drain on mobile devices. 2. **Minimal Risk**: This is a one-character change (`<` to `<=`) that fixes a clear logical error. The change is extremely contained and low-risk. 3. **Critical Subsystem**: PM runtime is a critical kernel subsystem affecting all device power management. A failure here can impact system-wide power efficiency. 4. **No Architectural Changes**: This doesn't introduce new features or change architecture - it simply fixes incorrect logic. 5. **Small and Contained**: The fix is confined to a single comparison operator in one function. 6. **Clear Co-authors**: The commit shows collaboration between Patrick Daly and Charan Teja Kalla from Qualcomm, indicating this was found in real-world usage. ## Comparison with Similar Commits Looking at the historical examples: - **Similar Commit #5 (Status: YES)**: Also a PM runtime timer fix with data type correction - this had similar characteristics and was backported - The current commit follows the same pattern: critical PM runtime bug fix with minimal, contained changes ## Stable Tree Criteria Met - ✅ Fixes important bug affecting users - ✅ Relatively small and contained fix - ✅ No clear side effects beyond fixing the issue - ✅ No major architectural changes - ✅ Touches critical kernel subsystem (appropriately) - ✅ Follows stable tree rules (important bugfix, minimal risk) - ✅ Has clear explanation of the problem and solution This commit perfectly fits the stable backport criteria and should be included in stable kernel trees to ensure proper device power management functionality across all kernel versions.
drivers/base/power/runtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index edee7f1af1cec..35e1a090ef901 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -996,7 +996,7 @@ static enum hrtimer_restart pm_suspend_timer_fn(struct hrtimer *timer) * If 'expires' is after the current time, we've been called * too early. */ - if (expires > 0 && expires < ktime_get_mono_fast_ns()) { + if (expires > 0 && expires <= ktime_get_mono_fast_ns()) { dev->power.timer_expires = 0; rpm_suspend(dev, dev->power.timer_autosuspends ? (RPM_ASYNC | RPM_AUTO) : RPM_ASYNC);