summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/gsp_sema_service.c159
1 files changed, 149 insertions, 10 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index f8925df..373da64 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -816,6 +816,13 @@ struct sema_svc {
u64 fwd_gr_fault;
u64 fwd_gr_bridge;
/*
+ * m156 fail-closed releases: data-bearing forwarded GR launches whose
+ * completion release was withheld because no executor was witnessed
+ * (the engine acked but wrote nothing), with a channel error
+ * delivered instead. Counts the gate firing, not backend failures.
+ */
+ u64 fwd_gr_failclosed;
+ /*
* GR launch completion releases (the QMD-level semaphore the physical
* engine authors on success) that the emulation had to write itself
* because the hardware copy did not deliver them (forward timeout,
@@ -3507,15 +3514,15 @@ static void svc_rebuild(struct nv_vgpu_device *vgpu)
svc.fwd_rel_fallback || svc.fwd_rel_pending ||
svc.fwd_rel_overflow || svc.fwd_hold_engaged ||
svc.fwd_hold_timeouts || svc.fwd_hold_canceled ||
- svc.fwd_gr_sw_rel)
- pr_info("sema-svc: work_guard deferred=%llu applied=%llu fallback=%llu pending=%llu overflow=%llu engaged=%llu timeouts=%llu canceled=%llu copy_skipped=%llu copy_soft=%llu copy_fail=%llu direct=%llu gr_sw_rel=%llu\n",
+ svc.fwd_gr_sw_rel || svc.fwd_gr_failclosed)
+ pr_info("sema-svc: work_guard deferred=%llu applied=%llu fallback=%llu pending=%llu overflow=%llu engaged=%llu timeouts=%llu canceled=%llu copy_skipped=%llu copy_soft=%llu copy_fail=%llu direct=%llu gr_sw_rel=%llu gr_failclosed=%llu\n",
svc.fwd_rel_deferred, svc.fwd_rel_applied,
svc.fwd_rel_fallback, svc.fwd_rel_pending,
svc.fwd_rel_overflow, svc.fwd_hold_engaged,
svc.fwd_hold_timeouts, svc.fwd_hold_canceled,
svc.fwd_copy_skipped, svc.fwd_copy_soft,
svc.fwd_copy_fail, svc.rel_hold_direct,
- svc.fwd_gr_sw_rel);
+ svc.fwd_gr_sw_rel, svc.fwd_gr_failclosed);
/* show a few entries so mappings can be verified from dmesg */
if (gsp_sema_dbg) {
u32 shown;
@@ -10907,6 +10914,17 @@ static void svc_fwd_soft_copy_inline(struct nv_vgpu_device *vgpu)
* hardware has not produced yet.
*/
#define SVC_FWD_GR_HOLD_TIMEOUT_MS 12000u
+/*
+ * m156: a data-bearing GR launch under the fail-closed gate can legitimately
+ * spend the whole cold-context recovery chain before its outcome is
+ * published (5 s initial poll + re-arm + 5 s + up to three 2 s fresh-entry
+ * retries plus the retirement waits). Expiring the hold inside that window
+ * would fail a launch that is still retrying, so the gate raises the GR
+ * hold bound to this value; the backend is itself bounded, so the work
+ * item's done always arrives before it, and the value remains the
+ * last-resort watchdog.
+ */
+#define SVC_FWD_GR_HOLD_GATED_MS 40000u
struct svc_fwd_hold {
bool active;
@@ -11416,11 +11434,98 @@ static void svc_fwd_hold_gr_release(struct nv_vgpu_device *vgpu,
svc_fwd_req.gr_rel_payload);
}
+/*
+ * m156 fail-closed witness. A data-bearing forwarded GR launch's completion
+ * release may only be authored when an executor witnessed the launch: the
+ * backend returns success only after the retirement wait observed engine
+ * writes (gsp_phys_rmcall_gr_failclosed), and the CUDA bridge owns a launch
+ * it executed. The engine's own ack (qmd_rel/ch_rel) is a front-end
+ * retirement, not an execution witness - r155 live: streams2's launch
+ * logged qmd_rel=6/ch_rel=0xc0ffee02 with "0/205 pages changed" on the
+ * initial attempt and all three outcome retries, the backend returned
+ * success, and the client read all-zero output (STREAMS bad=32768). A
+ * launch with no resolved data pages has no output to lose and keeps the
+ * historical release behavior.
+ */
+static bool svc_gr_rel_witnessed(bool done)
+{
+ if (!gsp_phys_rmcall_gr_failclosed_value())
+ return true; /* gate disabled: historical behavior */
+ if (!svc_fwd_req.gr || !svc_fwd_req.gr_n_data)
+ return true;
+ /*
+ * A delivered engine fault is itself a loud failure edge (notifier +
+ * channel event); the release then wakes the client's tracker so it
+ * observes the error instead of hanging.
+ */
+ if (svc_fwd_hold.fault_delivered)
+ return true;
+ if (gsp_cuda_bridge_enabled() && svc_fwd_req.gr_bridge_done)
+ return true;
+ return done && svc_fwd_req.hw_rc == 0;
+}
+
+/*
+ * m156 fail-closed delivery. When the release is withheld because no
+ * executor was witnessed, write the channel's error notifier and post the
+ * channel event so the guest driver observes a launch failure instead of
+ * waiting forever on a word nothing will write (the ISSUE-0002 wedge). The
+ * record shape and the notifier_err latch mirror svc_fwd_fault_deliver's
+ * notifier tail; there is no decoded fault record here, so info32 carries
+ * the host-detected failure marker 0x1.
+ */
+static void svc_gr_failclosed_deliver(struct nv_vgpu_device *vgpu,
+ struct svc_channel *ch)
+{
+ u8 notif[SVC_NOTIFY_SIZE] = {0};
+ u32 info32 = 0x1u;
+ u16 info16, status = SVC_NOTIFY_ERROR_STATUS;
+ int ret;
+
+ if (!vgpu || !ch || !ch->notifier_pa || ch->notifier_err)
+ return;
+ info16 = (u16)ch->engine_type;
+ memcpy(notif + SVC_NOTIFY_INFO32_OFF, &info32, 4);
+ memcpy(notif + SVC_NOTIFY_INFO16_OFF, &info16, 2);
+ memcpy(notif + SVC_NOTIFY_STATUS_OFF, &status, 2);
+ ret = svc_write_ap(vgpu, ch->notifier_pa, ch->notifier_as, notif,
+ sizeof(notif));
+ if (ret) {
+ pr_err_ratelimited("vgsp-gr: FAIL-CLOSED notifier write failed ch=0x%x pa=0x%llx as=%u ret=%d\n",
+ ch->h_object, ch->notifier_pa,
+ ch->notifier_as, ret);
+ return;
+ }
+ ch->notifier_err = true;
+ ch->notifier_err_until = jiffies + SVC_NOTIFIER_ERR_HOLD;
+ ch->notifier_err_put = ch->put_prev;
+ svc.fwd_gr_failclosed++;
+ pr_info("vgsp-gr: FAIL-CLOSED: completion release withheld for data-bearing launch h=0x%x (no executor witnessed; guest release va=0x%llx pay=0x%x not authored); channel error delivered\n",
+ ch->h_object, svc_fwd_req.gr_rel_va,
+ svc_fwd_req.gr_rel_payload);
+ ret = gsp_rm_post_channel_event(vgpu, ch->h_object);
+ if (ret)
+ pr_info("vgsp-gr: FAIL-CLOSED channel event post failed ch=0x%x ret=%d\n",
+ ch->h_object, ret);
+}
+
static bool svc_fwd_hold_expired(void)
{
- if (svc_fwd_req.gr && !smp_load_acquire(&svc_fwd_req.done))
+ if (svc_fwd_req.gr && !smp_load_acquire(&svc_fwd_req.done)) {
+ unsigned long ms = SVC_FWD_GR_HOLD_TIMEOUT_MS;
+
+ /*
+ * m156: a data-bearing launch under the fail-closed gate must
+ * not have its hold resolved while the backend is still
+ * running its bounded recovery chain (the outcome decides
+ * whether the release is authored or withheld).
+ */
+ if (svc_fwd_req.gr_n_data > 0 &&
+ gsp_phys_rmcall_gr_failclosed_value())
+ ms = SVC_FWD_GR_HOLD_GATED_MS;
return time_after(jiffies, svc_fwd_hold.since +
- msecs_to_jiffies(SVC_FWD_GR_HOLD_TIMEOUT_MS));
+ msecs_to_jiffies(ms));
+ }
return time_after(jiffies, svc_fwd_hold.since +
msecs_to_jiffies(SVC_FWD_HOLD_TIMEOUT_MS));
}
@@ -11646,7 +11751,15 @@ static void svc_fwd_hold_expire_check(struct nv_vgpu_device *vgpu,
shim.engine_type = svc_fwd_hold.engine_type;
svc_fwd_rel_apply_all(hv, &shim, svc_fwd_hold.rel,
svc_fwd_hold.nrel);
- svc_fwd_hold_gr_release(hv, &shim);
+ /*
+ * m156: a data-bearing launch whose hold resolved with no
+ * executor witness keeps its completion release withheld and
+ * gets a channel error instead (fail closed).
+ */
+ if (svc_gr_rel_witnessed(done))
+ svc_fwd_hold_gr_release(hv, &shim);
+ else
+ svc_gr_failclosed_deliver(hv, ch);
}
if (!(done && svc_fwd_req.hw_rc == 0))
svc_fwd_hold_direct_note();
@@ -11751,7 +11864,17 @@ static bool svc_fwd_hold_poll(struct nv_vgpu_device *vgpu,
svc.fwd_relay_fallback++;
svc_fwd_rel_apply_all(hv, ch, svc_fwd_hold.rel,
svc_fwd_hold.nrel);
- svc_fwd_hold_gr_release(hv, ch);
+ /*
+ * m156 fail-closed: a data-bearing forwarded launch with no
+ * executor witness must not have its completion release
+ * authored here (the r150 big-copy / r155 streams2 class:
+ * the client would read stale/zero output as a successful
+ * kernel). Withhold the release and deliver a channel error.
+ */
+ if (svc_gr_rel_witnessed(done))
+ svc_fwd_hold_gr_release(hv, ch);
+ else
+ svc_gr_failclosed_deliver(hv, ch);
svc_fwd_hold_direct_note();
svc.fwd_rel_fallback++;
if (!done)
@@ -14316,10 +14439,26 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
* request leaves the
* authorship to the
* hardware (or its hold
- * fallback).
+ * fallback). m156: a
+ * queued data-bearing GR
+ * launch under the
+ * fail-closed gate needs
+ * the hold even when this
+ * pass recorded no release
+ * of its own - the hold's
+ * success edge is the only
+ * author of the deferred
+ * completion release and
+ * its failure edge withholds
+ * it.
*/
- if (svc_fwd_defer_armed &&
- svc_fwd_rel_n > 0) {
+ if ((fwd_guest_queued &&
+ !ch->engine_type &&
+ gsp_phys_rmcall_gr_failclosed_value() &&
+ svc_fwd_req.gr &&
+ svc_fwd_req.gr_n_data > 0) ||
+ (svc_fwd_defer_armed &&
+ svc_fwd_rel_n > 0)) {
if (fwd_guest_queued &&
!svc_fwd_hold.active) {
svc_fwd_hold_engage(vgpu,