--- a/sys/dev/drm/i915/intel_overlay.c 2026-09-19 09:33:41.608721811 +0000 +++ b/sys/dev/drm/i915/intel_overlay.c 2026-09-19 09:33:52.932577054 +0000 @@ -370,8 +370,15 @@ intel_overlay_release_old_vma(overlay); - overlay->crtc->overlay = NULL; - overlay->crtc = NULL; + /* + * A GPU reset may have run intel_overlay_reset() while our off + * request was still in flight; that clears overlay->crtc (and + * releases old_vma). Do not dereference a cleared crtc here. + */ + if (overlay->crtc) { + overlay->crtc->overlay = NULL; + overlay->crtc = NULL; + } overlay->active = false; if (IS_I830(dev_priv)) @@ -481,6 +488,21 @@ if (!overlay) return; + /* + * Retire any still-pending flip first (e.g. the retire callback of + * an in-flight intel_overlay_off()) BEFORE clearing the overlay + * state. Otherwise: + * (a) intel_overlay_off_tail() fires later, after overlay->crtc + * has been NULLed here, and dereferences it (NULL+offset + * write => kernel page fault); or + * (b) intel_overlay_release_old_vid() below submits a new wait + * while last_flip still tracks the un-retired off request, + * tripping GEM_BUG_ON() in intel_overlay_submit_request(). + * After a GPU reset the request is already complete, so retiring + * here is instantaneous and runs the callback with crtc valid. + */ + intel_overlay_recover_from_interrupt(overlay); + intel_overlay_release_old_vid(overlay); overlay->old_xscale = 0;