From a7874aa9bebcdb469fd5f0a9beb4b1b18315d1a2 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Sun, 11 Oct 2026 22:17:15 -0400 Subject: vgsp: GR data path - pre-publish bounce fill, engine-change proof, ipin-first data resolution Two fixes and one diagnostic for the all-zero-results class (r89: smiley's 4 vadd launches reported GUEST GR LAUNCH OK yet every output word read 0): - gsp_phys_booter: copy the guest data pages into the bounce window BEFORE gsp_phys_rmcall_publish_locked() rings the doorbell. The fill used to run after the kick; the engine front end starts within ~7 ms, so the kernel could read the window before it was filled. - gsp_phys_booter: per-page checksums at copy-in, compared at copy-out; the copy-out log now reports 'N changed by engine'. An all-unchanged window after a reported completion is the front-end-ran/grid-never- dispatched signature (completion semaphores are front-end authored). The first 3 data (va,hpa,reloc) triples are logged per launch. - gsp_sema_service: svc_gr_data_fill resolves data pages through svc_ipin_lookup() first (guest-truth pins), falling back to svc_resolve_va, mirroring the pool resolver order; a wrong page here silently runs the kernel on zeros. First 16 resolved (va,pa,ap) triples are logged for post-mortem comparison against the guest's own mapping. --- kernel/gsp_phys_booter.c | 91 +++++++++++++++++++++++++++++++++-------------- kernel/gsp_sema_service.c | 35 ++++++++++++++++-- 2 files changed, 97 insertions(+), 29 deletions(-) (limited to 'kernel') diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index 67092b7..72b500a 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -16486,6 +16486,15 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req, static u32 sked_sample[11]; static u32 sked_tick; static bool sked_first; + /* + * m142: per-bounce-page checksums captured at copy-in. The copy-out + * compares them to report how many pages the engine actually modified; + * an all-unchanged window after a reported completion is the + * "front-end ran, grid never dispatched / read zeros" signature that + * previously masqueraded as a successful launch. Safe as static: the + * only caller is the single-inflight ordered gsp-phys-work item. + */ + static u32 bounce_csum_in[GSP_PHYS_RMCALL_GR_MAIL_SLOT]; sked_tick = 0; sked_first = true; u32 qmd_win_pde = 0, qmd_win_installed = 0; @@ -17259,6 +17268,49 @@ gr_page_unmapped: } } + /* + * m142: copy the guest data pages into the bounce window BEFORE the + * doorbell. This block used to run after gsp_phys_rmcall_publish_locked() + * kicked the engine: the engine front end starts executing as soon as + * the doorbell rings (observed ~7 ms, not guaranteed), so the kernel + * could read the bounce window before it was filled. Filling first + * removes the race entirely; per-page checksums are kept so the + * copy-out can prove whether the engine modified the window at all. + */ + if (req->n_data && boot_stage.rmcall_gr_bounce.pages) { + u32 dcopied = 0, dfailed = 0, di; + + for (di = 0; di < req->n_data; di++) { + u32 bi = (u32)((req->data[di].reloc_va - + GSP_PHYS_RMCALL_GR_BOUNCE_VA0) >> + 12); + const u8 *src; + u8 *dst; + u32 ci = 0, csum = 0; + + if (bi >= GSP_PHYS_RMCALL_GR_MAIL_SLOT || + !req->data[di].hpa) { + dfailed++; + continue; + } + src = (const u8 *)__va(req->data[di].hpa); + dst = gsp_phys_chunk_page_va( + &boot_stage.rmcall_gr_bounce, bi); + memcpy(dst, src, GSP_PHYS_PAGE_SIZE); + for (ci = 0; ci < GSP_PHYS_PAGE_SIZE; ci += 4) + csum += get_unaligned_le32(dst + ci); + bounce_csum_in[bi] = csum; + dcopied++; + } + dma_wmb(); + pr_info("vgsp-gr: bounce copy-in: %u pages, %u skipped (pre-publish)\n", + dcopied, dfailed); + for (di = 0; di < req->n_data && di < 3; di++) + pr_info("vgsp-gr: data[%u] va=0x%llx hpa=0x%llx reloc=0x%llx\n", + di, req->data[di].va, req->data[di].hpa, + req->data[di].reloc_va); + } + ret = gsp_phys_rmcall_publish_locked(bar0, boot_stage.rmcall_va_pb, words, &sub); mutex_unlock(&gsp_phys_rm_lock); @@ -17293,27 +17345,6 @@ gr_page_unmapped: } } if (req->n_data && boot_stage.rmcall_gr_bounce.pages) { - u32 dcopied = 0, dfailed = 0, di; - - for (di = 0; di < req->n_data; di++) { - u32 bi = (u32)((req->data[di].reloc_va - - GSP_PHYS_RMCALL_GR_BOUNCE_VA0) >> - 12); - - if (bi >= GSP_PHYS_RMCALL_GR_MAIL_SLOT || - !req->data[di].hpa) { - dfailed++; - continue; - } - memcpy(gsp_phys_chunk_page_va( - &boot_stage.rmcall_gr_bounce, bi), - (u8 *)__va(req->data[di].hpa), - GSP_PHYS_PAGE_SIZE); - dcopied++; - } - dma_wmb(); - pr_info("vgsp-gr: bounce copy-in: %u pages, %u skipped\n", - dcopied, dfailed); if (gsp_phys_rmcall_gr_recommit_value()) { struct gsp_phys_rmcall_pdb_params rp; struct { @@ -17725,24 +17756,30 @@ gr_page_unmapped: * by the kernels, so copying them back is a no-op. */ if (req->n_data && boot_stage.rmcall_gr_bounce.pages) { - u32 wbi, wrote = 0; + u32 wbi, wrote = 0, changed = 0; for (wbi = 0; wbi < req->n_data; wbi++) { u32 bi = (u32)((req->data[wbi].reloc_va - GSP_PHYS_RMCALL_GR_BOUNCE_VA0) >> 12); + const u8 *src; + u32 ci, csum = 0; if (bi >= GSP_PHYS_RMCALL_GR_MAIL_SLOT || !req->data[wbi].hpa) continue; - memcpy((u8 *)__va(req->data[wbi].hpa), - gsp_phys_chunk_page_va( - &boot_stage.rmcall_gr_bounce, bi), + src = gsp_phys_chunk_page_va( + &boot_stage.rmcall_gr_bounce, bi); + for (ci = 0; ci < GSP_PHYS_PAGE_SIZE; ci += 4) + csum += get_unaligned_le32(src + ci); + if (csum != bounce_csum_in[bi]) + changed++; + memcpy((u8 *)__va(req->data[wbi].hpa), src, GSP_PHYS_PAGE_SIZE); wrote++; } - pr_info("vgsp-gr: bounce copy-out: %u pages written back\n", - wrote); + pr_info("vgsp-gr: bounce copy-out: %u pages written back, %u changed by engine\n", + wrote, changed); } poll = w1; if (bar0) diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index ee73027..ec269c5 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -12127,6 +12127,8 @@ static bool svc_gr_page_inline_covers(u64 page) * the mail message). */ #define SVC_GR_DATA_BOUNCE_MAX 100u +static u32 svc_gr_data_log; /* m142: bounded per-page attribution log */ + static void svc_gr_data_fill(struct nv_vgpu_device *vgpu) { u64 wins[16]; @@ -12237,8 +12239,26 @@ static void svc_gr_data_fill(struct nv_vgpu_device *vgpu) break; if (x < svc_fwd_req.gr_n_data) continue; /* already mapped */ - if (svc_resolve_va(vgpu, va, &pa, &ap)) - break; + /* + * m142: guest-truth injected pins first, the same order + * the pool resolver uses (svc_pool_pa_resolve). The + * learned/scanned sources can hold a stale page for a + * VA a previous client mapped (the dual-PA class proven + * on the pool semaphore, r88); a bounce built from the + * wrong page silently runs the kernel on zeros while + * every launch still reports OK. + */ + { + u64 ipa = 0; + int iap = SVC_APERTURE_SYS; + + if (!svc_ipin_lookup(va, &ipa, &iap)) { + pa = ipa; + ap = (u32)iap; + } else if (svc_resolve_va(vgpu, va, &pa, &ap)) { + break; + } + } if (svc_fwd_req.gr_n_data >= SVC_GR_DATA_BOUNCE_MAX) { pr_info("vgsp-sema: svc-gr data fill: %u candidate ptrs, %u pages resolved (reloc 0x%llx+) CAP-HIT\n", n_cand, svc_fwd_req.gr_n_data, @@ -12252,6 +12272,17 @@ static void svc_gr_data_fill(struct nv_vgpu_device *vgpu) ((u64)svc_fwd_req.gr_n_data << 12); rec->ap = (u8)svc_ap_of(ap); svc_fwd_req.gr_n_data++; + /* + * m142: bounded per-page attribution log. A wrong data + * page is invisible in the aggregate counters; these + * lines let a post-mortem compare the chosen PA against + * the guest's own mapping. + */ + if (svc_gr_data_log < 16) { + svc_gr_data_log++; + pr_info("vgsp-sema: svc-gr data page va=0x%llx -> pa=0x%llx ap=%u\n", + va, rec->pa, rec->ap); + } } (void)base; pr_info("vgsp-sema: svc-gr candidate[%u] base=0x%llx -> %u new pages (total %u)\n", -- cgit v1.2.3