Re: [PATCH v2 3/4] drm/xe/multi_queue: track and recover lost CGP updates across VF migration
Matthew Brost <[email protected]> Mon, 3 Aug 2026 12:19:22 -0700
| Newsgroups | org.freedesktop.lists.intel-xe |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> Reviewed-by: Matthew Brost <[email protected]> > --- > 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 >