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/gsp_phys_booter.c | |
| 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/gsp_phys_booter.c')
| -rw-r--r-- | kernel/gsp_phys_booter.c | 55 |
1 files changed, 55 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", |
