diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-15 17:50:52 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-15 17:50:52 -0400 |
| commit | de91d640757afb40b1f78d659399a5cff3e540c6 (patch) | |
| tree | c1df846086ead6aacf11f369eafcf1a6b835526d /kernel | |
| parent | ecfb1278086f98161402f93bca8d3df602fbcb45 (diff) | |
| download | open-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.c | 55 | ||||
| -rw-r--r-- | kernel/gsp_phys_gpu.c | 38 | ||||
| -rw-r--r-- | kernel/gsp_phys_gpu.h | 4 |
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); |
