All of 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.