From: Matthew Brost <matthew.brost@intel.com>
To: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>
Cc: <intel-xe@lists.freedesktop.org>
Subject: Re: [PATCH v2 3/4] drm/xe/multi_queue: track and recover lost CGP updates across VF migration
Date: Mon, 3 Aug 2026 12:19:22 -0700 [thread overview]
Message-ID: <anDpuohdptVIRcAG@gsse-cloud1.jf.intel.com> (raw)
In-Reply-To: <20260731232625.3313657-9-niranjana.vishwanathapura@intel.com>
On Fri, Jul 31, 2026 at 04:26:26PM -0700, Niranjana Vishwanathapura wrote:
> When a CGP_SYNC (or REGISTER_CONTEXT_MULTI_QUEUE with CGP) is in flight
> during VF migration, GuC loses the message and never sends CGP_SYNC_DONE.
> Two failure modes exist:
>
> 1. The send was already issued (CGP_SYNC_DONE not received):
> group->sync_pending stays set and cgp_update_q points to the queue
> that owns the outstanding sync.
>
> 2. The wait woke early (send not issued):
> The queue returned from xe_guc_exec_queue_group_cgp_sync() without
> sending after vf_recovery() became true.
>
> Track which kind of sync is outstanding (registering_cgp / updating_cgp)
> and the owning queue (cgp_update_q) so
> guc_exec_queue_revert_pending_state_change()
> can recover both cases:
> - A registration-time CGP bails or is lost → clear registered flag so
> run_job re-registers after unpause (re_register / registering_cgp paths).
> - A dynamic CGP update bails or is lost → set needs_cgp_sync so replay
> re-issues the update after unpause (re_update / updating_cgp paths).
>
> Tag all registration call sites with CGP_SYNC_REGISTRATION so the bail
> path distinguishes them from dynamic updates.
>
> Assisted-by: Github-Copilot:Claude-opus-4.8
> Signed-off-by: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>
Reviewed-by: Matthew Brost <matthew.brost@intel.com>
> ---
> drivers/gpu/drm/xe/xe_exec_queue_types.h | 7 ++
> drivers/gpu/drm/xe/xe_guc_exec_queue_types.h | 30 +++++
> drivers/gpu/drm/xe/xe_guc_submit.c | 112 +++++++++++++++++--
> 3 files changed, 141 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
> index 53b6c0bf4849..b2276559c2f6 100644
> --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
> +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
> @@ -70,6 +70,13 @@ struct xe_exec_queue_group {
> spinlock_t suspend_lock;
> /** @sync_pending: CGP_SYNC_DONE g2h response pending */
> bool sync_pending;
> + /**
> + * @cgp_update_q: Queue that issued the currently outstanding (sent)
> + * CGP_SYNC or REGISTER_CONTEXT_MULTI_QUEUE; NULL when none is
> + * outstanding. Used during VF recovery to identify and replay the
> + * message whose CGP_SYNC_DONE was not received.
> + */
> + struct xe_exec_queue *cgp_update_q;
> /** @banned: Group banned */
> bool banned;
> /** @stopped: Group is stopped, protected by list_lock */
> diff --git a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
> index acdc24d1a6bd..573b920edb41 100644
> --- a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
> +++ b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
> @@ -76,6 +76,36 @@ struct xe_guc_exec_queue {
> * recovery.
> */
> bool needs_resume;
> + /** @multi_queue: multi-queue group CGP state for VF post migration recovery */
> + struct {
> + /**
> + * @multi_queue.needs_cgp_sync: Needs a CGP_SYNC (dynamic CGP
> + * update) message replayed during recovery.
> + */
> + u8 needs_cgp_sync:1;
> + /**
> + * @multi_queue.re_register: A registration-time CGP update was
> + * interrupted by recovery; the queue must be re-registered.
> + */
> + u8 re_register:1;
> + /**
> + * @multi_queue.re_update: A dynamic CGP update was interrupted
> + * by recovery; the CGP update must be replayed.
> + */
> + u8 re_update:1;
> + /**
> + * @multi_queue.registering_cgp: This queue's currently
> + * outstanding CGP_SYNC is a registration (matched against
> + * group->cgp_update_q in revert).
> + */
> + u8 registering_cgp:1;
> + /**
> + * @multi_queue.updating_cgp: This queue's currently outstanding
> + * CGP_SYNC is a dynamic update (matched against
> + * group->cgp_update_q in revert).
> + */
> + u8 updating_cgp:1;
> + } multi_queue;
> };
>
> #endif
> diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
> index 13d0ab8052e5..c018bc0d8d6f 100644
> --- a/drivers/gpu/drm/xe/xe_guc_submit.c
> +++ b/drivers/gpu/drm/xe/xe_guc_submit.c
> @@ -800,9 +800,12 @@ static void xe_guc_exec_queue_group_cgp_update(struct xe_device *xe,
> }
> }
>
> +#define CGP_SYNC_REGISTRATION BIT(0)
> +
> static void xe_guc_exec_queue_group_cgp_sync(struct xe_guc *guc,
> struct xe_exec_queue *q,
> - const u32 *action, u32 len)
> + const u32 *action, u32 len,
> + unsigned int flags)
> {
> struct xe_exec_queue_group *group = q->multi_queue.group;
> struct xe_device *xe = guc_to_xe(guc);
> @@ -829,17 +832,45 @@ static void xe_guc_exec_queue_group_cgp_sync(struct xe_guc *guc,
> return;
> }
>
> + /*
> + * If woken by VF migration recovery, do not touch the CGP or send: the
> + * message would be lost and, for a registration, GuC must (re-)register
> + * the context before its CGP entry may be read. Flag the queue so revert
> + * replays it - a registration by re-registration, a dynamic update by a
> + * replayed CGP_SYNC - and bail.
> + */
> + if (vf_recovery(guc)) {
> + if (flags & CGP_SYNC_REGISTRATION)
> + q->guc->multi_queue.re_register = true;
> + else
> + q->guc->multi_queue.re_update = true;
> + return;
> + }
> +
> scoped_guard(spinlock, &q->multi_queue.lock)
> priority = q->multi_queue.priority;
>
> xe_lrc_set_multi_queue_priority(q->lrc[0], priority);
> xe_guc_exec_queue_group_cgp_update(xe, q);
>
> + /*
> + * Record the nature of this outstanding sync so revert can replay it if
> + * its CGP_SYNC_DONE is lost across a migration: a registration is
> + * recovered by re-registration, a dynamic update by a replayed CGP_SYNC.
> + */
> + if (flags & CGP_SYNC_REGISTRATION) {
> + q->guc->multi_queue.registering_cgp = true;
> + q->guc->multi_queue.updating_cgp = false;
> + } else {
> + q->guc->multi_queue.updating_cgp = true;
> + q->guc->multi_queue.registering_cgp = false;
> + }
> + WRITE_ONCE(group->cgp_update_q, q);
> WRITE_ONCE(group->sync_pending, true);
> xe_guc_ct_send(&guc->ct, action, len, G2H_LEN_DW_MULTI_QUEUE_CONTEXT, 1);
> }
>
> -static void guc_exec_queue_send_cgp_sync(struct xe_exec_queue *q)
> +static void guc_exec_queue_send_cgp_sync(struct xe_exec_queue *q, unsigned int flags)
> {
> #define MAX_MULTI_QUEUE_CGP_SYNC_SIZE (2)
> struct xe_guc *guc = exec_queue_to_guc(q);
> @@ -853,7 +884,7 @@ static void guc_exec_queue_send_cgp_sync(struct xe_exec_queue *q)
> xe_gt_assert(guc_to_gt(guc), len <= MAX_MULTI_QUEUE_CGP_SYNC_SIZE);
> #undef MAX_MULTI_QUEUE_CGP_SYNC_SIZE
>
> - xe_guc_exec_queue_group_cgp_sync(guc, q, action, len);
> + xe_guc_exec_queue_group_cgp_sync(guc, q, action, len, flags);
> }
>
> static void __register_exec_queue_group(struct xe_exec_queue *q,
> @@ -881,7 +912,8 @@ static void __register_exec_queue_group(struct xe_exec_queue *q,
> * XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE response
> * from guc.
> */
> - xe_guc_exec_queue_group_cgp_sync(guc, q, action, len);
> + xe_guc_exec_queue_group_cgp_sync(guc, q, action, len,
> + CGP_SYNC_REGISTRATION);
> }
>
> static void __register_mlrc_exec_queue(struct xe_guc *guc,
> @@ -1041,7 +1073,7 @@ static void register_exec_queue(struct xe_exec_queue *q, int ctx_type)
> init_policies(guc, q);
>
> if (xe_exec_queue_is_multi_queue_secondary(q))
> - guc_exec_queue_send_cgp_sync(q);
> + guc_exec_queue_send_cgp_sync(q, CGP_SYNC_REGISTRATION);
> }
>
> static u32 wq_space_until_wrap(struct xe_exec_queue *q)
> @@ -1923,7 +1955,7 @@ static void __guc_exec_queue_process_msg_set_multi_queue_priority(struct xe_sche
> struct xe_exec_queue *q = msg->private_data;
>
> if (guc_exec_queue_allowed_to_change_state(q))
> - guc_exec_queue_send_cgp_sync(q);
> + guc_exec_queue_send_cgp_sync(q, 0);
>
> kfree(msg);
> }
> @@ -2716,6 +2748,57 @@ static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc,
> q->guc->id);
> }
>
> + /*
> + * A registration time CGP update that bailed when woken by VF recovery.
> + * Re-register the queue.
> + */
> + if (q->guc->multi_queue.re_register) {
> + clear_exec_queue_registered(q);
> + q->guc->multi_queue.re_register = false;
> + xe_gt_dbg(guc_to_gt(guc), "Replay REGISTER (cgp) - guc_id=%d",
> + q->guc->id);
> + }
> +
> + /*
> + * If a CGP update gets dropped during migration, CGP_SYNC_DONE will not
> + * be received (sync_pending still set and this queue owns it). Recover
> + * it the same way and clear the stuck sync_pending.
> + */
> + if (xe_exec_queue_is_multi_queue(q)) {
> + struct xe_exec_queue_group *group = q->multi_queue.group;
> +
> + if (q == READ_ONCE(group->cgp_update_q) &&
> + READ_ONCE(group->sync_pending)) {
> + if (q->guc->multi_queue.registering_cgp) {
> + clear_exec_queue_registered(q);
> + xe_gt_dbg(guc_to_gt(guc), "Replay REGISTER (cgp sync) - guc_id=%d",
> + q->guc->id);
> + } else if (q->guc->multi_queue.updating_cgp) {
> + q->guc->multi_queue.needs_cgp_sync = true;
> + xe_gt_dbg(guc_to_gt(guc), "Replay CGP_SYNC - guc_id=%d",
> + q->guc->id);
> + }
> + q->guc->multi_queue.registering_cgp = false;
> + q->guc->multi_queue.updating_cgp = false;
> + WRITE_ONCE(group->cgp_update_q, NULL);
> + WRITE_ONCE(group->sync_pending, false);
> + }
> + }
> +
> + /*
> + * A dynamic-time CGP update that bailed when woken by VF recovery.
> + * Replay the dynamic CGP update unless the queue is registered or being
> + * re-registered, which re-does the CGP anyway.
> + */
> + if (q->guc->multi_queue.re_update) {
> + q->guc->multi_queue.re_update = false;
> + if (exec_queue_registered(q)) {
> + q->guc->multi_queue.needs_cgp_sync = true;
> + xe_gt_dbg(guc_to_gt(guc), "Replay CGP_SYNC (re-update) - guc_id=%d",
> + q->guc->id);
> + }
> + }
> +
> q->guc->resume_time = 0;
> }
>
> @@ -3424,7 +3507,8 @@ int xe_guc_exec_queue_cgp_context_error_handler(struct xe_guc *guc, u32 *msg,
> int xe_guc_exec_queue_cgp_sync_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
> {
> struct xe_device *xe = guc_to_xe(guc);
> - struct xe_exec_queue *q;
> + struct xe_exec_queue_group *group;
> + struct xe_exec_queue *q, *upd_q;
> u32 guc_id = msg[0];
>
> if (unlikely(len < 1)) {
> @@ -3441,8 +3525,20 @@ int xe_guc_exec_queue_cgp_sync_done_handler(struct xe_guc *guc, u32 *msg, u32 le
> return -EPROTO;
> }
>
> + /*
> + * The outstanding CGP update is now confirmed; clear the owning queue's
> + * tracking so a later migration does not needlessly replay it.
> + */
> + group = q->multi_queue.group;
> + upd_q = READ_ONCE(group->cgp_update_q);
> + if (upd_q) {
> + upd_q->guc->multi_queue.registering_cgp = false;
> + upd_q->guc->multi_queue.updating_cgp = false;
> + WRITE_ONCE(group->cgp_update_q, NULL);
> + }
> +
> /* Wakeup the serialized cgp update wait */
> - WRITE_ONCE(q->multi_queue.group->sync_pending, false);
> + WRITE_ONCE(group->sync_pending, false);
> xe_guc_ct_wake_waiters(&guc->ct);
>
> return 0;
> --
> 2.43.0
>
next prev parent reply other threads:[~2026-08-03 19:19 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-31 23:26 [PATCH v2 0/4] drm/xe/multi_queue: Handle lost message during VF migration Niranjana Vishwanathapura
2026-07-31 23:26 ` [PATCH v2 1/4] drm/xe: split VF pause into prepare and revert phases Niranjana Vishwanathapura
2026-08-03 16:55 ` Matthew Brost
2026-07-31 23:26 ` [PATCH v2 2/4] drm/xe/multi_queue: handle CGP_SYNC wait timeout during VF recovery Niranjana Vishwanathapura
2026-08-03 17:06 ` Matthew Brost
2026-07-31 23:26 ` [PATCH v2 3/4] drm/xe/multi_queue: track and recover lost CGP updates across VF migration Niranjana Vishwanathapura
2026-08-03 19:19 ` Matthew Brost [this message]
2026-07-31 23:26 ` [PATCH v2 4/4] drm/xe/multi_queue: replay dynamic CGP updates lost during " Niranjana Vishwanathapura
2026-08-03 19:20 ` Matthew Brost
2026-07-31 23:33 ` ✓ CI.KUnit: success for drm/xe/multi_queue: Handle lost message during VF migration (rev3) Patchwork
2026-08-01 0:25 ` ✓ Xe.CI.BAT: " Patchwork
2026-08-01 1:08 ` ✓ 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=anDpuohdptVIRcAG@gsse-cloud1.jf.intel.com \
--to=matthew.brost@intel.com \
--cc=intel-xe@lists.freedesktop.org \
--cc=niranjana.vishwanathapura@intel.com \
/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