From 8cbb6003478d3a8ba1ee063dd1d207e252076a5d Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Fri, 23 Oct 2026 16:24:50 -0400 Subject: vgsp-sema r563: stall-watchdog self-heal (forced consume after 20 s of no progress) Every remaining frozen-pump shape (r562: STALL-DUMP no entry consumed for 20 s, held=99 opdef=150/11/3 frozen, pool_dual=0 conflicts=1) has the same end state: some hold condition keeps winning and the walk breaks on the same entry forever, whichever hole was closed last (r548..r558 each closed one). Add the single backstop that cannot be out-run: when the r539 stall watchdog fires (20 s without consuming a GPFIFO entry while withheld work exists), arm a budget of 4 forced consumes; the next hold decisions that would park an entry are bypassed (opdef hold, no-capture hold, busy-defer re-hold, data-stalled hold), the entry is consumed with its withheld releases applied forward-only and the GR QMD completion authored by the existing tail, and the client's wait advances. Bounded by the dump cadence (<=4/boot) and the per-arming budget, so a healthy boot never sees it; each bypass logs one STALL-HEAL line. This turns 'the client stalls forever' into 'the client completes with the best-effort data the guards would have fallen back to anyway'. --- kernel/gsp_sema_service.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 127f995..0f89f5a 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -1615,6 +1615,7 @@ static u32 svc_cand_ovw_log; /* C9: candidate overwrite witness */ static u32 svc_inline_recs_log; /* C10: inline-recs digest witness */ static u32 svc_gr_await_log; /* r351: launch-held-for-trust witness */ static u32 svc_gr_retry_log; /* r351: retry-forward/proceed witness */ +static bool svc_hold_bypass(void); /* r563: stall self-heal forward decl */ /* * r551: releases DELIVERED by the opdef hold-expiry paths (flush/sweep) are @@ -22670,6 +22671,7 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) if (svc.op_tr_pending && !svc_gr_hold_bound_expired(ch, svc_gr_launch_va(ch)) && + !svc_hold_bypass() && svc_opdef_hold(vgpu, ch, (u32)idx, va)) { @@ -22829,7 +22831,8 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) ch); if (fwd_guest_queued) { svc.fwd_gr_defer++; - } else if (!svc_gr_hold_bound_expired(ch, + } else if (!svc_hold_bypass() && + !svc_gr_hold_bound_expired(ch, svc_gr_launch_va(ch)) && (ch->gr_hold_idx != (u32)idx || time_before(jiffies, @@ -22923,6 +22926,7 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) if (!fwd_guest_queued && !ch->engine_type && svc_gr_data_stalled && + !svc_hold_bypass() && !svc_gr_hold_bound_expired(ch, svc_gr_launch_va(ch)) && (ch->gr_hold_idx != (u32)idx || @@ -23231,7 +23235,8 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) time_before(jiffies, ch->gr_nocap_since + msecs_to_jiffies( - gsp_sema_gr_nocap_hold_ms))) { + gsp_sema_gr_nocap_hold_ms)) && + !svc_hold_bypass()) { /* Force a fresh capture on * the retry: a replay decode * would suppress it. */ @@ -24977,6 +24982,31 @@ static unsigned long svc_stall_watch_last; static u64 svc_stall_watch_done; static u32 svc_stall_watch_dumps; +/* + * r563: stall-watchdog self-heal. The r562 STALL-DUMP shape (pump alive, + * no entry consumed for 20 s, held/opdef counters frozen) has been closed + * one hole at a time (r548..r558), but every remaining interaction hole + * has the same end state: some hold condition keeps winning and the walk + * breaks on the same entry forever. When the watchdog fires, arm a small + * budget of forced consumes: the next hold decisions that would otherwise + * park an entry are bypassed, the entry is consumed with its withheld + * releases applied forward-only (and the GR QMD completion authored by the + * existing tail), and the client's wait advances. Bounded by the dump + * cadence (<= 4 per boot) and by the budget (4 hold bypasses per arming), + * so a healthy boot never sees it and a stalled one cannot livelock. + */ +static u32 svc_force_consume; + +static inline bool svc_hold_bypass(void) +{ + if (!svc_force_consume) + return false; + svc_force_consume--; + pr_info_ratelimited("vgsp-sema: STALL-HEAL: hold bypassed (forced consume, budget left %u)\n", + svc_force_consume); + return true; +} + static void svc_stall_watch_dump(void) { u32 i; @@ -25035,6 +25065,8 @@ static void svc_stall_watch_check(void) return; /* idle boot, not a stall */ svc_stall_watch_dumps++; svc_stall_watch_dump(); + /* r563: self-heal after the dump (see svc_hold_bypass). */ + svc_force_consume = 4; } static int svc_thread(void *arg) -- cgit v1.2.3