Describe the bug
After an i915 GPU hang on Meteor Lake with GuC submission enabled, the recovery path can panic while signal_irq_work() calls
guc_retire_inflight_request_prio().
The kernel dump shows that rq->context referred to an intel_context that was concurrently freed. The panic occurred while
attempting to lock context->guc_state.lock.
This report concerns the recovery-path panic, not the underlying cause of the preceding GPU hang.
Panic backtrace
Fatal trap 12: page fault while in kernel mode
current thread: kernel/linuxkpi_short_wq_2
witness_checkorder()
__mtx_lock_flags()
guc_retire_inflight_request_prio()
signal_irq_work()
taskqueue_run_locked()
taskqueue_thread_loop()
The mutex address was inside the freed intel_context. Inspection of the dump showed that the context allocation had been
overwritten with FreeBSD's 0xdeadc0de freed-memory poison pattern.
The i915_request itself was still referenced and valid. It was specifically the associated context whose lifetime had ended.
Root cause
signal_irq_work() currently uses this ordering:
rcu_read_lock();
/* Traverse signalers and move completed requests to a local list. */
rcu_read_unlock();
llist_for_each_safe(signal, sn, signal) {
struct i915_request *rq = ...;
if (rq->engine->sched_engine->retire_inflight_request_prio)
rq->engine->sched_engine->retire_inflight_request_prio(rq);
...
}
Holding a request reference does not keep its intel_context alive. Concurrent request retirement can call intel_context_unpin(),
drop the final context reference and schedule rcu_context_free().
On Linux, irq_work normally runs in interrupt context, which supplies an implicit RCU read-side critical section on the non-
PREEMPT_RT kernels supported by i915. FreeBSD LinuxKPI executes irq_work callbacks on an ordinary taskqueue. Consequently, once
the explicit rcu_read_unlock() above executes, the RCU callback may free the context while the taskqueue worker is still
processing the local request list.
The dump and disassembly show that guc_retire_inflight_request_prio() loaded the context and examined it, after which the
context was freed before the function attempted to lock the embedded GuC-state mutex.
Proposed fix
The attached patch moves rcu_read_unlock() below the local request-processing loop, keeping every post-processing use of
rq->context inside the explicit RCU read-side critical section.
The patch builds successfully and passes git apply --check against:
- freebsd/drm-kmod master at b6f7264
- the Meteor Lake development tree at 74d460b
PR #469 still contains the original ordering and therefore does not currently fix this race. Its required LinuxKPI branch also
continues to invoke irq_work callbacks from a taskqueue without adding equivalent RCU protection.
drm-kmod-mtl-irq-work-rcu-lifetime.patch
Describe the bug
After an i915 GPU hang on Meteor Lake with GuC submission enabled, the recovery path can panic while
signal_irq_work()callsguc_retire_inflight_request_prio().The kernel dump shows that
rq->contextreferred to anintel_contextthat was concurrently freed. The panic occurred whileattempting to lock
context->guc_state.lock.This report concerns the recovery-path panic, not the underlying cause of the preceding GPU hang.
Panic backtrace
Fatal trap 12: page fault while in kernel mode
current thread: kernel/linuxkpi_short_wq_2
witness_checkorder()
__mtx_lock_flags()
guc_retire_inflight_request_prio()
signal_irq_work()
taskqueue_run_locked()
taskqueue_thread_loop()
The mutex address was inside the freed intel_context. Inspection of the dump showed that the context allocation had been
overwritten with FreeBSD's 0xdeadc0de freed-memory poison pattern.
The i915_request itself was still referenced and valid. It was specifically the associated context whose lifetime had ended.
Root cause
signal_irq_work() currently uses this ordering:
rcu_read_lock();
/* Traverse signalers and move completed requests to a local list. */
rcu_read_unlock();
llist_for_each_safe(signal, sn, signal) {
struct i915_request *rq = ...;
}
Holding a request reference does not keep its intel_context alive. Concurrent request retirement can call intel_context_unpin(),
drop the final context reference and schedule rcu_context_free().
On Linux, irq_work normally runs in interrupt context, which supplies an implicit RCU read-side critical section on the non-
PREEMPT_RT kernels supported by i915. FreeBSD LinuxKPI executes irq_work callbacks on an ordinary taskqueue. Consequently, once
the explicit rcu_read_unlock() above executes, the RCU callback may free the context while the taskqueue worker is still
processing the local request list.
The dump and disassembly show that guc_retire_inflight_request_prio() loaded the context and examined it, after which the
context was freed before the function attempted to lock the embedded GuC-state mutex.
Proposed fix
The attached patch moves rcu_read_unlock() below the local request-processing loop, keeping every post-processing use of
rq->context inside the explicit RCU read-side critical section.
The patch builds successfully and passes git apply --check against:
PR #469 still contains the original ordering and therefore does not currently fix this race. Its required LinuxKPI branch also
continues to invoke irq_work callbacks from a taskqueue without adding equivalent RCU protection.
drm-kmod-mtl-irq-work-rcu-lifetime.patch