Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Raag Jadav" <raag.jadav@intel.com>, intel-xe@lists.freedesktop.org
Subject: Re: [PATCH v11 1/9] drm/xe/gt: Use GT ordered workqueue for wedging
Date: Sat, 17 Oct 2026 04:24:51 +0000	[thread overview]
Message-ID: <sashiko-outbox-175288@kernel.org> (raw)
In-Reply-To: <20261017034842.67054-2-raag.jadav@intel.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] drm/xe/gt: deferring wedge in xe_gt_declare_wedged() behind TDR on ordered_wq
- [High] drm/xe/gt: gt_wedge_worker() disabled after GuC and engine devm teardown
- [Medium] drm/xe/gt: missed GT wedge when xe_device_declare_wedged() races with xe_gt_init()
--

commit 7ffca021de25c270cc3b55226256fb5d40b620c4
Author: Raag Jadav <raag.jadav@intel.com>

drm/xe/gt: Use GT ordered workqueue for wedging

This commit defers GT wedge handling to a worker queued on gt->ordered_wq
and introduces for_each_initialized_gt() so xe_device_declare_wedged() only
wedges initialized GTs.

> diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c
> index bc9bf6f0b5a79..f084efbb1fcf6 100644
> --- a/drivers/gpu/drm/xe/xe_gt.c
> +++ b/drivers/gpu/drm/xe/xe_gt.c
[ ... ]
> @@ -724,6 +726,9 @@ static void xe_gt_fini(void *arg)
>  	struct xe_gt *gt = arg;
>  	int i;
>  
> +	if (disable_work_sync(&gt->wedge.worker))
> +		xe_pm_runtime_put(gt_to_xe(gt));
> +
>  	gt_reset_worker_disable(gt);
>  
>  	for (i = 0; i < XE_ENGINE_CLASS_MAX; ++i)

[Severity: High]
Does disabling gt->wedge.worker in xe_gt_fini() happen too late during
devres teardown?

In xe_gt_init(), xe_gt_fini() is registered via devm_add_action_or_reset()
before gt_init_with_gt_forcewake() and gt_init_with_all_forcewake():

drivers/gpu/drm/xe/xe_gt.c:xe_gt_init() {
	...
	err = devm_add_action_or_reset(gt_to_xe(gt)->drm.dev, xe_gt_fini, gt);
	if (err)
		return err;
	...
	err = gt_init_with_gt_forcewake(gt);
	...
	err = gt_init_with_all_forcewake(gt);
}

Inside gt_init_with_all_forcewake(), gt_reset_worker_disable() is
registered last so that devres drains gt->reset.worker before tearing down
GuC submission and hardware state:

drivers/gpu/drm/xe/xe_gt.c:gt_init_with_all_forcewake() {
	...
	/* Last action registered here so devres drains resets before teardown */
	err = devm_add_action_or_reset(xe->drm.dev, gt_reset_worker_disable, gt);
	if (err)
		return err;
}

Because devres runs cleanup actions in reverse registration order,
disable_work_sync(&gt->wedge.worker) in xe_gt_fini() runs after
gt_reset_worker_disable(), guc_submit_fini(), guc_action_disable_ct(),
hw_engine_fini(), and guc_fini_hw().

If gt->wedge.worker is pending when teardown begins (or is queued by a
failing gt_reset_worker() while gt_reset_worker_disable() drains it),
gt_wedge_worker() can run concurrently with or after guc_submit_fini() and
guc_fini_hw().

In XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET, xe_guc_submit_wedge() can then
acquire references on queues in guc->submission_state.exec_queue_lookup
after guc_submit_fini() has already finished its loop dropping wedged queue
references, leaking those xe_exec_queue references and triggering the
xa_empty(&guc->submission_state.exec_queue_lookup) assert in
guc_submit_sw_fini():

drivers/gpu/drm/xe/xe_guc_submit.c:xe_guc_submit_wedge() {
	...
	if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) {
		mutex_lock(&guc->submission_state.lock);
		xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
			if (xe_exec_queue_get_unless_zero(q))
				set_exec_queue_wedged(q);
		mutex_unlock(&guc->submission_state.lock);
	}
}

In XE_WEDGED_MODE_UPON_CRITICAL_ERROR, gt_wedge_worker() can also access
GuC CT and submission state after guc_action_disable_ct(),
guc_submit_fini(), and guc_fini_hw() have already torn them down.

Should gt->wedge.worker be disabled alongside gt->reset.worker in
gt_reset_worker_disable() before GuC and engine teardown?

> @@ -784,6 +789,8 @@ int xe_gt_init(struct xe_gt *gt)
>  			return err;
>  	}
>  
> +	/* Must be last */
> +	WRITE_ONCE(gt->initialized, true);
>  	return 0;
>  }
>  

[Severity: Medium]
What happens if xe_device_declare_wedged() is called while xe_gt_init() is
still in progress?

During xe_gt_init(), gt_init_with_gt_forcewake() and
gt_init_with_all_forcewake() initialize GuC submission, create the
migration and default-LRC kernel exec queues, enable GuC CT communication,
and start async GSC work before gt->initialized is set to true at the end
of xe_gt_init(). Hardware interrupts are also already installed earlier in
xe_device_probe(), and on multi-GT devices earlier GTs may already be
initialized and active while a subsequent GT is inside xe_gt_init().

If xe_device_declare_wedged() runs during this window (for example from
mert_handle_cat_error(), csc_hw_error_work(), gsc_work(), or a timed-out
kernel job), for_each_initialized_gt() in xe_device_declare_wedged() sees
READ_ONCE(gt->initialized) == false and skips xe_gt_declare_wedged(gt):

drivers/gpu/drm/xe/xe_device.c:xe_device_declare_wedged() {
	...
	for_each_initialized_gt(gt, xe, id)
		xe_gt_declare_wedged(gt);
}

When xe_gt_init() then finishes and sets WRITE_ONCE(gt->initialized, true)
without checking xe_device_wedged(xe), could that GT miss
xe_gt_declare_wedged() entirely, leaving its GuC CT enabled, GuC firmware
running, and kernel exec queues unwedged?

> @@ -1204,6 +1211,15 @@ struct xe_hw_engine *xe_gt_any_hw_engine(struct xe_gt *gt)
>  	return NULL;
>  }
>  
> +static void gt_wedge_worker(struct work_struct *w)
> +{
> +	struct xe_gt *gt = container_of(w, typeof(*gt), wedge.worker);
> +
> +	xe_uc_declare_wedged(&gt->uc);
> +	xe_tlb_inval_reset(&gt->tlb_inval);
> +	xe_pm_runtime_put(gt_to_xe(gt));
> +}
> +
>  /**
>   * xe_gt_declare_wedged() - Declare GT wedged
>   * @gt: the GT object
> @@ -1213,8 +1229,11 @@ struct xe_hw_engine *xe_gt_any_hw_engine(struct xe_gt *gt)
>   */
>  void xe_gt_declare_wedged(struct xe_gt *gt)
>  {
> -	xe_gt_assert(gt, gt_to_xe(gt)->wedged.mode);
> +	struct xe_device *xe = gt_to_xe(gt);
>  
> -	xe_uc_declare_wedged(&gt->uc);
> -	xe_tlb_inval_reset(&gt->tlb_inval);
> +	xe_gt_assert(gt, xe->wedged.mode);
> +
> +	xe_pm_runtime_get_noresume(xe);
> +	if (!queue_work(gt->ordered_wq, &gt->wedge.worker))
> +		xe_pm_runtime_put(xe);
>  }

[Severity: High]
Can queueing gt->wedge.worker onto gt->ordered_wq break TDR handling in
guc_exec_queue_timedout_job(), since TDR also runs on gt->ordered_wq?

In guc_exec_queue_init(), every GuC exec queue's DRM scheduler sets
timeout_wq to gt->ordered_wq, which is a single-threaded ordered workqueue.
When guc_exec_queue_timedout_job() runs on gt->ordered_wq, any work queued
onto gt->ordered_wq cannot execute until guc_exec_queue_timedout_job() (and
any other pending TDR items ahead of it) finishes.

In XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET, guc_exec_queue_timedout_job()
calls guc_submit_hint_wedged() -> xe_device_declare_wedged() ->
xe_gt_declare_wedged():

drivers/gpu/drm/xe/xe_guc_submit.c:guc_submit_hint_wedged() {
	...
	if (xe->wedged.mode != XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)
		return false;

	if (xe_device_wedged(xe))
		return true;

	xe_device_declare_wedged(xe);

	return true;
}

Previously, xe_gt_declare_wedged() synchronously ran xe_uc_declare_wedged()
-> xe_guc_declare_wedged() -> xe_guc_submit_wedge(), which stopped GuC CT,
took a reference on every registered queue via
xe_exec_queue_get_unless_zero(q), and marked each queue with
set_exec_queue_wedged(q) before returning to guc_exec_queue_timedout_job().

With gt_wedge_worker() queued behind guc_exec_queue_timedout_job() on
gt->ordered_wq, guc_exec_queue_timedout_job() proceeds to signal job fences
and call xe_sched_submission_start(sched) and
xe_guc_exec_queue_trigger_cleanup(q) before xe_guc_submit_wedge() has run:

drivers/gpu/drm/xe/xe_guc_submit.c:guc_exec_queue_timedout_job() {
	...
	if (!exec_queue_killed(q))
		wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
	else
		wedged = xe_device_wedged(xe);
	...
	} else {
		xe_sched_submission_start(sched);
		xe_guc_exec_queue_trigger_cleanup(q);
	}
}

Because sched->base.submit_wq is a separate workqueue from gt->ordered_wq,
__guc_exec_queue_process_msg_cleanup() and job freeing can run on another
CPU before gt_wedge_worker() runs on gt->ordered_wq:

drivers/gpu/drm/xe/xe_guc_submit.c:__guc_exec_queue_process_msg_cleanup() {
	...
	/* A wedged GuC won't answer the H2G, so tear down on the driver side. */
	if (bound && !exec_queue_wedged(q) && exec_queue_registered(q) &&
	    xe_uc_fw_is_running(&guc->fw))
		disable_scheduling_deregister(guc, q);
	else
		guc_exec_queue_destroy_async(q);
}

Since exec_queue_wedged(q) is still false and xe_uc_fw_is_running(&guc->fw)
is still true, won't __guc_exec_queue_process_msg_cleanup() call
disable_scheduling_deregister() and send H2G messages to the wedged GuC
instead of guc_exec_queue_destroy_async(), and allow q->refcount to drop to
zero before xe_guc_submit_wedge() can take its preservation reference?

Similarly, in XE_WEDGED_MODE_UPON_CRITICAL_ERROR, unlike
xe_gt_reset_async() (which calls xe_uc_reset_prepare(&gt->uc) synchronously
before queueing gt->reset.worker so guc->submission_state.stopped wakes
any waiters on guc->ct.wq), xe_gt_declare_wedged() defers
xe_guc_reset_prepare() into gt_wedge_worker().

If xe_device_declare_wedged() is called while guc_exec_queue_timedout_job()
is running or queued ahead of gt->wedge.worker on gt->ordered_wq, won't
guc_exec_queue_timedout_job() block gt_wedge_worker() from setting
guc->submission_state.stopped, causing TDR to either stall for 5 seconds in
wait_event_timeout(guc->ct.wq, ..., HZ * 5) and trigger a spurious GT
reset, or deadlock in xe_sched_submission_stop() ->
cancel_work_sync(&sched->work_process_msg) if
__guc_exec_queue_process_msg_suspend() is sleeping in
wait_event(guc->ct.wq, ...) waiting for xe_guc_read_stopped(guc)?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261017034842.67054-1-raag.jadav@intel.com?part=1

  reply	other threads:[~2026-10-17  4:25 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-17  3:47 [PATCH v11 0/9] Introduce xe_wedge Raag Jadav
2026-10-17  3:47 ` [PATCH v11 1/9] drm/xe/gt: Use GT ordered workqueue for wedging Raag Jadav
2026-10-17  4:24   ` sashiko-bot [this message]
2026-10-17  3:47 ` [PATCH v11 2/9] drm/xe: Drop xe_device_set_wedged_method() Raag Jadav
2026-10-17  3:47 ` [PATCH v11 3/9] drm/xe/hw_error: Guard csc_hw_error_work() with runtime PM Raag Jadav
2026-10-17  3:47 ` [PATCH v11 4/9] drm/xe: Move xe_device_wedged_fini() registration to xe_device_probe_early() Raag Jadav
2026-10-17  3:47 ` [PATCH v11 5/9] drm/xe: Make xe_device_declare_wedged() IRQ safe Raag Jadav
2026-10-17  3:47 ` [PATCH v11 6/9] drm/xe/pci_error: Flush GT wedge worker before bus reset Raag Jadav
2026-10-17  3:47 ` [PATCH v11 7/9] drm/xe/amc: Guard xe_amc_work() with runtime PM Raag Jadav
2026-10-17  3:47 ` [PATCH v11 8/9] drm/xe: Introduce xe_wedge Raag Jadav
2026-10-17  3:47 ` [PATCH v11 9/9] drm/xe/ras: Move xe_ras_process_errors() to xe_ras Raag Jadav
2026-10-17  4:00   ` sashiko-bot
2026-10-17  4:05 ` ✗ CI.checkpatch: warning for Introduce xe_wedge (rev10) Patchwork
2026-10-17  4:07 ` ✓ CI.KUnit: success " Patchwork
2026-10-17  4:46 ` ✓ Xe.CI.BAT: " Patchwork
2026-10-17  6:34 ` ✓ Xe.CI.FULL: " Patchwork

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-175288@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=raag.jadav@intel.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox