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 ---
Based on my analysis of the commit and the code context, here is my assessment: **YES** This commit should be backported to stable kernel trees. Here's my extensive analysis: ## Code Analysis The commit fixes a critical bug in the PM runtime autosuspend logic where `pm_suspend_timer_fn()` uses a strict inequality (`<`) instead of less- than-or-equal (`<=`) when comparing the timer expiration time with the current time. **The Bug:** ```c // BEFORE (buggy): 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); } // AFTER (fixed): 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); } ``` ## Why This Bug is Critical 1. **Race Condition:** When the timer fires exactly at the scheduled expiration time (`expires == ktime_get_mono_fast_ns()`), the current logic bails out without performing the suspend operation. 2. **Persistent State Corruption:** The bug has a cascading effect - when `timer_expires` is not cleared, future autosuspend requests fail. Looking at the `rpm_suspend()` function at lines 596-597: ```c if (!(dev->power.timer_expires && dev->power.timer_expires <= expires)) { ``` If `timer_expires` remains set from the failed timer callback, this condition will evaluate incorrectly, preventing future hrtimer scheduling. 3. **System-wide Impact:** This affects the PM runtime subsystem, which is fundamental to power management across the entire kernel. Devices that rely on autosuspend will get stuck in active state, leading to increased power consumption. ## Backport Suitability Assessment **✅ Fixes Important Bug:** This fixes a functional bug that can cause devices to not autosuspend, leading to power regression. **✅ Small and Contained:** The change is a single character modification (`<` to `<=`) - extremely minimal risk. **✅ No Architectural Changes:** This doesn't change any interfaces or architecture, just fixes the logic. **✅ Affects Critical Subsystem:** PM runtime is used extensively across the kernel for power management. **✅ Clear Side Effects:** The fix has clear, positive side effects - devices will now properly autosuspend when scheduled. **✅ Follows Stable Rules:** This is exactly the type of important bugfix with minimal risk that stable trees are designed for. ## Comparison with Historical Commits Looking at the similar commits provided: - Similar Commit #5 (Status: YES) was backported for fixing a data type issue in the same subsystem that could cause timer truncation on 32-bit architectures - This current fix is similar in scope - it's fixing timer-related logic in the same PM runtime subsystem - The risk profile is even lower than Similar Commit #5 since it's just a comparison operator change ## Conclusion This commit meets all criteria for stable backporting: - It fixes a real functional bug affecting power management - The change is minimal and surgical - It has no risk of introducing regressions - It affects a critical kernel subsystem used system-wide - The fix is straightforward and well-understood The timing issue fixed here could affect any device using PM runtime autosuspend, making this a candidate for immediate stable backporting to prevent power regressions in production systems.
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 bb68cba4d85a9..313ccb7e77646 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1001,7 +1001,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);