Linux Autosuspend Never Starts: Find the Last-Busy Update That Moves the Deadline

A device can return its runtime-PM usage count to zero and still miss every apparent autosuspend opportunity because its last-busy timestamp keeps moving. Trace the timestamp updates and autosuspend requests together. Do not diagnose a reference leak from active status alone, and do not shorten the delay until the source of the updates is understood.

Applicability: this analysis pins Linux v6.12 runtime PM with autosuspend enabled. The timestamps and delay below are hypothetical. The v6.12 documentation retains an older description of jiffies and whole-second rounding; the exact v6.12 implementation uses nanosecond timestamps and high-resolution timer slack, as explained below.

With a hypothetical 200 millisecond delay, marks at zero, 150 and 300 milliseconds move nominal expiration to 200, 350 and 500 milliseconds despite balanced references.
Unmeasured arithmetic fixture. Exact v6.12 code uses nanosecond expiration and timer slack; calculated deadlines are not guaranteed physical suspend times.

1. Separate three reasons for remaining active

First establish whether the usage count returns to its intended idle baseline. Then inspect whether autosuspend is enabled and permitted. Finally determine whether the currently calculated inactivity interval has expired. These are independent questions even when they produce the same visible runtime_status.

The runtime-PM framework guide documents last_busy, autosuspend_delay, and use_autosuspend as distinct state. A negative configured delay prevents runtime suspension through that autosuspend policy. Child activity, callbacks, and other eligibility conditions also remain relevant.

Build a record containing device identity, request identifier, usage-count transitions, last-busy writer, configured delay, autosuspend request time, and actual callback outcome. Without the writer identity, repeated timestamp movement can look like a timer that randomly refuses to expire.

2. Pin the timing calculation to source

The v6.12 pm_runtime_mark_last_busy helper stores the time returned by ktime_get_mono_fast_ns. It does not acquire a usage reference, resume the device, or by itself establish permission to perform hardware I/O.

In v6.12 runtime.c, the calculated expiration is last_busy plus autosuspend_delay converted from milliseconds to nanoseconds. The helper returns a future expiration or zero when the interval is no longer pending or the relevant delay mechanism is disabled.

That implementation does not apply the whole-second rounding described in the older prose. Its timer setup allows slack equal to 25 percent of the configured delay. Treat the calculated expiration as a scheduling input, not an exact timestamp at which a callback or physical power transition must occur.

Timer service, work scheduling, callback execution, and device constraints add their own effects. The slack value is not a total bound on observed suspend latency. Keep requested expiration, timer firing, callback start, and actual low-power evidence in separate columns.

3. Calculate a repeated-update example

For a hypothetical delay of 200 ms, let the last-busy mark initially occur at time 0. Its nominal expiration is 200 ms. A periodic operation finishes at 150 ms, updates last_busy, and returns its reference to zero. The new nominal expiration becomes 350 ms.

If the next operation finishes at 300 ms, the deadline becomes 500 ms. Another at 450 ms moves it to 650 ms. Under this illustrative pattern, every update occurs before the previous inactivity interval can finish. The reference accounting may be completely balanced while autosuspend remains deferred.

The 200 ms delay corresponds to 50 ms of timer slack in the inspected implementation. That arithmetic does not predict exact callback times. A previously scheduled timer can fire and cause the core to evaluate a newer last_busy value instead of suspending immediately.

Distinguish real hardware activity from bookkeeping. A worker that only reports cached application status may have no reason to refresh the device’s last-busy mark. A worker that actually accesses the peripheral needs correct runtime-PM ownership and may legitimately extend the interval. Fixing the latter by removing its mark without reviewing the transaction is unsafe.

4. Locate every timestamp writer

Search the deployed driver and subsystem for direct marks and wrappers that mark on completion. Include polling, diagnostics, retry paths, error recovery, and open/close behavior. Record whether each writer represents successful device activity, an attempted operation, or merely a timer firing.

Check the exact release helper version. In Linux v6.12, pm_runtime_put_autosuspend does not mark last_busy itself. A driver that expects release to refresh the timestamp needs an explicit correct mark; a driver using another version or wrapper may behave differently.

Do not add a mark at every function exit as a blanket repair. That can make a failed no-I/O poll keep the device active indefinitely. Place the mark at the operation boundary justified by the driver and subsystem contract.

Instrumentation should identify both caller and reason. A short reason code such as transaction-complete, recovery-attempt, or cached-status is more useful than printing the same “busy” message from several unrelated paths.

5. Run controlled periodic-work cases

Proposed workload Question to answer
One transaction, then silence Does the last mark stop moving and does suspend become eligible?
Real I/O repeated faster than the delay Does balanced activity explain continuing deferral?
Cached-only reporting at the same period Does unnecessary bookkeeping still refresh last_busy?
Polling stops after several iterations Does the final mark determine the next eligible interval?
Count reaches zero but callback refuses Is the remaining blocker a callback result rather than the timer?

Change one workload source at a time on a recoverable development target. Preserve required monitoring and safety functions. If a periodic function is necessary, consider supported batching or a different policy only after quantifying its functional requirements and transition costs.

Capture enough of the quiet interval after the final operation to distinguish a future deadline from a permanent block. A trace ending immediately after the last put cannot establish that autosuspend never happens.

6. Report the moving deadline and remaining limits

If the latest mark repeatedly precedes the next request by less than the configured delay, explain the observed active interval with that timeline. If the mark stops but suspension still fails, inspect the actual request, policy, child state, and callback result. Do not keep blaming a periodic worker after the evidence stops supporting that hypothesis.

No timer or power experiment was performed for this article. The deliverable is a request/last-busy/expiration trace with exact-version arithmetic and separate actual-suspend evidence. It can show why a zero-usage device stays active without inventing a current saving or a universal autosuspend timing guarantee.

Similar Posts