From 12442327cda834a7fcb13a6e010e1a747cb9110c Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Sat, 31 Oct 2026 22:16:00 -0400 Subject: vgsp: sema_gl_mme_block_cap=2048 (blocks >~2050 words never start on engine - CONT-78 wall root cause), run-aligned prelude drain with generation-keyed cursor (3089-word program over 2 launches, latch on final part, WEDGE-RECOVER rewinds), attr prelude same cap, sema_gl_mme_nop_param_mask (parameter-conserving NOP: call-run header rewritten to CALL_MME_DATA port) - r1177: full program ships (words=3089 covered=55) + blocks 1327-2048 all retire; bisect: slot 57 PARKS (keep=5|1<<57 -> 0 retires/6 fails, grfifo frozen events=0, no poison; r1142 CALL_MME_MACRO(57) class); NOP transform fires (26 runs retargeted) but unvalidated on wedged boots; next: healthy boot validation sequence (a)keep=5 (b)+1<<57 parks (c)+nop_param=1<<57 (d)sa_stub_all=0 -> N/N stores - SESSION 153 r1173-r1179 --- kernel/gsp_phys_booter.c | 252 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 199 insertions(+), 53 deletions(-) diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index 7d861ed..da3ea83 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -17576,6 +17576,54 @@ module_param_named(sema_gl_mme_keep_mask, gsp_phys_rmcall_gr_keep_mask, MODULE_PARM_DESC(sema_gl_mme_keep_mask, "Slots whose CALL_MME_MACRO/DATA runs are never dropped/stubbed (u64 bitmask; one slot at a time for macro re-enable)"); +/* + * SESSION 153-CONTINUED-79: per-launch method-block word cap + prelude drain. + * The r1172-r1175 A/B: every beef launch whose emitted block exceeded ~2000 + * words (3083-word full-SA prelude + window, 3315-3484 words) NEVER STARTED + * on the physical engine (MME-FAST events=0, grfifo frozen, ch_rel never + * written) while every block <= ~1300 words (1002-word partial capture + + * window) executed (retires in 90-170 us when the executed macro stayed + * inside the loaded IR). The failure class is fetch-side (a stuck GPFIFO + * entry), not content: once parked, every later entry also failed until a + * poison-latch TSG re-arm reset the PBDMA. The full-SA capture (55 slots, + * 3083 words, macros that chain past the first 15 slots e.g. macro 0 -> + * IR 0x201 = slot-25 entry) therefore cannot be replayed in ONE block. + * Emit at most gsp_phys_rmcall_gr_block_cap words per launch and drain the + * (SDM-sanitized, run-aligned) prelude across consecutive launches with a + * generation-keyed cursor; the emission latch is set only when the final + * part has been emitted, so a completed program is re-requested after the + * WEDGE-RECOVER latch clear. Default 2048; 0 = legacy 4096 module buffer. + */ +static u32 gsp_phys_rmcall_gr_block_cap = 2048u; +module_param_named(sema_gl_mme_block_cap, gsp_phys_rmcall_gr_block_cap, uint, + 0644); +MODULE_PARM_DESC(sema_gl_mme_block_cap, + "Max words emitted per GR launch method block (0 = 4096 module buffer; the >2048-word blocks never start on the physical engine)"); + +/* Generation-keyed drain cursor for the split MME prelude emission. */ +static u32 gsp_phys_gr_mme_pre_emit_gen; +static u32 gsp_phys_gr_mme_pre_emit_h; +static u32 gsp_phys_gr_mme_pre_emit_off; + +/* + * SESSION 153-CONTINUED-79: parameter-conserving NOP for kept parking slots. + * The r1177 bisect: keep={0,2,57} (full 3089-word program resident, split + * emission) parks the PBDMA on the emitted block (MME-FAST events=0, grfifo + * frozen, warm sem WEDGED) while keep={0,2} retires every launch - slot 57's + * CALL_MME_MACRO(57) run is the r1142 park class. Rather than drop the run + * (which also removes its parameter words from the stream), rewrite the call + * run's header to target the CALL_MME_DATA port (method+4) so the parameter + * words are still written (conserved) but no macro executes: the trigger + * word becomes a plain port write and the PBDMA never opens a macro context + * whose parameter drain can back up. A slot named in this mask must also be + * in sema_gl_mme_keep_mask (kept = retained in the window). Default 0. + */ +static u64 gsp_phys_rmcall_gr_nop_param_mask; +module_param_named(sema_gl_mme_nop_param_mask, gsp_phys_rmcall_gr_nop_param_mask, + ullong, 0644); +MODULE_PARM_DESC(sema_gl_mme_nop_param_mask, + "Kept slots whose CALL_MME_MACRO trigger is rewritten to a plain CALL_MME_DATA port write (params conserved, macro not executed; u64 bitmask)"); + /* * The same stubbed-slot predicate the launch builder applies (see the * sa_stub emit decision): a slot is stubbed when stubbing is on and either @@ -17607,7 +17655,7 @@ static bool gsp_phys_gr_slot_stubbed(u32 j, u64 sa_mask, const u32 *sa_ent) static u32 gsp_phys_gr_mme_drop_calls(u32 *win, u32 n, u64 sa_mask, const u32 *sa_ent, u64 *dropped_mask) { - u32 r = 0, w = 0, dropped = 0; + u32 r = 0, w = 0, dropped = 0, transform = 0; u64 dmask = 0; while (r < n) { @@ -17633,8 +17681,26 @@ static u32 gsp_phys_gr_mme_drop_calls(u32 *win, u32 n, u64 sa_mask, if (m >= 0x3800u && m <= 0x39fcu) { u32 j = (m - 0x3800u) / 8u; - if (gsp_phys_gr_slot_stubbed(j, sa_mask, - sa_ent)) { + if ((READ_ONCE(gsp_phys_rmcall_gr_keep_mask) & + (1ull << j)) && + (READ_ONCE(gsp_phys_rmcall_gr_nop_param_mask) & + (1ull << j))) { + /* + * SESSION 153-CONTINUED-79: kept + + * nop-param slot - keep the run but + * retarget its header to the DATA + * port so the params still land + * (see the sema_gl_mme_nop_param_mask + * comment). + */ + u32 nm = 0x3804u + 8u * j; + + win[r] = (h & ~0xfffu) | + ((nm >> 2) & 0xfffu); + dmask |= 1ull << j; + transform++; + } else if (gsp_phys_gr_slot_stubbed(j, sa_mask, + sa_ent)) { iscall = true; dmask |= 1ull << j; break; @@ -17651,6 +17717,9 @@ static u32 gsp_phys_gr_mme_drop_calls(u32 *win, u32 n, u64 sa_mask, w += span; r += span; } + if (transform) + pr_info("vgsp-gr: MME-CALL-NOP %u call run(s) retargeted to the CALL_MME_DATA port (params conserved)\n", + transform); if (dropped_mask) *dropped_mask = dmask; return dropped; @@ -17826,8 +17895,14 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, * emission then happens on the next launch that fits. */ bool emit_prelude = false; + bool pre_done = false; const u32 *pl = NULL; - u32 pl_n = 0, pl_gen = 0; + u32 pl_n = 0, pl_gen = 0, pl_room = 0; + u32 block_cap = READ_ONCE(gsp_phys_rmcall_gr_block_cap); + + if (block_cap < 64u || + block_cap > GSP_PHYS_RMCALL_GR_METHOD_WORDS) + block_cap = GSP_PHYS_RMCALL_GR_METHOD_WORDS; /* * SESSION 153-CONTINUED-63: MME START-ADDRESS RAM completion. * The prelude capture is capped at 512 words, so the client's @@ -18025,18 +18100,64 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, * priv-errored (ILLEGAL_OPCODE -> poison). Force * re-emission on every launch while any slot is kept * (idempotent per the emission contract). + * + * SESSION 153-CONTINUED-79: the emission is drained in + * per-launch parts capped at block_cap words (see the + * sema_gl_mme_block_cap comment; oversized blocks never + * start). The latch (gsp_phys_gr_mme_prelude_gen/h) is + * set only when the final part has gone out. */ - if (pl && pl_n && pl_gen && - (READ_ONCE(gsp_phys_rmcall_gr_keep_mask) || - pl_gen != READ_ONCE(gsp_phys_gr_mme_prelude_gen) || - req->h_object != READ_ONCE(gsp_phys_gr_mme_prelude_h))) { - if (win3d_n + 2u + 8u + pl_n + sa_block <= - GSP_PHYS_RMCALL_GR_METHOD_WORDS) { - emit_prelude = true; + if (pl && pl_n && pl_gen) { + /* + * Fixed overhead: SET_OBJECT bind (2), SA-stub + * block, window, and the 4x2-word release tail. + */ + u32 fixed = 2u + sa_block + win3d_n + 8u; + + pl_room = (fixed + 24u <= block_cap) ? + block_cap - fixed : 0u; + if (pl_room > pl_n) + pl_room = pl_n; + /* + * (Re)start the drain on a new generation/ + * channel, or when the request is forced (kept + * slots; a context-wiping wedge recovery clears + * the latch) and nothing is left to drain. + */ + if (pl_gen != + READ_ONCE(gsp_phys_gr_mme_pre_emit_gen) || + req->h_object != + READ_ONCE(gsp_phys_gr_mme_pre_emit_h) || + (READ_ONCE(gsp_phys_gr_mme_pre_emit_off) >= + pl_n && + (READ_ONCE(gsp_phys_rmcall_gr_keep_mask) || + pl_gen != READ_ONCE( + gsp_phys_gr_mme_prelude_gen)))) { + WRITE_ONCE(gsp_phys_gr_mme_pre_emit_gen, + pl_gen); + WRITE_ONCE(gsp_phys_gr_mme_pre_emit_h, + req->h_object); + WRITE_ONCE(gsp_phys_gr_mme_pre_emit_off, 0); + } + if (pl_room >= 24u) { + emit_prelude = + (pl_gen != READ_ONCE( + gsp_phys_gr_mme_prelude_gen) || + req->h_object != READ_ONCE( + gsp_phys_gr_mme_prelude_h) || + READ_ONCE( + gsp_phys_gr_mme_pre_emit_off) < + pl_n); } else { - pr_info("vgsp-gr: MME-PRELUDE deferred (window=%u prelude=%u sa=%u exceeds block; replaying window alone) h=0x%x\n", - win3d_n, pl_n, sa_block, - req->h_object); + static u32 pre_defer_log; + + if (pre_defer_log < 8) { + pre_defer_log++; + pr_info("vgsp-gr: MME-PRELUDE deferred (window=%u prelude=%u sa=%u cap=%u) h=0x%x\n", + win3d_n, pl_n, sa_block, + block_cap, + req->h_object); + } } } } @@ -18046,52 +18167,55 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, (GSP_PHYS_RMCALL_GR_SET_OBJECT >> 2); *p++ = GSP_PHYS_RMCALL_CLASS_GR_3D; if (emit_prelude) { - u32 g0 = pl_gen, g1 = 0, sdrop = 0, pwords = pl_n; + u32 g0 = pl_gen, g1 = 0, sdrop = 0, pwords = 0; + u32 doff = READ_ONCE(gsp_phys_gr_mme_pre_emit_off); + u32 room = (block_cap > (u32)(p - pb) + win3d_n + 8u + + sa_block) ? + block_cap - (u32)(p - pb) - win3d_n - 8u - + sa_block : 0u; - if (READ_ONCE(gsp_phys_rmcall_gr_sdm_drop)) { - u32 r = 0, w = 0; + /* + * SESSION 153-CONTINUED-79: run-aligned drain at the + * generation cursor; whole runs only, SDM-family + * single-word entries skipped exactly like the legacy + * sanitize (see gsp_phys_rmcall_gr_sdm_drop). + */ + while (doff < pl_n && pwords + 1u <= room) { + u32 h = pl[doff]; + u32 op = (h >> 29) & 7u; + u32 cnt = (h >> 16) & 0xfffu; + u32 span = ((op == 1u || op == 3u || + op == 5u) && cnt && + doff + 1u + cnt <= pl_n) ? + 1u + cnt : 1u; - /* - * SESSION 153-CONTINUED-59: SDM sanitize (see the - * gsp_phys_rmcall_gr_sdm_drop comment). Walk the - * prelude verbatim but drop single-word SDM-family - * encodings; they are the r1102 PBENTRY offenders. - */ - while (r < pl_n) { - u32 h = pl[r]; - u32 op = (h >> 29) & 7u; - u32 cnt = (h >> 16) & 0xfffu; - u32 span = ((op == 1u || op == 3u || - op == 5u) && cnt && - r + 1u + cnt <= pl_n) ? - 1u + cnt : 1u; - - if (h && (op == 0u || op == 2u || - op == 6u)) { - sdrop++; - r += span; - continue; - } - memcpy(&p[w], &pl[r], (size_t)span * 4u); - w += span; - r += span; + if (doff + span > pl_n) + span = pl_n - doff; + if (READ_ONCE(gsp_phys_rmcall_gr_sdm_drop) && + h && (op == 0u || op == 2u || op == 6u)) { + sdrop += span; + doff += span; + continue; } - p += w; - pwords = w; - } else { - memcpy(p, pl, (size_t)pl_n * 4); - p += pl_n; + if (pwords + span > room) + break; + memcpy(p, &pl[doff], (size_t)span * 4u); + p += span; + pwords += span; + doff += span; } + WRITE_ONCE(gsp_phys_gr_mme_pre_emit_off, doff); + pre_done = (doff >= pl_n); /* seqcheck: a mid-copy append must not be recorded as * emitted (the copy may be a torn prefix). */ gsp_sema_mme_prelude_get(NULL, &g1); - if (g1 == g0) { + if (pre_done && g1 == g0) { WRITE_ONCE(gsp_phys_gr_mme_prelude_gen, g0); WRITE_ONCE(gsp_phys_gr_mme_prelude_h, req->h_object); - pr_info("vgsp-gr: MME-PRELUDE emitted gen=%u words=%u window=%u h=0x%x push=0x%llx sdm_drop=%u\n", - g0, pwords, win3d_n, req->h_object, - req->push_va, sdrop); + pr_info("vgsp-gr: MME-PRELUDE emitted gen=%u words=%u window=%u h=0x%x push=0x%llx sdm_drop=%u cap=%u\n", + g0, pl_n, win3d_n, req->h_object, + req->push_va, sdrop, block_cap); /* * SESSION 153-CONTINUED-70: one full dump of the * replayed MME context-setup program per boot @@ -18128,9 +18252,14 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, sj, sa_ent[sj], gsp_sema_mme_ir_covered(sa_ent[sj])); } + } else if (pre_done) { + pr_info("vgsp-gr: MME-PRELUDE copy raced an append (gen %u->%u); will re-emit next launch\n", + g0, g1); } else { - pr_info("vgsp-gr: MME-PRELUDE copy raced an append (gen %u->%u); emitted words=%u, will re-emit next launch\n", - g0, g1, pl_n); + pr_info("vgsp-gr: MME-PRELUDE part gen=%u off=%u words=%u/%u window=%u h=0x%x push=0x%llx sdm_drop=%u cap=%u (continuation next launch)\n", + g0, doff, pwords, pl_n, win3d_n, + req->h_object, req->push_va, sdrop, + block_cap); } } /* @@ -18237,10 +18366,20 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb, */ if (attr_pre && attr_pre_n && READ_ONCE(gsp_phys_rmcall_gr_attr_prelude)) { - u32 avail = GSP_PHYS_RMCALL_GR_METHOD_WORDS - + u32 avail = block_cap - (u32)(p - pb) - (win3d_n + 2u + 8u); u32 off, copied = 0; + /* + * SESSION 153-CONTINUED-79: the attr prelude shares + * the same per-launch block cap (see + * sema_gl_mme_block_cap); unsigned underflow would + * hand it the whole module buffer on an oversized + * window. + */ + if ((u32)(p - pb) + win3d_n + 2u + 8u >= block_cap) + avail = 0; + if (gsp_phys_gr_attr_emit_gen != READ_ONCE(gsp_phys_gr_attr_pre_gen)) { WRITE_ONCE(gsp_phys_gr_attr_emit_gen, @@ -24449,6 +24588,13 @@ gr_page_unmapped: * pre-retire - the r1110 mme_pc=513 class). */ WRITE_ONCE(gsp_phys_gr_mme_prelude_gen, 0); + /* + * SESSION 153-CONTINUED-79: also rewind the + * split-emission drain cursor, else a cleared + * latch with an exhausted cursor would skip + * the owed re-preload. + */ + WRITE_ONCE(gsp_phys_gr_mme_pre_emit_off, 0); pr_warn("vgsp-gr: WEDGE-RECOVER after re-arm grfifo=0x%08x (%s); recovery #%u this boot; MME prelude latch cleared (context re-preload owed)\n", g1, (g1 & 0xffff0000u) == 0xbadf0000u ? -- cgit v1.2.3