From dec59afafa6ecae494404bd78a5c527c7603b03b Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Sun, 18 Oct 2026 15:04:35 -0400 Subject: vgsp: stalled-entry detector + opdef-hold deadline fix (GR-STALL-stale-entry) r228 witness: entry 82 of channel 0x5c000059 stayed pending with put=84 get=82 last=82 frozen for ~10 s (its 1 MiB op operand never resolved, sema-optrace rc=-2) while 'sema-opdef: hold entry=82' and 'opdef-clear caller=4 entry=82' repeated: svc_opdef_drop_stale cleared the opdef slot on every rewind replay that processed a different entry of the channel, so the 10 s deadline restarted and the hold re-armed forever (opdef_held climbing, no EXPIRED). No GR-STALL line fired because this is below the GR-publish layer: the launch never reached a capture/submit. - svc_opdef_drop_stale: only a slot whose entry was CONSUMED this put epoch (slot_done_epoch bit set by svc_replay_note) is delivered-late and dropped. A rewind re-decoding an unconsumed entry no longer resets its hold deadline, so the bounded 10 s flush/expiry is reachable again. - svc_gr_stale_wedge_check: new stale-entry leg (reason=stale-entry, entry=1) for a genuinely stalled pending entry: GR channel, put > get, position AND submissions static for the 3 s window, and no bounded owner active (no defer_since grace, no no-capture window, no live m150 hold, no opdef slot for the stuck entry). Action is the canonical unstuck: last_get = 0 + wd_nocap_put, decode releases only (forward-only, no re-capture/re-forward), once per put epoch. No residual publish/arm (m181-only rule); r220 hold guard untouched. Green safety: a green pending entry either advances submissions (consume within ms) or sets one of the bounded owners the leg gates on; the stale-entry leg therefore needs all of {put > get, 3 s position stall, 0 consumption, no owner} to fire. make kernel green. (cherry picked from commit f0789007c49c7c6e01da5643d56ceff0b2b83819) --- kernel/gsp_sema_service.c | 64 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 7 deletions(-) (limited to 'kernel') diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 57a6ba5..e57bcac 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -13295,6 +13295,21 @@ static void svc_opdef_drop_stale(struct nv_vgpu_device *vgpu, if (!e->active || e->vgpu != vgpu || e->h_object != ch->h_object || e->entry_idx == keep_idx) continue; + /* + * STALLFREE r228 (entry=82 of 0x5c000059): a slot whose entry + * the ring has NOT consumed in this put epoch is NOT stale. A + * pin-batch rewind moves last_get backwards over an unconsumed + * entry and re-decodes it; clearing the slot here restarted the + * 10 s opdef deadline on every re-walk, so the hold re-armed + * forever (log: 'opdef-clear caller=4 entry=82' + 'opdef: hold + * entry=82' repeating while the 1 MiB op's operand never + * resolved, rc=-2; put=84 get=82 last=82 frozen for 10 s). The + * epoch bit is set by svc_replay_note on consume, so only an + * entry that was truly consumed has its withheld set + * delivered-late and dropped here. + */ + if (!test_bit(e->entry_idx, ch->slot_done_epoch)) + continue; if (e->n) svc_fwd_rel_apply_all(vgpu, ch, e->rel, e->n); svc_opdef_clear(e, SVC_OPDEF_CLR_STALE); @@ -16460,8 +16475,8 @@ static void svc_userd_sweep(struct nv_vgpu_device *vgpu, struct svc_channel *ch, static void svc_gr_stale_wedge_check(struct nv_vgpu_device *vgpu, struct svc_channel *ch, u32 put, u32 get) { - bool catchup, residual, dead_reset, replay_cand; - bool fire_replay, fire_reset; + bool catchup, residual, dead_reset, replay_cand, stalled_cand; + bool fire_replay, fire_reset, fire_stall; const char *reason = ""; unsigned long waited; @@ -16507,7 +16522,27 @@ static void svc_gr_stale_wedge_check(struct nv_vgpu_device *vgpu, dead_reset = (catchup && put > 1 && !ch->wd_epoch_done && !ch->gr_pub_armed && ch->counter_last == 0 && ch->submissions >= 8); - if (!residual && !dead_reset && !replay_cand) { + /* + * STALLFREE r228: stalled PENDING entry. The producer has submitted + * work the host has not consumed (put > get) and the entry loop has + * made no progress - position AND submissions static across the + * window - while no bounded owner is active for the stuck entry: no + * unreadable-push grace (defer_since), no no-capture window, no + * opdef operand slot, no m150 hold window. That combination is a + * genuinely unexplained stall (r228: the opdef deadline kept being + * reset by rewind replays, fixed in svc_opdef_drop_stale; this leg + * is the safety net for any future variant that slips the bounded + * owners). A green channel always advances submissions (consume) or + * sets one of the bounded owners on a pending entry, so the leg + * cannot fire there. + */ + stalled_cand = (!catchup && put != get && !ch->defer_since && + !ch->gr_nocap_since && + !(ch->gr_hold_idx == (u32)ch->last_get && + time_before(jiffies, ch->gr_hold_until)) && + !svc_opdef_find(vgpu, ch->h_object, + (u32)ch->last_get)); + if (!residual && !dead_reset && !replay_cand && !stalled_cand) { ch->wd_since = 0; ch->wd_put = put; ch->wd_last_get = ch->last_get; @@ -16533,7 +16568,13 @@ static void svc_gr_stale_wedge_check(struct nv_vgpu_device *vgpu, * which the existing grace paths own. */ fire_replay = replay_cand && ch->submissions != ch->wd_sub; - if (!residual && !fire_reset && !fire_replay) { + /* + * The stalled-entry leg fires only with proof of no progress at all: + * nothing was consumed during the window (submissions static), so + * the entry is genuinely parked with no bounded owner. + */ + fire_stall = stalled_cand && ch->submissions == ch->wd_sub; + if (!residual && !fire_reset && !fire_replay && !fire_stall) { ch->wd_since = 0; return; } @@ -16560,7 +16601,13 @@ static void svc_gr_stale_wedge_check(struct nv_vgpu_device *vgpu, ch->counter_last = 0; } } - if (fire_reset) { + if (fire_reset || fire_stall) { + /* + * The canonical unstuck: rewind to slot 0 so the fresh + * session's own entries (which start there) get walked, with + * forced replay (wd_nocap_put) so the re-walk decodes releases + * only and never re-captures a launch; once per put epoch. + */ ch->wd_epoch_done = true; ch->last_get = 0; ch->wd_nocap_put = put; @@ -16581,12 +16628,15 @@ static void svc_gr_stale_wedge_check(struct nv_vgpu_device *vgpu, reason = "stale-replay"; else if (fire_reset) reason = "stale-dead-range"; + else if (fire_stall) + reason = "stale-entry"; else reason = "stale-epoch"; - 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=%u action=recover reason=%s reset=%d rewind=%d cpa=0x%llx rpa=0x%llx\n", + 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=%u action=recover reason=%s reset=%d rewind=%d entry=%d cpa=0x%llx rpa=0x%llx\n", ch->h_object, ch->gpfifo_va, (u32)ch->last_get, put, get, ch->submissions, waited, reason, - fire_reset ? 1 : 0, fire_replay ? 1 : 0, + (fire_reset || fire_stall) ? 1 : 0, + fire_replay ? 1 : 0, fire_stall ? 1 : 0, ch->counter_pa, ch->release_pa); } -- cgit v1.2.3