summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-15 17:50:52 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-15 17:50:52 -0400
commitde91d640757afb40b1f78d659399a5cff3e540c6 (patch)
treec1df846086ead6aacf11f369eafcf1a6b835526d /kernel
parentecfb1278086f98161402f93bca8d3df602fbcb45 (diff)
downloadopen-iov-de91d640757afb40b1f78d659399a5cff3e540c6.tar.gz
open-iov-de91d640757afb40b1f78d659399a5cff3e540c6.zip
vgsp-gr: m156 fail-closed release gate - backend half (witness + deferred release0)
The engine's QMD/channel completion ack is a front-end retirement, not an execution witness. r155 live evidence: streams2's launch logged qmd_rel=6/ch_rel=0xc0ffee02 while the retirement wait saw '0/205 pages changed' on the initial attempt and all three outcome retries; the backend then returned success and the client read all-zero output (STREAMS bad=32768). The r152 matmul/smiley boots show the same signature. For a data-bearing launch (resolved operand pages + working bounce window): - new param gsp_phys_rmcall_gr_failclosed (default 1) forces release0 to the module semaphore (the m64 rel-mod shape), so the engine's ack cannot reach the guest's release word before an executor is witnessed; - when the retirement wait still saw zero writes after the bounded retries, the backend returns -EIO instead of success and logs 'FAIL-CLOSED: ... success withheld', so the sema service can withhold the guest release and fail loudly (companion commit). Set gsp_phys_rmcall_gr_failclosed=0 to restore the historical fail-open behavior. Build: make -j8 (kernel), green.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_phys_booter.c55
-rw-r--r--kernel/gsp_phys_gpu.c38
-rw-r--r--kernel/gsp_phys_gpu.h4
3 files changed, 97 insertions, 0 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 9eb1179..0c27853 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -16976,6 +16976,16 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
char qmdwin_note[32] = "";
bool rearmed = false;
u32 attempts = 0;
+ /*
+ * m156 fail-closed gate: for a data-bearing launch (resolved operand
+ * pages plus the working bounce window) release0 is routed to the
+ * module semaphore so the engine's completion ack cannot reach the
+ * guest's release word, and success is withheld when the retirement
+ * wait still observed zero engine writes. See the param comment in
+ * gsp_phys_gpu.c.
+ */
+ bool fail_closed = false;
+ u32 writes_seen = 0;
int ret = 0;
if (out)
@@ -17050,6 +17060,15 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
qmd[4] |= 1u << 12;
/*
+ * m156: a data-bearing launch whose resolved pages can actually be
+ * witnessed (the bounce window is active) is subject to the
+ * fail-closed release gate.
+ */
+ if (gsp_phys_rmcall_gr_failclosed_value() && req->n_data &&
+ boot_stage.rmcall_gr_bounce.pages)
+ fail_closed = true;
+
+ /*
* QMD release0: when the launch has a one-word absolute release and
* the caller resolved the guest release page, author it there through
* a fresh shadow line so the guest observes its own QMD release; else
@@ -17062,6 +17081,21 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
u32 one_word = (qmd[24] >> 31) & 1;
bool rel_mod = gsp_phys_rmcall_gr_rel_mod_value() != 0;
+ /*
+ * m156: force the module-semaphore release for a data-bearing
+ * launch under the fail-closed gate. The engine authors the
+ * ack into the module sem (host-visible only); the guest
+ * release word is authored later by the sema service, and
+ * only when the backend witnessed engine writes. Without
+ * this the engine's retirement ack lands in the guest's
+ * release word even when no executor produced data, and the
+ * client reads stale/zero results as success (r155 live:
+ * streams2 bad=32768 with "0/205 pages changed" on every
+ * attempt).
+ */
+ if (fail_closed)
+ rel_mod = true;
+
if (!rel_mod && req->rel_hpa && one_word && !red_en &&
n_shadow < GSP_PHYS_RMCALL_GR_SHADOW_PAGES) {
u64 sh_va = 0;
@@ -18388,6 +18422,7 @@ gr_page_unmapped:
if (req->n_data && boot_stage.rmcall_gr_bounce.pages) {
u32 wrote = gsp_phys_gr_output_retire_wait(req, bounce_csum_in);
+ writes_seen = wrote;
if (gsp_phys_rmcall_gr_nowrite_retry_value()) {
while (attempts < GSP_PHYS_RMCALL_GR_ATTEMPTS) {
if (wrote > 0)
@@ -18447,6 +18482,7 @@ gr_page_unmapped:
}
wrote = gsp_phys_gr_output_retire_wait(
req, bounce_csum_in);
+ writes_seen = wrote;
pr_info("vgsp-gr: outcome retry %u done: engine wrote %u page(s)\n",
attempts, wrote);
}
@@ -18536,6 +18572,25 @@ gr_page_unmapped:
wrote, changed, chg[0], chg[1], chg[2], chg[3], chg[4],
chg[5], chg[6], chg[7]);
}
+ /*
+ * m156 fail-closed: a data-bearing launch that acked (qmd_rel and
+ * ch_rel observed) but whose retirement wait saw no engine write on
+ * any attempt must not be reported as success. The completion ack is
+ * a front-end retirement, not an execution witness (r155 live:
+ * streams2 read all-zero output after exactly this signature, while
+ * this function returned 0 and the client's release advanced). Return
+ * an error so the sema service withholds the guest release and
+ * delivers a channel error; release0 was routed to the module
+ * semaphore above, so the engine's ack never reached the guest word.
+ */
+ if (fail_closed && writes_seen == 0) {
+ pr_err("vgsp-gr: FAIL-CLOSED: data-bearing launch acked (qmd_rel=0x%08x ch_rel=0x%08x) but the engine wrote 0 of %u resolved pages after %u attempt(s) (elapsed_ms=%u); success withheld (rc=-EIO)\n",
+ w0, w1, req->n_data, attempts + 1u,
+ jiffies_to_msecs(jiffies - start));
+ poll = w1;
+ ret = -EIO;
+ goto out;
+ }
poll = w1;
if (bar0)
pr_info("vgsp-gr: SKED readback (ok): HWW_ESR=0x%08x INFO2=0x%08x CWD_T0=0x%08x CWD_T1=0x%08x CWD_T2=0x%08x\n",
diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c
index 9b7235b..72884b8 100644
--- a/kernel/gsp_phys_gpu.c
+++ b/kernel/gsp_phys_gpu.c
@@ -974,6 +974,44 @@ int gsp_phys_rmcall_gr_nowrite_retry_value(void)
EXPORT_SYMBOL_GPL(gsp_phys_rmcall_gr_nowrite_retry_value);
/*
+ * m156 fail-closed release gate (gsp_phys_rmcall_gr_failclosed; default 1).
+ *
+ * The engine's QMD/channel completion ack is a front-end retirement, not an
+ * execution witness: the r155 live log shows launches reporting
+ * qmd_rel=6/ch_rel=0xc0ffee02 with the retirement wait observing
+ * "0/205 pages changed" on the initial attempt and all three outcome
+ * retries, after which the backend returned success and the client
+ * (streams2) read all-zero output (STREAMS bad=32768) while the soft
+ * release machinery also authored the completion for never-attempted
+ * captures. For a data-bearing launch (resolved operand pages exist) the
+ * completion release must therefore be host-owned and gated on an observed
+ * engine write:
+ *
+ * - release0 is routed to the module semaphore (the m64 rel-mod shape) so
+ * the engine's ack cannot reach the guest's release word;
+ * - the backend returns an error (not success) when the retirement wait
+ * still saw zero writes after the bounded retries, so the sema service
+ * holds the guest release instead of authoring it;
+ * - the sema service delivers a channel error for the withheld release so
+ * the client observes a launch failure, never stale/zero data as
+ * success.
+ *
+ * Set to 0 to restore the historical fail-open behavior (engine-authored
+ * release, success returned regardless of writes).
+ */
+static int gsp_phys_rmcall_gr_failclosed_param = 1;
+module_param_named(gsp_phys_rmcall_gr_failclosed,
+ gsp_phys_rmcall_gr_failclosed_param, int, 0644);
+MODULE_PARM_DESC(gsp_phys_rmcall_gr_failclosed,
+ "Withhold a data-bearing forwarded GR launch's completion release unless the engine wrote data (default 1 = fail closed)");
+
+int gsp_phys_rmcall_gr_failclosed_value(void)
+{
+ return gsp_phys_rmcall_gr_failclosed_param;
+}
+EXPORT_SYMBOL_GPL(gsp_phys_rmcall_gr_failclosed_value);
+
+/*
* m69 window-0 shadow pool (gsp_phys_rmcall_gr_shadow_win0; default 0). When
* set, the forwarded GR launch copies the QMD's program/param pages into the
* window-0 shadow pool (module pages at VAs 0x108000..0x10f000, PTEs planted
diff --git a/kernel/gsp_phys_gpu.h b/kernel/gsp_phys_gpu.h
index 73b0822..d6981e9 100644
--- a/kernel/gsp_phys_gpu.h
+++ b/kernel/gsp_phys_gpu.h
@@ -369,6 +369,10 @@ int gsp_phys_rmcall_gr_skedrec_value(void);
* wrong-place engine writes (default 1 = enabled). */
int gsp_phys_rmcall_gr_nowrite_retry_value(void);
+/* m156: withhold a data-bearing forwarded GR launch's completion release
+ * unless the retirement wait observed engine writes (default 1 = enabled). */
+int gsp_phys_rmcall_gr_failclosed_value(void);
+
/* m73: use host-visible sysmem chunks for V7 instance/RAMFC (m62 shape). */
int gsp_phys_rmcall_gr_inst_sys_value(void);