From f0c8fd4ab843920e00065702c34bb383b02c601c Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Fri, 16 Oct 2026 17:07:19 -0400 Subject: vgsp: m162: retirement wait accepts partial writes only after 300 ms of stability (1024-page outputs were copied out with 2 pages written) + m161 fetch-witness logging --- kernel/gsp_phys_booter.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index 009eb4f..f54a0dc 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -17083,7 +17083,17 @@ gsp_phys_gr_output_retire_wait(const struct gsp_phys_gr_submit *req, why = ""; break; } - if (changed > 0 && changed == prev && iter >= 3u) { + /* + * m162: a partial count must be stable for ~300 ms before it + * counts as settled. The old 3-iteration (~18 ms) window + * accepted a stalled store ramp or an un-dispatched grid as + * "settled": observed 1024-page vecadd outputs copied out with + * only 2 pages written (the client then verified 32736 bad + * elements). A genuinely masked/short kernel costs at most + * 300 ms extra; a ramping engine gets the time its stores + * need. + */ + if (changed > 0 && changed == prev && iter >= 60u) { why = " (settled)"; break; } @@ -17099,6 +17109,24 @@ gsp_phys_gr_output_retire_wait(const struct gsp_phys_gr_submit *req, return changed; } +/* + * m161: hardware fetch witness. The host writes GSP_PHYS_GR_QMD_SENTINEL + * into the inline-QMD page before every deposit; the engine's QMD fetch is + * the only actor that overwrites it (the post-mortem has used this as its + * ran/not-run marker since m110). Computing it as a standalone check lets + * the outcome-retry path classify a zero-write result beyond the release + * word, which the sema service's pending-release sweep can satisfy in + * software. + */ +static bool gsp_phys_gr_qmd_fetched(const u8 *qmd_mem) +{ + if (!qmd_mem) + return false; + return get_unaligned_le32(qmd_mem + + GSP_PHYS_GR_QMD_SENTINEL_WORD * 4) != + GSP_PHYS_GR_QMD_SENTINEL; +} + /* * m153: fill the bounce window from the request's guest pages and capture a * per-page checksum baseline. Used before the first publish and again before @@ -18605,6 +18633,13 @@ gr_page_unmapped: if (req->n_data && gsp_phys_gr_data_chunk()->pages) { u32 wrote = gsp_phys_gr_output_retire_wait(req, bounce_csum_in); + if (wrote == 0) { + bool ff = gsp_phys_gr_qmd_fetched(qmd_dst_mem); + + pr_info("vgsp-gr: first-attempt witness: engine fetch %s (qmd sentinel %s)\n", + ff ? "OK" : "NONE", + ff ? "overwritten" : "intact"); + } if (gsp_phys_rmcall_gr_nowrite_retry_value()) { while (attempts < GSP_PHYS_RMCALL_GR_ATTEMPTS) { if (wrote > 0) @@ -18764,6 +18799,35 @@ gr_page_unmapped: jiffies_to_msecs(jiffies - ack_start)); } + /* + * m161: hardware fetch witness. The fresh-ack + * poll cannot distinguish the engine's release + * from the sema service's software release (its + * pending-release sweep re-applies the payload + * as soon as the word reads below it). The QMD + * page's sentinel word is written only by the + * engine's QMD fetch, so it is the one side + * effect the sweep cannot fake: intact after the + * wait = this deposit never reached the engine. + */ + if (qmd_dst_mem) { + unsigned long wdl = jiffies + + msecs_to_jiffies(1500); + bool fetched = false; + + while (time_before(jiffies, wdl)) { + if (gsp_phys_gr_qmd_fetched( + qmd_dst_mem)) { + fetched = true; + break; + } + msleep(5); + } + pr_info("vgsp-gr: outcome retry %u: engine fetch witness %s (qmd sentinel %s)\n", + attempts, + fetched ? "OK" : "NONE", + fetched ? "overwritten" : "intact"); + } wrote = gsp_phys_gr_output_retire_wait( req, bounce_csum_in); pr_info("vgsp-gr: outcome retry %u done: engine wrote %u page(s)\n", -- cgit v1.2.3