diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-23 16:24:50 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-23 16:24:50 -0400 |
| commit | 8cbb6003478d3a8ba1ee063dd1d207e252076a5d (patch) | |
| tree | 306a61dca6e46746655d98f5e3038ff49f3d1ff4 | |
| parent | cc60d6dcce1c6ee956c43a5c6d2bb639660ac5ec (diff) | |
| download | open-iov-8cbb6003478d3a8ba1ee063dd1d207e252076a5d.tar.gz open-iov-8cbb6003478d3a8ba1ee063dd1d207e252076a5d.zip | |
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'.
| -rw-r--r-- | kernel/gsp_sema_service.c | 36 |
1 files 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) |
