From 57af76cd9cbe73a6406681bd8a51258849b98384 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Sun, 18 Oct 2026 15:12:04 -0400 Subject: vgsp: channel-reuse reconciliation + hint-flood stop (GR-STALL-stale-entry-chanswitch) r229a evidence: victory_mail OK -> ctaid_probe attempt 1 printed DEVICE and never completed (killed at 50 s); attempt 2 (fresh context) worked. The retained window shows mail's teardown + session flush (gen 3) then, for the whole first-attempt window, no channel registration and only the host 'completion hint rc=-2' flood (~0.5 s cadence, window reopened by the completion-apply sites). No GR lines, no stale-* lines: the first launch never reached the sema walk for the new binding. - svc_thread hint: on rc == -2 (no event context to post to) stop reopening svc.post_hint_until; the next real completion reopens it. The flood was pure noise and obscured the real state. - Channel-reuse reconciliation: svc_chan_registration marks a genuinely new binding (!same_gen) with wd_chanswitch; the channel's first pump pass drops every opdef slot bound to that handle (a dead context's withheld sets must not ride the reused channel - the r228 opdef-hold class) and, when the ring already carries content (put > 1), logs 'GR-STALL-stale-wedge ... reason=stale-entry-chanswitch'. Captures stay enabled: the fresh client's own entries may already be at slot 0, and suppressing them would lose exactly the first launch this protects; a stale launch re-walk stays bounded by gsp_phys_gr_wait_max_ms and the m181 put-drop recovery. r220 hold guard and the m181 arm rule untouched. Note: the phase2 witness for r229 was not readable at commit time (/tmp/opencode/witness-r229-phase2.log absent); r229a is boot-side and post-flush, so the attempt-1 window shows the guest sent no RM/channel traffic for ~9.5 s - the reconciliation removes the host-side reused- handle state classes; r230 will show whether the first-attempt launch now reaches the walk. make kernel green. (cherry picked from commit 21eb5bb0582aaa2409601a1a1466f7bc72841647) --- kernel/gsp_sema_service.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index e57bcac..19b2d32 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -422,6 +422,11 @@ struct svc_channel { u32 wd_nocap_put; /* force-replay (no capture) for slots below * this while the stale range is re-walked; 0 = * none */ + bool wd_chanswitch; /* STALLFREE r229: registered; first pump + * pass must reconcile the binding before the + * walk (drop dead opdef slots, treat any + * pre-existing ring content as another + * context's: release-only re-walk) */ }; /* @@ -1308,6 +1313,18 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc) ch->gr_rehold_idx = ~0u; ch->gr_nocap_idx = ~0u; /* m164: no no-capture window armed */ ch->session_gen = svc_sess_gen; /* m176 */ + /* + * STALLFREE r229: a genuinely new binding - a fresh channel or + * a same-handle rebinding across a session/context boundary + * (!same_gen) - must reconcile on its first pump pass: drop + * opdef slots a dead context left under this handle and treat + * any pre-existing ring content as that context's + * (release-only re-walk, no captures), so the first launch on + * a reused channel cannot be silently consumed from another + * context's bytes. A same-generation RM re-issue preserves + * state deliberately (m148/m176) and is not reconciled. + */ + ch->wd_chanswitch = !same_gen; if (same_gen) bitmap_copy(ch->rel_done, keep_rel_done, SVC_GPFIFO_MAX_BITS); @@ -13348,6 +13365,26 @@ static void svc_opdef_drop_gone(struct nv_vgpu_device *vgpu, } } +/* + * STALLFREE r229: drop every opdef slot bound to one channel handle. A + * context switch/teardown invalidates the handle's withheld sets - their + * waiters are gone and the releases must not ride the reused channel (the + * session flush already clears them at a session boundary; this covers a + * same-handle rebinding that races the flush). Counted as a LOSS when the + * slot still held undelivered releases. + */ +static void svc_opdef_drop_channel(struct nv_vgpu_device *vgpu, u32 h_object) +{ + int i; + + for (i = 0; i < SVC_OPDEF_SLOTS; i++) { + struct svc_opdef_ent *e = &svc_opdef[i]; + + if (e->active && e->vgpu == vgpu && e->h_object == h_object) + svc_opdef_drop_counted(e); + } +} + static void svc_opdef_reset_all(void) { int i; @@ -17033,6 +17070,34 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) */ svc_gr_stale_wedge_check(vgpu, ch, put, get); + /* + * STALLFREE r229: first-use reconciliation after a genuinely + * new binding (see svc_chan_registration). The rebinding's + * ring cursor starts at 0 (fresh sequence); drop the dead + * context's opdef slots under this handle (their waiters are + * gone and the sets must not ride the reused channel, the + * r228 opdef-hold class), and when the ring already carries + * content (put > 1) log the reconcile so the transition is + * visible. Captures stay enabled: the fresh client's own + * entries may already be at slot 0.., and suppressing them + * would lose exactly the first launch this must protect; a + * stale launch re-walk is bounded by the submit budget and + * the m181 put-drop recovery (same precedent). + */ + if (ch->wd_chanswitch) { + ch->wd_chanswitch = false; + svc_opdef_drop_channel(vgpu, ch->h_object); + if (put > 1) { + svc.gr_stale_wedge++; + pr_warn_ratelimited("vgsp-gr: GR-STALL-stale-wedge h=0x%x va=0x%llx last=%u put=%u get=%u sub=%llu waited_ms=0 action=recover reason=stale-entry-chanswitch reset=0 rewind=0 entry=0 cpa=0x%llx rpa=0x%llx\n", + ch->h_object, ch->gpfifo_va, + (u32)ch->last_get, put, get, + ch->submissions, + ch->counter_pa, + ch->release_pa); + } + } + /* * m136 (SA-STALL M2): liveness watchdog for a held CE entry. * When this channel's entry loop is not re-entered (early @@ -18802,6 +18867,7 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) ch->wd_rewind_off_until; svc.chan[k].wd_epoch_done = ch->wd_epoch_done; svc.chan[k].wd_nocap_put = ch->wd_nocap_put; + svc.chan[k].wd_chanswitch = ch->wd_chanswitch; svc.chan[k].pushpg_va = ch->pushpg_va; svc.chan[k].pushpg_n = ch->pushpg_n; svc.chan[k].content_max = ch->content_max; @@ -19778,6 +19844,16 @@ static int svc_thread(void *arg) svc.last_hint = jiffies; rc = gsp_rm_post_all_events(vgpu); + /* + * STALLFREE r229: -2 means there is no event + * context to post to (a client before its first + * registration, or a torn-down one). Stop + * reopening the hint window then - the flood is + * pure noise and hides the real state; the next + * completion reopens it. + */ + if (rc == -2) + svc.post_hint_until = 0; if (gsp_sema_dbg) pr_info_ratelimited("sema-svc: completion hint rc=%d\n", rc); -- cgit v1.2.3