On Wed, 12 Aug 2026, Weifeng Liu <[email protected]> wrote: > Gentle ping. I would like to provide some additional context about the > issue this patch is trying to fix.
Have you filed a bug over at [1]? If not, please do, and add the information there, with logs, and point at this thread too. Thanks, Jani. [1] https://drm.pages.freedesktop.org/intel-docs/how-to-file-i915-bugs.html > > A customer observed a shutdown hang in an automated S3 resume and > power-off test. This issue occurs after resuming from S3 and later > starting the power-off sequence. During process teardown, i915 reports > GuC CT errors such as: > > i915 0000:00:02.0: [drm] *ERROR* GT0: GUC: CT: Unsolicited response > message: len 1, data 0xe0000100 (fence 2941, last 2945) > i915 0000:00:02.0: [drm] *ERROR* GT0: GUC: CT: Failed to handle HXG > message (-ENOKEY) 00 01 00 e0 > i915 0000:00:02.0: [drm] *ERROR* GT0: GUC: CT: Failed to process CT > message (-ENOKEY) 01 00 7d 0b 00 01 00 e0 > > No rendering or display failure was observed before shutdown. The GuC > error appears to be related to inconsistent context state after S3, but > this patch does not attempt to fix that underlying issue; I focused on > fixing the shutdown hang issue currently. > > With initcall_debug enabled, the last device shutdown message is: > > i915 0000:00:02.0: shutdown > > and device_shutdown() does not make further progress. > > While investigating this hang, I found that wait_for_suspend() assumes > that intel_gt_wait_for_idle() either succeeds or returns -ETIME. > However, the request retirement and GuC pending-message waits are > interruptible and may return -EINTR or -ERESTARTSYS when the shutdown > task has a pending signal. These errors bypass the existing wedge and > cleanup path, after which wait_for_suspend() may block waiting for a GT > wakeref that cannot be released. > > As a diagnostic experiment, adding a timeout to > intel_wakeref_wait_for_idle() allowed the power-off sequence to > complete consistently. I did not propose that approach upstream because > continuing suspend with an active GT would weaken the existing suspend > invariant. > > Instead, this patch makes only GT idle wait used by wait_for_suspend() > uninterruptible. It retains the existing timeout and wedges the GT only > if that timeout expires. All other intel_gt_wait_for_idle() callers > remain interruptible. > > Does this approach look reasonable, or would you prefer a different way > to prevent signals from bypassing the existing timeout recovery path? > > And I'll be very grateful if you can provide some hints about the root > cause of the GuC error. > > Best regards, > Weifeng > > On Wed, 2026-08-05 at 14:45 +0800, Weifeng Liu wrote: >> wait_for_suspend() only handles -ETIME from the GT idle wait. >> However, >> the wait can also be interrupted by a pending signal while retiring >> requests or waiting for GuC messages, e.g., during the reboot process >> invoked by init. This skips the wedge and cleanup path and may leave >> suspend waiting indefinitely for a leaked wakeref. >> >> Make only the suspend idle wait uninterruptible while preserving its >> existing timeout. Other callers remain interruptible, and the GT is >> still wedged only when the timeout expires. >> >> Cc: Matthew Brost <[email protected]> >> Assisted-by: OpenCode:GPT-5.6-Sol >> Signed-off-by: Weifeng Liu <[email protected]> >> --- >> drivers/gpu/drm/i915/gt/intel_gt.c | 24 +++++++++++++++-- >> -- >> drivers/gpu/drm/i915/gt/intel_gt.h | 1 + >> drivers/gpu/drm/i915/gt/intel_gt_pm.c | 3 ++- >> drivers/gpu/drm/i915/gt/intel_gt_requests.c | 8 ++++--- >> drivers/gpu/drm/i915/gt/intel_gt_requests.h | 13 ++++++++-- >> drivers/gpu/drm/i915/gt/uc/intel_guc.h | 3 ++- >> .../gpu/drm/i915/gt/uc/intel_guc_submission.c | 5 ++-- >> drivers/gpu/drm/i915/gt/uc/intel_uc.h | 6 +++-- >> 8 files changed, 47 insertions(+), 16 deletions(-) >> >> diff --git a/drivers/gpu/drm/i915/gt/intel_gt.c >> b/drivers/gpu/drm/i915/gt/intel_gt.c >> index 5c7f862f7100..5eaa52f535d4 100644 >> --- a/drivers/gpu/drm/i915/gt/intel_gt.c >> +++ b/drivers/gpu/drm/i915/gt/intel_gt.c >> @@ -659,7 +659,9 @@ static void __intel_gt_disable(struct intel_gt >> *gt) >> GEM_BUG_ON(intel_gt_pm_is_awake(gt)); >> } >> >> -int intel_gt_wait_for_idle(struct intel_gt *gt, long timeout) >> +static int __intel_gt_wait_for_idle(struct intel_gt *gt, >> + bool interruptible, >> + long timeout) >> { >> long remaining_timeout; >> >> @@ -667,10 +669,11 @@ int intel_gt_wait_for_idle(struct intel_gt *gt, >> long timeout) >> if (!intel_gt_pm_is_awake(gt)) >> return 0; >> >> - while ((timeout = intel_gt_retire_requests_timeout(gt, >> timeout, >> - >> &remaining_timeout)) > 0) { >> + while ((timeout = __intel_gt_retire_requests_timeout(gt, >> interruptible, >> + >> timeout, >> + >> &remaining_timeout)) > 0) { >> cond_resched(); >> - if (signal_pending(current)) >> + if (interruptible && signal_pending(current)) >> return -EINTR; >> } >> >> @@ -680,7 +683,18 @@ int intel_gt_wait_for_idle(struct intel_gt *gt, >> long timeout) >> if (remaining_timeout < 0) >> remaining_timeout = 0; >> >> - return intel_uc_wait_for_idle(>->uc, remaining_timeout); >> + return intel_uc_wait_for_idle(>->uc, interruptible, >> + remaining_timeout); >> +} >> + >> +int intel_gt_wait_for_idle(struct intel_gt *gt, long timeout) >> +{ >> + return __intel_gt_wait_for_idle(gt, true, timeout); >> +} >> + >> +int intel_gt_wait_for_idle_uninterruptible(struct intel_gt *gt, long >> timeout) >> +{ >> + return __intel_gt_wait_for_idle(gt, false, timeout); >> } >> >> int intel_gt_init(struct intel_gt *gt) >> diff --git a/drivers/gpu/drm/i915/gt/intel_gt.h >> b/drivers/gpu/drm/i915/gt/intel_gt.h >> index 998ca029b73a..6c7e5415f1dc 100644 >> --- a/drivers/gpu/drm/i915/gt/intel_gt.h >> +++ b/drivers/gpu/drm/i915/gt/intel_gt.h >> @@ -143,6 +143,7 @@ void intel_gt_driver_release(struct intel_gt >> *gt); >> void intel_gt_driver_late_release_all(struct drm_i915_private >> *i915); >> >> int intel_gt_wait_for_idle(struct intel_gt *gt, long timeout); >> +int intel_gt_wait_for_idle_uninterruptible(struct intel_gt *gt, long >> timeout); >> >> void intel_gt_check_and_clear_faults(struct intel_gt *gt); >> i915_reg_t intel_gt_perf_limit_reasons_reg(struct intel_gt *gt); >> diff --git a/drivers/gpu/drm/i915/gt/intel_gt_pm.c >> b/drivers/gpu/drm/i915/gt/intel_gt_pm.c >> index c7f59d60fac6..9fe31aa07ae8 100644 >> --- a/drivers/gpu/drm/i915/gt/intel_gt_pm.c >> +++ b/drivers/gpu/drm/i915/gt/intel_gt_pm.c >> @@ -315,7 +315,8 @@ static void wait_for_suspend(struct intel_gt *gt) >> if (!intel_gt_pm_is_awake(gt)) >> return; >> >> - if (intel_gt_wait_for_idle(gt, I915_GT_SUSPEND_IDLE_TIMEOUT) >> == -ETIME) { >> + if (intel_gt_wait_for_idle_uninterruptible(gt, >> + >> I915_GT_SUSPEND_IDLE_TIMEOUT) == -ETIME) { >> /* >> * Forcibly cancel outstanding work and leave >> * the gpu quiet. >> diff --git a/drivers/gpu/drm/i915/gt/intel_gt_requests.c >> b/drivers/gpu/drm/i915/gt/intel_gt_requests.c >> index 93298820bee2..b37e8f1d0b5c 100644 >> --- a/drivers/gpu/drm/i915/gt/intel_gt_requests.c >> +++ b/drivers/gpu/drm/i915/gt/intel_gt_requests.c >> @@ -130,8 +130,10 @@ void intel_engine_fini_retire(struct >> intel_engine_cs *engine) >> GEM_BUG_ON(engine->retire); >> } >> >> -long intel_gt_retire_requests_timeout(struct intel_gt *gt, long >> timeout, >> - long *remaining_timeout) >> +long __intel_gt_retire_requests_timeout(struct intel_gt *gt, >> + bool interruptible, >> + long timeout, >> + long *remaining_timeout) >> { >> struct intel_gt_timelines *timelines = >->timelines; >> struct intel_timeline *tl, *tn; >> @@ -159,7 +161,7 @@ long intel_gt_retire_requests_timeout(struct >> intel_gt *gt, long timeout, >> mutex_unlock(&tl->mutex); >> >> timeout = >> dma_fence_wait_timeout(fence, >> - >> true, >> + >> interruptible, >> >> timeout); >> dma_fence_put(fence); >> >> diff --git a/drivers/gpu/drm/i915/gt/intel_gt_requests.h >> b/drivers/gpu/drm/i915/gt/intel_gt_requests.h >> index d2969f68dd64..9242f85f4040 100644 >> --- a/drivers/gpu/drm/i915/gt/intel_gt_requests.h >> +++ b/drivers/gpu/drm/i915/gt/intel_gt_requests.h >> @@ -12,8 +12,17 @@ struct intel_engine_cs; >> struct intel_gt; >> struct intel_timeline; >> >> -long intel_gt_retire_requests_timeout(struct intel_gt *gt, long >> timeout, >> - long *remaining_timeout); >> +long __intel_gt_retire_requests_timeout(struct intel_gt *gt, >> + bool interruptible, >> + long timeout, >> + long *remaining_timeout); >> +static inline long >> +intel_gt_retire_requests_timeout(struct intel_gt *gt, long timeout, >> + long *remaining_timeout) >> +{ >> + return __intel_gt_retire_requests_timeout(gt, true, timeout, >> + >> remaining_timeout); >> +} >> static inline void intel_gt_retire_requests(struct intel_gt *gt) >> { >> intel_gt_retire_requests_timeout(gt, 0, NULL); >> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h >> b/drivers/gpu/drm/i915/gt/uc/intel_guc.h >> index 053780f562c1..b5f4fddf5c1d 100644 >> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h >> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h >> @@ -509,7 +509,8 @@ static inline void intel_guc_disable_msg(struct >> intel_guc *guc, u32 mask) >> spin_unlock_irq(&guc->irq_lock); >> } >> >> -int intel_guc_wait_for_idle(struct intel_guc *guc, long timeout); >> +int intel_guc_wait_for_idle(struct intel_guc *guc, bool >> interruptible, >> + long timeout); >> >> int intel_guc_deregister_done_process_msg(struct intel_guc *guc, >> const u32 *msg, u32 len); >> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c >> b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c >> index 788e59cdfac9..aa74208c2b4e 100644 >> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c >> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c >> @@ -680,14 +680,15 @@ int intel_guc_wait_for_pending_msg(struct >> intel_guc *guc, >> return (timeout < 0) ? timeout : 0; >> } >> >> -int intel_guc_wait_for_idle(struct intel_guc *guc, long timeout) >> +int intel_guc_wait_for_idle(struct intel_guc *guc, bool >> interruptible, >> + long timeout) >> { >> if (!intel_uc_uses_guc_submission(&guc_to_gt(guc)->uc)) >> return 0; >> >> return intel_guc_wait_for_pending_msg(guc, >> &guc- >> >outstanding_submission_g2h, >> - true, timeout); >> + interruptible, >> timeout); >> } >> >> static int guc_context_policy_init_v70(struct intel_context *ce, >> bool loop); >> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc.h >> b/drivers/gpu/drm/i915/gt/uc/intel_uc.h >> index 014bb7d83689..b62fe3b96b64 100644 >> --- a/drivers/gpu/drm/i915/gt/uc/intel_uc.h >> +++ b/drivers/gpu/drm/i915/gt/uc/intel_uc.h >> @@ -96,9 +96,11 @@ uc_state_checkers(gsc, gsc_uc); >> #undef uc_state_checkers >> #undef __uc_state_checker >> >> -static inline int intel_uc_wait_for_idle(struct intel_uc *uc, long >> timeout) >> +static inline int intel_uc_wait_for_idle(struct intel_uc *uc, >> + bool interruptible, >> + long timeout) >> { >> - return intel_guc_wait_for_idle(&uc->guc, timeout); >> + return intel_guc_wait_for_idle(&uc->guc, interruptible, >> timeout); >> } >> >> #define intel_uc_ops_function(_NAME, _OPS, _TYPE, _RET) \ -- Jani Nikula, Intel
