I encounter intermittent shutdown/restart hangs with i915/GuC and the normal 34 ms scheduling-disable delay. Setting that delay to zero has avoided the hang in my testing. This review fixes a reproducible LinuxKPI cancellation-accounting defect that can leave the corresponding context reference unreleased, rather than changing the driver's delay.
Commit 79e290d967862bef1adcf39f0bfcf1b4993a8202 corrected the ordinary queued and already-executing return values of cancel_delayed_work_sync(). There is still a gap between native taskqueue dequeue and LinuxKPI callback ownership:
- In sys/kern/subr_taskqueue.c, taskqueue_run_locked() removes the task, clears ta_pending and sets tb_running before calling its handler.
- Before linux_work_fn() claims the callback, linux_cancel_delayed_work_sync_int() atomically changes WORK_ST_TASK to WORK_ST_IDLE.
- taskqueue_cancel() can now return EBUSY with pending == 0: the native task is busy, but the Linux callback has not started.
- linux_work_fn() subsequently sees IDLE and skips the callback. The cancelling thread drains the task and retries, but returns false because neither the timer-stop result nor the native pending count records what it cancelled.
The task can also finish consuming IDLE before taskqueue_cancel() is called, giving success with pending == 0 and the same incorrect false return. This affects zero-delay WORK_ST_TIMER and WORK_ST_CANCEL left by a failed nonblocking cancellation as well. A zero-delay self-requeue handled by linux_work_fn()'s executor loop can similarly be pending without a native queue entry.
Use the old state returned by the atomic transition to account for pending work suppressed by this cancellation: TIMER, TASK and CANCEL contribute a true result. EXEC does not: the callback has already been claimed, or has finished with that sticky state. Keep the native pending-count aggregation, drain operations and retry decisions unchanged; use a separate local variable for the callout-stop result. In particular, EBUSY alone is not treated as successful cancellation, which would regress the already-running-callback case fixed by 79e290d96786. Correct the helper's comment to distinguish its retry return value from its cancellation output.
One affected consumer is guc_request_alloc() in drm-kmod's drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c. It calls intel_context_sched_disable_unpin() when cancel_delayed_work_sync() returns true. Suppressing the delayed callback while returning false can leave neither path responsible for releasing that reference, retaining the engine wakeref that shutdown later waits for.
My failed-shutdown diagnostic capture showed a GT wakeref count of 1, an RCS0 wakeref count of 2, no pending GuC replies and a shutdown wakeref wait lasting at least 51 seconds. This is consistent with the mechanism above, but the diagnostic history dropped some observations and did not capture the decisive atomic transition. The capture came from a kernel based on b9811d13572b with unrelated local changes; linux_work.c and subr_taskqueue.c were unmodified. The controlled tests establish the source defect independently; they do not prove that every hardware hang has this cause.
The patch is based on vanilla main f492ef8318f580081047da41905c3b339e924387, verified as the current tip on September 26. It changes only linux_work.c, with no drm-kmod, GuC-delay, PFN, Wi-Fi, Bluetooth or diagnostic changes. An LLM prepared the patch and the source-extraction test harness. I have now booted a kernel containing this fix on the affected laptop, alongside my unrelated local kernel changes, with vanilla drm-kmod and the normal 34 ms GuC delay. This confirms basic integration; repeated shutdown/restart trials and an in-kernel race test are still required.
Related earlier fix: https://cgit.freebsd.org/src/commit/?id=79e290d967862bef1adcf39f0bfcf1b4993a8202
Related discussion: https://github.com/freebsd/freebsd-src/pull/2268
Driver call site: https://github.com/freebsd/drm-kmod/blob/f252a30f27d157d9c763cd408850775096a6263f/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c#L3895
Full-context patch file:
Source-extraction test sources, manifests and results: