summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/gsp_phys_booter.c255
-rw-r--r--kernel/gsp_phys_booter.h22
-rw-r--r--kernel/gsp_sema_service.c1027
3 files changed, 1234 insertions, 70 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 85bdb89..f7b43fb 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -1428,6 +1428,17 @@ bool gsp_phys_boot_requested(void)
#define GSP_PHYS_RMCALL_HANDLE_USER_LANE_PM_FB 0xcaf000b8u
#define GSP_PHYS_RMCALL_HANDLE_USER_LANE_FAULT_MEM0 0xcaf000c0u
#define GSP_PHYS_RMCALL_HANDLE_USER_LANE_FAULT_MEM1 0xcaf000c4u
+/*
+ * Mode 35, graphics-execution plan §F first experiment: the TURING_A
+ * (0xc597, GR_OBJECT_TYPE_3D) class object constructed on the V7
+ * user-lane channel AFTER its promote - the missing half the mode-35
+ * capture never provided ("mode 35: ... the capture carries no 0xc597 on
+ * the user lane"). Best-effort like the TURING_COMPUTE_A: a rejection is
+ * logged and the lane keeps running; a construction on V7 must not hit
+ * the wired-golden deadlock class (m3a-m3g, that channel). 0xcaf00094 is
+ * free between 0xcaf00090 and 0xcaf000a0.
+ */
+#define GSP_PHYS_RMCALL_HANDLE_USER_LANE_GR3D 0xcaf00094u
/*
* Mode-35 V7 channel flags/privilege and fault/PM geometry, decoded from the
@@ -14778,6 +14789,35 @@ static int gsp_phys_rmcall_stage3(void __iomem *bar0)
boot_stage.rmcall_gr_object =
GSP_PHYS_RMCALL_HANDLE_USER_LANE_GR_OBJ;
}
+ /*
+ * Graphics-execution plan §F first 3D-lane experiment:
+ * construct the TURING_A (0xc597) class object on V7
+ * after the accepted promote, through the same generic
+ * alloc helper the golden lanes use. The plan's open
+ * question is whether a 0xc597 construct on the
+ * execution lane is accepted without the wired-golden
+ * deadlock class; the result line below is the
+ * experiment's evidence either way, and the construct
+ * never gates the lane.
+ */
+ {
+ u32 g3_rpc = 0, g3_status = 0;
+ int g3_ret = gsp_phys_rmcall_alloc_gr_object_class(bar0,
+ GSP_PHYS_RMCALL_CLASS_GR_3D,
+ GSP_PHYS_RMCALL_HANDLE_USER_LANE_GR3D,
+ GSP_PHYS_RMCALL_HANDLE_USER_LANE,
+ "mode 35 TURING_A on V7 (plan-F first 3D-lane experiment, after promote)",
+ &g3_rpc, &g3_status);
+
+ pr_info("vgsp-boot: rm-call: mode 35 plan-F experiment: TURING_A (0xc597) on V7 hChannel=0x%x -> ret=%d hObject=0x%x status=0x%x rpc_result=0x%x (%s)\n",
+ GSP_PHYS_RMCALL_HANDLE_USER_LANE,
+ g3_ret,
+ g3_ret ? 0 :
+ GSP_PHYS_RMCALL_HANDLE_USER_LANE_GR3D,
+ g3_status, g3_rpc,
+ g3_ret ? "rejected/absent; lane continues" :
+ "accepted; V7 carries a 3D class object for the method-stream replay");
+ }
pr_info("vgsp-boot: rm-call: mode 35: execution channel switched from V6 0x%x to V7 0x%x (captured flags=0x%x internalFlags=0x%x hContextShare=0x%x hVASpace=0, gpFifoVa=0x%llx entries=%u, sysmem USERD, sysmem instance/ramfc (m62 readback), TURING_COMPUTE_A 0x%x); V6 stays allocated and unscheduled, stage 4 + forwarding run on V7\n",
boot_stage.rmcall_channel,
GSP_PHYS_RMCALL_HANDLE_USER_LANE,
@@ -16825,6 +16865,22 @@ static u32 gsp_phys_rmcall_gr_emit_acquires(u32 *p,
}
/*
+ * r929 (GL 3D execution, Path A): replay a captured raw 3D method window.
+ * The sema service marks stream captures (the X server's C597 sections that
+ * carried their own operands) with a raw window (req->stream); when this is
+ * on, gsp_phys_rmcall_build_gr_launch emits SET_OBJECT TURING_A + the window
+ * verbatim + the channel release tail instead of the compute prelude/QMD, so
+ * the engine executes the REAL 3D methods (state binds, LINE_LENGTH_IN +
+ * LAUNCH_DMA uploads, program/tex binds) and, once draws are in the window,
+ * rasterizes. No effect on any submit without a stream window (all CUDA
+ * launches). Default on; A/B with gsp_phys_rmcall_gr_3d=0.
+ */
+static bool gsp_phys_rmcall_gr_3d = true;
+module_param_named(gsp_phys_rmcall_gr_3d, gsp_phys_rmcall_gr_3d, bool, 0644);
+MODULE_PARM_DESC(gsp_phys_rmcall_gr_3d,
+ "Replay a captured raw C597 method window verbatim instead of the compute prelude (default 1)");
+
+/*
* GR launch method block: the inline QMD run at its original shape and the
* channel REPORT_SEMAPHORE release. The stock guest push does NOT emit
* SET_OBJECT before the inline run (the compute class is bound on the
@@ -16834,7 +16890,8 @@ static u32 gsp_phys_rmcall_gr_emit_acquires(u32 *p,
static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
const struct gsp_phys_gr_submit *req,
const u32 *qmd, u64 qmd_va,
- u64 sem_va, u32 sem_payload)
+ u64 sem_va, u32 sem_payload,
+ const u32 *win3d, u32 win3d_n)
{
u32 *p = pb;
int prelude = gsp_phys_rmcall_gr_prelude_value();
@@ -16842,6 +16899,51 @@ static u32 gsp_phys_rmcall_build_gr_launch(u32 *pb,
int two_step = gsp_phys_rmcall_gr_pcas_two_step_value();
/*
+ * r929 (Path A): a captured 3D method window replays verbatim. Bind
+ * TURING_A (0xC597) on the channel subchannel (the same bind the
+ * window itself carries - belt and braces, a rebind is harmless),
+ * then the window words (address operands relocated to shadow-pool
+ * VAs by gsp_phys_rmcall_gr_3d_patch_window), then the channel
+ * release tail. No compute init, no QMD, no PCAS: the engine walks
+ * real 3D methods (SET_OBJECT C597, state binds, LINE_LENGTH_IN +
+ * LAUNCH_DMA inline uploads, program/tex binds). Completion is the
+ * trailing REPORT_SEMAPHORE alone (the submit path leaves qmd_rel
+ * off for this mode).
+ */
+ if (gsp_phys_rmcall_gr_3d && req && win3d && win3d_n &&
+ win3d_n + 2u + 8u <= GSP_PHYS_RMCALL_GR_METHOD_WORDS) {
+ *p++ = ((u32)GSP_PHYS_RMCALL_METHOD_IMM << 29) | (1u << 16) |
+ (0u << 13) |
+ (GSP_PHYS_RMCALL_GR_SET_OBJECT >> 2);
+ *p++ = GSP_PHYS_RMCALL_CLASS_GR_3D;
+ memcpy(p, win3d, (size_t)win3d_n * 4);
+ p += win3d_n;
+ p = gsp_phys_rmcall_push_gr(p,
+ GSP_PHYS_RMCALL_GR_SET_REPORT_SEMAPHORE_A,
+ (u32)(sem_va >> 32));
+ p = gsp_phys_rmcall_push_gr(p,
+ GSP_PHYS_RMCALL_GR_SET_REPORT_SEMAPHORE_B,
+ (u32)sem_va);
+ p = gsp_phys_rmcall_push_gr(p,
+ GSP_PHYS_RMCALL_GR_SET_REPORT_SEMAPHORE_C,
+ sem_payload);
+ p = gsp_phys_rmcall_push_gr(p,
+ GSP_PHYS_RMCALL_GR_SET_REPORT_SEMAPHORE_D,
+ GSP_PHYS_RMCALL_GR_SEM_RELEASE_D);
+ {
+ static u32 gr3d_log;
+
+ if (gr3d_log < 16) {
+ gr3d_log++;
+ pr_info("vgsp-gr: 3D STREAM replay h=0x%x push=0x%llx class=0xc597 window=%u words=%u verbatim (no prelude/QMD)\n",
+ req->h_object, req->push_va,
+ win3d_n, (u32)(p - pb));
+ }
+ }
+ return (u32)(p - pb);
+ }
+
+ /*
* r425 A/B: with gsp_phys_rmcall_gr_init_once the compute init preamble
* is emitted only on the first forwarded launch (the stock driver's
* per-channel shape); subsequent launches start at the inline-QMD
@@ -19188,6 +19290,104 @@ static void gsp_phys_gr_stall_log(const char *reason, u64 push_va,
reason, push_va, jiffies_to_msecs(jiffies - start), action);
}
+/*
+ * r929 v3 (Path A): relocate one 3D-window address operand to a shadow-pool
+ * VA. The guest VAs the 3D methods name (uploads 0x1_214f8f00.., vertex,
+ * program, tex pools) are installed in the ladder page tree by the Tier-1
+ * mapper yet the engine's FE/PD walks still fault FAULT_PDE on them (r929
+ * v2 evidence: "ENGINE GRAPHICS HUBCLIENT_FE faulted @ 0x1_214f8000 ...
+ * FAULT_PDE"); the shadow pool lines (map_shadow_locked) ARE the proven
+ * engine-visible region (every CUDA data/page relocation lives there). The
+ * page offset is preserved; a VA whose 4 KB page has no resolved hpa stays
+ * verbatim (it faults later and is named in the drain). Called with
+ * gsp_phys_rm_lock held.
+ */
+static bool gsp_phys_rmcall_gr_3d_reloc(u32 *up_w, u32 *lo_w,
+ const struct gsp_phys_gr_submit *req,
+ u32 *n_shadow)
+{
+ u64 va = ((u64)(*up_w & 0xffu) << 32) | (u64)(*lo_w & 0xfffffffcu);
+ u64 page = va & ~(u64)(GSP_PHYS_PAGE_SIZE - 1);
+ u64 sh = 0;
+ u32 line = 0, i;
+
+ if (!va || !page)
+ return false;
+ for (i = 0; i < req->n_pages; i++) {
+ if ((req->pages[i].guest_va &
+ ~(u64)(GSP_PHYS_PAGE_SIZE - 1)) != page ||
+ !req->pages[i].hpa)
+ continue;
+ if (*n_shadow >= GSP_PHYS_RMCALL_GR_SHADOW_PAGES)
+ return false;
+ if (gsp_phys_rmcall_gr_map_shadow_locked(*n_shadow,
+ req->pages[i].hpa,
+ &sh, &line))
+ return false;
+ if (line != 0xffffffffu)
+ (*n_shadow)++; /* alias hit consumes no slot */
+ sh |= va & (GSP_PHYS_PAGE_SIZE - 1);
+ *lo_w = (*lo_w & 0x3u) | ((u32)sh & 0xfffffffcu);
+ *up_w = (*up_w & ~0xffu) | ((u32)(sh >> 32) & 0xffu);
+ return true;
+ }
+ return false;
+}
+
+/*
+ * r929 v3: copy the 3D window and relocate every address operand a 3D-class
+ * method run carries (same recognizers as the sema service's svc_gl_field:
+ * LINE_LENGTH_IN 0x180 OFFSET_OUT pair, SET_VERTEX_STREAM_SUBSTITUTE 0xf84,
+ * SET_TEX_SAMPLER_POOL 0x155c, SET_TEX_HEADER_POOL 0x1574 and the
+ * SET_PIPELINE_PROGRAM_ADDRESS 0x2014/0x2018+j*0x40 pairs). Returns the
+ * number of relocated pair(s). Run widths follow the service's decoder:
+ * op 1/3/5 are 1+count dwords, everything else resyncs one dword.
+ */
+static u32 gsp_phys_rmcall_gr_3d_patch_window(const u32 *in, u32 n, u32 *out,
+ const struct gsp_phys_gr_submit *req,
+ u32 *n_shadow)
+{
+ u32 off = 0, reloc = 0;
+
+ memcpy(out, in, (size_t)n * 4);
+ while (off < n) {
+ u32 hdr = out[off];
+ u32 op = (hdr >> 29) & 0x7u;
+ u32 cnt = (hdr >> 16) & 0xfffu;
+ u32 addr = (hdr & 0x1fffu) << 2;
+
+ if ((op == 1 || op == 3 || op == 5) && cnt &&
+ off + 1u + cnt <= n) {
+ u32 *d = &out[off + 1];
+ u32 lo_i = 0;
+ bool pair = false;
+
+ if (op == 1 && addr == 0x0180 && cnt >= 4) {
+ lo_i = 2; /* OFFSET_OUT is v2/v3 */
+ pair = true;
+ } else if (op == 1 && addr == 0x0f84 && cnt >= 2) {
+ pair = true;
+ } else if (op == 1 && addr == 0x155c && cnt >= 3) {
+ pair = true;
+ } else if (op == 1 && addr == 0x1574 && cnt >= 3) {
+ pair = true;
+ } else if (op == 1 && addr >= 0x2014 &&
+ addr < 0x4000 && (addr & 0x3fu) == 0x14 &&
+ cnt >= 2) {
+ pair = true;
+ }
+ if (pair && gsp_phys_rmcall_gr_3d_reloc(&d[lo_i],
+ &d[lo_i + 1],
+ req, n_shadow))
+ reloc++;
+ off += 1u + cnt;
+ continue;
+ }
+ off++; /* header-only / resync, like the service decoder */
+ }
+ return reloc;
+}
+
int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
struct gsp_phys_backend_copy_result *out)
{
@@ -19205,6 +19405,7 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
* under the warning bound.
*/
static u32 methods[GSP_PHYS_RMCALL_GR_METHOD_WORDS];
+ static u32 stream_patch[GSP_PHYS_RMCALL_GR_METHOD_WORDS];
static u32 qmd[GSP_PHYS_GR_QMD_WORDS];
static struct gsp_phys_fault_info fault;
unsigned long start = jiffies;
@@ -19215,6 +19416,17 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
const u8 *guest_param_pg;
u32 want_qmd = 0, want_ch = 0;
bool qmd_rel = false, ch_rel = false;
+ /*
+ * r929 (Path A): this submission is a raw 3D method window. The QMD
+ * machinery (qmdwin relocation, QMD prewrite, release0 redirect,
+ * PCAS) does not apply: the block is SET_OBJECT C597 + the window +
+ * the channel release tail, and completion is the channel release
+ * alone (qmd_rel stays off so the poll does not wait for a QMD
+ * release word no engine will write).
+ */
+ bool stream3d = gsp_phys_rmcall_gr_3d && req && req->stream_n;
+ const u32 *win3d = NULL;
+ u32 win3d_n = 0;
bool qmd_rel_redirected = false; /* r420b: release0 -> module sem */
u64 qmd_rel_hpa = 0, ch_rel_hpa = 0;
u64 ch_sem_va = GSP_PHYS_RMCALL_VA_SEM + 4;
@@ -19494,9 +19706,10 @@ int gsp_phys_backend_guest_gr_submit(const struct gsp_phys_gr_submit *req,
* a fresh shadow line so the guest observes its own QMD release; else
* point release0 at the module semaphore so the host observes engine
* completion. A reduction release is never redirected (its arithmetic
- * would change).
+ * would change). r929: skipped entirely for a 3D stream replay -
+ * there is no QMD, so there is no release0 to point anywhere.
*/
- if (((qmd[4] >> 10) & 1)) {
+ if (!stream3d && ((qmd[4] >> 10) & 1)) {
u32 red_en = (qmd[24] >> 26) & 1;
u32 one_word = (qmd[24] >> 31) & 1;
bool rel_mod = gsp_phys_rmcall_gr_rel_mod_value() != 0;
@@ -20230,8 +20443,9 @@ gr_page_unmapped:
* GPFIFO/PB/REPORT_SEM legs already use. The 2 MiB window is installed
* by the ladder itself, so no window ensure is sent for it (a
* pdeIndex-0 Tier-1 install would touch the ladder's own PDE).
+ * r929: a 3D stream replay has no inline QMD at all (skipped).
*/
- if (gsp_phys_rmcall_gr_qmdwin_value()) {
+ if (!stream3d && gsp_phys_rmcall_gr_qmdwin_value()) {
u8 *qw = boot_stage.rmcall_gr_qmdwin.pages ?
gsp_phys_chunk_page_va(&boot_stage.rmcall_gr_qmdwin, 0) :
NULL;
@@ -20258,9 +20472,9 @@ gr_page_unmapped:
* use it as the destination when the caller resolved it; a Tier-1
* install keeps the guest VA (the guest sees its own QMD), a Tier-2
* alias keeps the launch runnable, and an unresolved page keeps the
- * module scratch VA.
+ * module scratch VA. r929: skipped for a 3D stream replay.
*/
- if (!qmdwin_on && req->qmd_hpa) {
+ if (!stream3d && !qmdwin_on && req->qmd_hpa) {
u64 qmd_page = req->qmd_va & ~(u64)(GSP_PHYS_PAGE_SIZE - 1);
u32 qmd_off = (u32)(req->qmd_va & (GSP_PHYS_PAGE_SIZE - 1));
bool dst_ok = false;
@@ -20378,9 +20592,28 @@ gr_page_unmapped:
GSP_PHYS_GR_QMD_SENTINEL);
}
+ /*
+ * r929 v3 (Path A): relocate the 3D window's address operands to
+ * shadow-pool VAs the engine walkers can reach (see
+ * gsp_phys_rmcall_gr_3d_patch_window; the r929 v2 FE faulted
+ * FAULT_PDE on a Tier-1-mapped guest VA). Runs with the rm lock
+ * held, before the method block is built.
+ */
+ if (stream3d) {
+ u32 nrel = gsp_phys_rmcall_gr_3d_patch_window(
+ req->stream, req->stream_n, stream_patch, req,
+ &n_shadow);
+
+ win3d = stream_patch;
+ win3d_n = req->stream_n;
+ pr_info("vgsp-gr: 3D window relocated %u operand pair(s) to shadow VAs (window=%u shadow=%u)\n",
+ nrel, win3d_n, n_shadow);
+ }
+
words = gsp_phys_rmcall_build_gr_launch(methods, req, qmd,
qmd_dst,
- ch_sem_va, want_ch);
+ ch_sem_va, want_ch,
+ win3d, win3d_n);
memcpy(gsp_phys_chunk_page_va(pb, 0), methods, words * 4);
pr_info("vgsp-gr: qmd w0..3=%08x %08x %08x %08x w4..7=%08x %08x %08x %08x w23..25=%08x %08x %08x w32/33=%08x %08x w48/49=%08x %08x\n",
qmd[0], qmd[1], qmd[2], qmd[3], qmd[4], qmd[5], qmd[6], qmd[7],
@@ -21596,7 +21829,8 @@ gr_page_unmapped:
words = gsp_phys_rmcall_build_gr_launch(
methods, req, qmd,
qmd_dst, ch_sem_va,
- want_ch);
+ want_ch, win3d,
+ win3d_n);
memcpy(gsp_phys_chunk_page_va(pb, 0),
methods, words * 4);
}
@@ -25153,10 +25387,11 @@ static_assert(offsetof(struct gsp_phys_gr_inline, data) == 12 &&
static_assert((2u + 1u + GSP_PHYS_RMCALL_GR_QMD_INLINE_WORDS + 8u) <=
GSP_PHYS_RMCALL_GR_METHOD_WORDS,
"the GR launch method block (compute init + SET_OBJECT + inline-QMD run + PCAS + REPORT_SEMAPHORE) must fit the bound");
-static_assert(GSP_PHYS_RMCALL_GR_INIT_WORDS + GSP_PHYS_GR_RAW_WORDS +
+static_assert(GSP_PHYS_RMCALL_GR_INIT_WORDS +
+ GSP_PHYS_RMCALL_GR_QMD_WINDOW_WORDS +
GSP_PHYS_RMCALL_GR_TAIL_WORDS <=
GSP_PHYS_RMCALL_GR_METHOD_WORDS,
- "the verbatim GR launch block (compute init + raw tail + PCAS + REPORT_SEMAPHORE) must fit the bound");
+ "the verbatim GR launch block minimum (compute init + window + PCAS + REPORT_SEMAPHORE) must fit the bound; the per-launch runtime check bounds raw_qmd_hdr for larger preludes");
static_assert(GSP_PHYS_RMCALL_GR_METHOD_WORDS * 4u <=
GSP_PHYS_RMCALL_COPY_SIZE,
"the GR launch method block must fit one module pushbuffer page");
diff --git a/kernel/gsp_phys_booter.h b/kernel/gsp_phys_booter.h
index af74f7c..dfdf1d9 100644
--- a/kernel/gsp_phys_booter.h
+++ b/kernel/gsp_phys_booter.h
@@ -368,7 +368,12 @@ void gsp_phys_backend_relay_counters(u64 *requested, u64 *ok, u64 *verify_fail,
#define GSP_PHYS_GR_QMD_SENTINEL 0xdeadbeefu
#define GSP_PHYS_GR_QMD_SENTINEL_WORD 63u
#define GSP_PHYS_GR_QMD_WORD_NONE 0xffffffffu
-#define GSP_PHYS_GR_RAW_WORDS 256u
+/*
+ * r929: grown 256 -> 512 dwords so a whole GL 3D section push (the observed
+ * X server one-shot is 1524 B = 381 dwords) survives in the captured raw
+ * tail; the raw window is the source of the 3D method-stream replay.
+ */
+#define GSP_PHYS_GR_RAW_WORDS 512u
/*
* B-2 (large-operand data path): the grown, boot-fixed GR data-bounce region.
@@ -451,6 +456,21 @@ struct gsp_phys_gr_submit {
u64 push_va; /* guest push identity (logging only) */
u32 push_len;
u32 h_object; /* guest GR channel handle (logging only) */
+ /*
+ * r929 (GL 3D path): the captured raw 3D method window - the
+ * SET_OBJECT C597 section from its class bind through the section
+ * close (state binds, LINE_LENGTH_IN + LAUNCH_DMA uploads, program/
+ * tex binds; the prelude before the bind is excluded - its host-sem
+ * VAs belong to the client's VM and are unmapped in the replay
+ * channel, r929 v1 FAULT_PDE). When stream_n > 0 and
+ * gsp_phys_rmcall_gr_3d is on, the launch block replays the window
+ * verbatim after a SET_OBJECT TURING_A bind instead of the compute
+ * prelude + QMD; the engine then executes the REAL 3D methods.
+ * stream points into the caller's raw buffer; it is a separate
+ * pointer so a caller can pass any window.
+ */
+ const u32 *stream;
+ u32 stream_n; /* dwords */
};
/* True when gsp_phys_gr_forward is set and the ladder is a live GR channel
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 8711517..52820b7 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -196,6 +196,8 @@ struct sema_map_entry {
#define SVC_GR_RAM_REFRESH_MAX 3 /* keep-alive budget refreshes */
#define SVC_GR_RAM_REFRESH_MS 10000 /* minimum spacing between them */
#define SVC_GR_RAM_WIN (2ULL << 20) /* allocation window pinned */
+/* SESSION 154 (M-GL1.2): watched USERD candidates for the liveness latch. */
+#define SVC_GR_USERD_WATCH 8
/*
* r908: guest-RAM ring-scan top (guest physical). The guest's usable RAM
@@ -566,6 +568,45 @@ struct svc_channel {
u8 gr_ram_userd_passes; /* slices walked so far */
bool gr_ram_userd_logged; /* one discovery/give-up line */
/*
+ * SESSION 154 (M-GL1.2): liveness confirmation for the RAM USERD
+ * latch. The shape+entry gates alone admitted frozen or slowly
+ * churning lookalike pages (r913: pa 0x7c000 adopted at put=1 only
+ * survived by luck; r914: pa 0x9e200 adopted at put=12 get=8 and
+ * its put%2==0 collapsed the walk - submissions stayed 0 while the
+ * client retried for minutes). A candidate is WATCHED across passes
+ * instead: only a block whose PUT takes the client's own single +1
+ * step is adopted as the live control block - the measured live
+ * block advances 1->2->3 while the lookalikes jump by larger
+ * amounts or churn to zero.
+ */
+ u64 gr_ram_userd_watch[SVC_GR_USERD_WATCH]; /* watched PAs */
+ u32 gr_ram_userd_watch_put[SVC_GR_USERD_WATCH]; /* last PUT seen */
+ u8 gr_ram_userd_watch_hits[SVC_GR_USERD_WATCH]; /* +1/+2 streak */
+ u8 gr_ram_userd_watch_n;
+ u32 gr_ram_put_seeded; /* last PUT the walk seed consumed */
+ /*
+ * SESSION 154 (M-GL1.2): speculative-latch confirmation state. The
+ * RAM USERD scan latches a gated candidate immediately; the pump
+ * confirms it from its own per-pass PUT read (a +1/+2 step), and
+ * unlatches a churning/frozen block (see svc_pump_channels).
+ */
+ u32 gr_ram_userd_latch_put; /* PUT at the speculative latch */
+ unsigned long gr_ram_userd_latch_at; /* jiffies at the latch */
+ bool gr_ram_userd_confirmed; /* +1/+2 step observed */
+ /*
+ * SESSION 154 (M-GL1.2): content anchor for a client push whose
+ * translation went stale (r913: a validated pin kept answering for
+ * a reused page and the wrong-VAS walk answer was implausible while
+ * the real push bytes sat in RAM on a page no directory covered).
+ * Scan guest RAM for the client's release-group signature (sliced,
+ * cursor-resumed, like the ring scans), validate the page and pin
+ * it so the pump's retry decodes the real bytes.
+ */
+ u64 gr_anchor_cursor; /* resume point of the anchor scan */
+ u64 gr_anchor_va; /* entry VA the sweep is for */
+ u8 gr_anchor_sweeps; /* completed sweeps for that VA */
+ bool gr_anchor_logged; /* one start line per channel */
+ /*
* r496e: true when the ring was found by the RAM content scan (a
* kernel-RM sysmem channel - the X/GL 3D channel). Such a channel's
* pushes are 2D/3D method streams the emulation does not execute;
@@ -3322,6 +3363,36 @@ static void svc_pin_store_win(u64 va, u64 pa, int ap)
}
/*
+ * SESSION 154 (M-GL1.2): drop a validated pin whose content no longer
+ * parses as the page's kind of stream (a stale translation kept answering
+ * for a page the guest has since reused - r913: va 0x1203c0000 read
+ * `first8=00000001` through a validated pin while the real push bytes sat
+ * elsewhere). The next read then consults the window guess, the trusted
+ * walk and the content sources instead of the dead pin.
+ */
+static void svc_pin_drop(u64 va)
+{
+ u64 page = va & ~(u64)(SEMA_PAGE_SIZE - 1);
+ u32 mask = SVC_PIN_SLOTS - 1;
+ u32 h = (u32)((page >> SEMA_PAGE_SHIFT) * 2654435761u) & mask;
+ u32 probe;
+
+ for (probe = 0; probe < SVC_PIN_PROBE; probe++) {
+ struct svc_pin *p = &svc.pin[(h + probe) & mask];
+
+ if (!p->va_page)
+ return;
+ if (p->va_page == page)
+ break;
+ }
+ if (probe == SVC_PIN_PROBE)
+ return;
+ memset(&svc.pin[(h + probe) & mask], 0, sizeof(struct svc_pin));
+ if (svc.pin_n)
+ svc.pin_n--;
+}
+
+/*
* Injected (host-tool) translations. Kept separate from the discovered pin
* table so a hand-verified VA->PA pair can never be shadowed, evicted or
* silently dropped by the self-discovery stream: in the shared table a hot
@@ -6454,7 +6525,13 @@ static struct svc_fwd_cand svc_fwd_cand;
* so the hand-off array is never the binding limit.
*/
#define SVC_GR_DATA_MAX 32768
-#define SVC_GR_PUSH_WORDS 256
+/*
+ * r929: 256 -> 512 dwords (2 KB) so a whole GL 3D section push (the X
+ * server's one-shot stream is 1524 B = 381 dwords) survives in raw[] - the
+ * 3D stream replay needs the window verbatim, not just its tail. Must equal
+ * GSP_PHYS_GR_RAW_WORDS (static_assert below).
+ */
+#define SVC_GR_PUSH_WORDS 512
#define SVC_GR_QMD_WORD_NONE 0xffffffffu
/* svc_gr_cand.rel_flags bits (decoded from qmd[24] and qmd[4]). */
#define SVC_GR_REL_REDUCTION 0x1u
@@ -6523,6 +6600,17 @@ struct svc_gr_cand_rec {
u32 push_n; /* raw tail words kept in raw[] */
u32 push_qmd_hdr; /* raw[] offset of the 0x318 run header */
u32 push_qmd_end; /* raw[] offset just past that run */
+ /*
+ * r929 (GL 3D path): this capture is a real C597 method stream (the
+ * section carried its own operands, op_src=stream) whose whole push
+ * fits in raw[]. The backend replays raw[stream_off..stream_off+
+ * stream_n) verbatim as a 3D method window (SET_OBJECT TURING_A +
+ * the engine's own uploads/binds) instead of running the synthesized
+ * compute QMD. The window is sliced from the section's C597 bind.
+ */
+ bool stream_3d;
+ u32 stream_off; /* dword offset of the window inside raw[] */
+ u32 stream_n; /* dwords of the replayable window */
u32 raw[SVC_GR_PUSH_WORDS];
u32 qmd[SVC_GR_QMD_WORDS];
u64 qmd_dev_va;
@@ -6889,6 +6977,11 @@ struct svc_gl_up {
struct svc_gl_sec {
bool seen;
u32 subch;
+ u32 start_off; /* byte offset of the C597 SET_OBJECT that opened the
+ * section - the 3D replay window starts here (r929:
+ * the prelude before it carries the channel's own
+ * host-sem/2D ops whose VAs are not mapped in the
+ * replay channel and FAULT the method walk) */
u64 prog_va; /* SET_PIPELINE_PROGRAM_ADDRESS_A/B(j) upper|lower */
u64 param_va; /* SET_TEX_HEADER_POOL_A/B/C upper|lower */
u32 param_size; /* observed pool value word (0x10 on the r882 stream) */
@@ -6921,6 +7014,47 @@ struct svc_gl_sec {
u32 blob_len;
};
static struct svc_gl_sec svc_gl; /* service thread only */
+/*
+ * SESSION 153 (G-B gate: the client's own C597 draws must open sections).
+ * The replay guard at svc_gl_flush consults svc_gr_cand_push_seen(), which
+ * returns true when the push VA sits in the GR candidate ring - even when
+ * no GL section was ever captured for that push. The GL client's draw
+ * pushes are admitted as GR candidates during driver init (they are read
+ * as ordinary GR pushes first), so every later replay of them is rejected
+ * as "replay already captured" and no GL section ever opens. Track GL
+ * section captures in their own table: a replay only skips when THIS table
+ * holds the exact (push_va, content hash) pair. The hash keeps a later,
+ * different push at the same VA (a frame loop: same slot, new content)
+ * capturable, matching the r383 reasoning for the GR seen table.
+ */
+#define SVC_GL_CAP_HIST 256
+static struct {
+ u64 push_va;
+ u64 hash;
+} svc_gl_cap_hist[SVC_GL_CAP_HIST];
+static u32 svc_gl_cap_next;
+static u32 svc_gl_cap_seen_log;
+
+static bool svc_gl_cap_seen(u64 push_va, u64 hash)
+{
+ u32 i;
+
+ if (!push_va)
+ return false;
+ for (i = 0; i < SVC_GL_CAP_HIST; i++)
+ if (svc_gl_cap_hist[i].push_va == push_va &&
+ svc_gl_cap_hist[i].hash == hash)
+ return true;
+ return false;
+}
+
+static void svc_gl_cap_store(u64 push_va, u64 hash)
+{
+ u32 i = svc_gl_cap_next++ % SVC_GL_CAP_HIST;
+
+ svc_gl_cap_hist[i].push_va = push_va;
+ svc_gl_cap_hist[i].hash = hash;
+}
static u32 svc_gl_rep_log; /* bounded replay-already-captured witness */
static u32 svc_gl_prog_log; /* R16 bounded program-read witness */
static u32 svc_gl_up_log; /* R17 bounded section-upload-apply witness */
@@ -6971,6 +7105,103 @@ static void svc_gl_prog_apply_param(void)
pr_info("sema-gl: prog-hex param: %u bytes stored\n",
svc_gl_prog_len);
}
+
+/*
+ * SESSION 153 (GL client draw sections): the GL client's own per-frame
+ * pushes bind TURING_A (0xC597) exactly like the X server's one-shot
+ * section, but carry no program/param/release operands at all (measured:
+ * `0001fff0 20010000 0000c597 0001fff0 20016043 00000000 20016040
+ * 00000000` on h=0xbfef0002 - the C597 SET_OBJECT plus 2D-class
+ * NoOp/Notify only). The old flush guard required the full operand trio
+ * and silently dropped every client section, so no client capture ever
+ * appeared. Two additive sources close that gap:
+ *
+ * 1. a per-channel operand HOLD: the last valid prog/param/rel operands
+ * seen in a section on this channel are remembered and adopted by a
+ * later draw section on the same channel (still marked src=hold);
+ * 2. a dev fallback (gsp_sema_gl_draw_synth): when the client
+ * never supplied operands at all, the draw section captures with the
+ * operand values the X server's one-shot GL section supplied in the
+ * same boot class (r891/r923), marked src=draw-synth.
+ *
+ * r929 (Path A): the dev fallback is now OFF by default. The stream
+ * captures (real operands, src=stream) are the primary 3D path and replay
+ * verbatim; a draw-synth capture synthesizes a compute QMD from fake/dev
+ * operands, and on this boot class its launches never completed
+ * (qmd_rel=0x600df00d / ch_rel miss -> SYNTH-FAULT + GR-COMPLETE-SW every
+ * ~19 s per kick, GZDoom wedged). Set sema_gl_draw_synth=1 to A/B the old
+ * fallback.
+ */
+#define SVC_GL_HOLD_MAX 8
+struct svc_gl_hold {
+ u32 h;
+ u64 prog_va;
+ u64 param_va;
+ u32 param_size;
+ u64 rel_va;
+ u32 rel_payload;
+};
+static struct svc_gl_hold svc_gl_hold_tab[SVC_GL_HOLD_MAX];
+static u32 svc_gl_open_log; /* bounded section-open witness */
+static u32 svc_gl_skip_log; /* bounded flush-skip witness */
+static bool gsp_sema_gl_draw_synth;
+module_param_named(sema_gl_draw_synth, gsp_sema_gl_draw_synth, bool, 0644);
+MODULE_PARM_DESC(sema_gl_draw_synth,
+ "Capture a draw-shaped C597 section with the dev fallback operands when the client supplied none (default 0 since r929; witness marks src=draw-synth)");
+#define SVC_GL_SYNTH_PROG_VA 0x121201b00ULL
+#define SVC_GL_SYNTH_PARAM_VA 0x1214fa220ULL
+#define SVC_GL_SYNTH_PARAM_SIZE 0x10u
+#define SVC_GL_SYNTH_REL_VA 0x120001000ULL
+#define SVC_GL_SYNTH_REL_PAY 1u
+
+static void svc_gl_hold_store(u32 h, u64 prog, u64 param, u32 psize,
+ u64 rel, u32 rpay)
+{
+ u32 i, free = SVC_GL_HOLD_MAX;
+
+ if (!h || !prog || !param || !rel)
+ return;
+ for (i = 0; i < SVC_GL_HOLD_MAX; i++) {
+ if (svc_gl_hold_tab[i].h == h) {
+ free = i;
+ break;
+ }
+ if (!svc_gl_hold_tab[i].h && free == SVC_GL_HOLD_MAX)
+ free = i;
+ }
+ if (free == SVC_GL_HOLD_MAX)
+ free = h % SVC_GL_HOLD_MAX;
+ svc_gl_hold_tab[free].h = h;
+ svc_gl_hold_tab[free].prog_va = prog;
+ svc_gl_hold_tab[free].param_va = param;
+ svc_gl_hold_tab[free].param_size = psize;
+ svc_gl_hold_tab[free].rel_va = rel;
+ svc_gl_hold_tab[free].rel_payload = rpay;
+}
+
+static bool svc_gl_hold_load(u32 h, u64 *prog, u64 *param, u32 *psize,
+ u64 *rel, u32 *rpay)
+{
+ u32 i;
+
+ for (i = 0; i < SVC_GL_HOLD_MAX; i++) {
+ if (svc_gl_hold_tab[i].h != h || !h)
+ continue;
+ if (prog)
+ *prog = svc_gl_hold_tab[i].prog_va;
+ if (param)
+ *param = svc_gl_hold_tab[i].param_va;
+ if (psize)
+ *psize = svc_gl_hold_tab[i].param_size;
+ if (rel)
+ *rel = svc_gl_hold_tab[i].rel_va;
+ if (rpay)
+ *rpay = svc_gl_hold_tab[i].rel_payload;
+ return true;
+ }
+ return false;
+}
+
/*
* SESSION 149 R17 (M4): standalone GL-class upload tracker. A context-init
* upload push (the one-shot program upload) is NOT required to sit inside a
@@ -7137,20 +7368,78 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
int blob_rc = -1;
bool blob_trusted = false;
const char *blob_src = "none";
+ const char *op_src = "stream";
svc_gl_prog_apply_param();
- if (!svc_gl.seen || !svc_gl.prog_va || !svc_gl.param_va ||
- !svc_gl.rel_va || !trusted)
+ if (!svc_gl.seen || !trusted)
goto out;
+ {
+ u64 hp = 0, hm = 0, hr = 0;
+ u32 hms = 0, hrp = 0;
+ bool held = false;
+
+ if (!svc_gl.prog_va || !svc_gl.param_va || !svc_gl.rel_va) {
+ held = svc_gl_hold_load(ch->h_object, &hp, &hm, &hms,
+ &hr, &hrp);
+ if (!svc_gl.prog_va && held) {
+ svc_gl.prog_va = hp;
+ op_src = "hold";
+ }
+ if (!svc_gl.param_va && held) {
+ svc_gl.param_va = hm;
+ svc_gl.param_size = hms;
+ op_src = "hold";
+ }
+ if (!svc_gl.rel_va && held) {
+ svc_gl.rel_va = hr;
+ svc_gl.rel_payload = hrp;
+ op_src = "hold";
+ }
+ if ((!svc_gl.prog_va || !svc_gl.param_va) &&
+ gsp_sema_gl_draw_synth && !ch->engine_type) {
+ if (!svc_gl.prog_va)
+ svc_gl.prog_va = SVC_GL_SYNTH_PROG_VA;
+ if (!svc_gl.param_va) {
+ svc_gl.param_va = SVC_GL_SYNTH_PARAM_VA;
+ svc_gl.param_size =
+ SVC_GL_SYNTH_PARAM_SIZE;
+ }
+ op_src = "draw-synth";
+ }
+ if (!svc_gl.rel_va && gsp_sema_gl_draw_synth &&
+ !ch->engine_type) {
+ svc_gl.rel_va = SVC_GL_SYNTH_REL_VA;
+ svc_gl.rel_payload = SVC_GL_SYNTH_REL_PAY;
+ op_src = "draw-synth";
+ }
+ if (!svc_gl.prog_va || !svc_gl.param_va ||
+ !svc_gl.rel_va) {
+ if (gsp_sema_dbg && svc_gl_skip_log < 16) {
+ svc_gl_skip_log++;
+ pr_info("sema-gl: flush skip h=0x%x push=0x%llx prog=0x%llx param=0x%llx rel=0x%llx (no operands, no hold)\n",
+ ch->h_object, push_va,
+ svc_gl.prog_va, svc_gl.param_va,
+ svc_gl.rel_va);
+ }
+ goto out;
+ }
+ }
+ }
/*
* SESSION 149 R15 (M1.1a): admit a rewind replay when the same slot
* re-read valid bytes whose content hash matches the recorded pass
* (the pump sets cap_trust only for such a hash-verified replay; see
- * the trust admission at the decode call site). The session-wide
- * (push_va, hash) capture history is the exactly-once guard: a push
- * already captured/forwarded makes every later replay a no-op.
- */
- if (replay && svc_gr_cand_push_seen(push_va, svc_push_hash(buf, len))) {
+ * the trust admission at the decode call site). The exactly-once guard
+ * for GL capture is GL-specific (SESSION 153): svc_gr_cand_push_seen()
+ * also matches the GR candidate ring and the session seen table, and
+ * the GL client's C597 draw pushes are admitted as GR candidates
+ * during driver init - so using it here suppressed every later GL
+ * section capture on those push VAs ("replay already captured" for
+ * every client draw). svc_gl_cap_seen() only matches pushes whose GL
+ * section was actually captured, with the content hash so a later
+ * different push in the same slot (a frame loop) stays capturable.
+ */
+ if (replay && svc_gl_cap_seen(push_va, svc_push_hash(buf, len))) {
if (svc_gl_rep_log < 16) {
svc_gl_rep_log++;
pr_info("sema-gl: replay already captured h=0x%x push=0x%llx (no re-capture)\n",
@@ -7298,18 +7587,40 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
memcpy(blob, svc_gl.blob, blob_n);
blob_src = "lid-fallback";
}
- if (blob_n) {
+ /*
+ * r929 (Path A): a stream-derived capture is a 3D method window - the
+ * backend replays the raw push verbatim and the engine runs the real
+ * uploads/binds, so the synthesized compute attachments (the inline
+ * program record, and with it the prog-hex fallback) do not apply.
+ * Draw-synth/hold captures keep the old compute path unchanged.
+ */
+ if (blob_n && op_src[0] != 's') {
memset(&pr, 0, sizeof(pr));
pr.dst_va = svc_gl.prog_va;
pr.len = blob_n;
memcpy(pr.data, blob, blob_n);
n_recs = 1;
}
- pr_info("sema-gl: section captured h=0x%x push=0x%llx len=%u off=0x%x prog=0x%llx param=0x%llx/0x%x rel=0x%llx/%u ptr=%u blob=%u replay=%u\n",
+ /*
+ * SESSION 153: record this GL section capture in the GL-specific
+ * exactly-once table so only an actual capture suppresses later
+ * replays of the same (push_va, content hash).
+ */
+ svc_gl_cap_store(push_va, svc_push_hash(buf, len));
+ pr_info("sema-gl: section captured h=0x%x push=0x%llx len=%u off=0x%x prog=0x%llx param=0x%llx/0x%x rel=0x%llx/%u ptr=%u blob=%u replay=%u src=%s\n",
ch->h_object, push_va, len, at_off, svc_gl.prog_va,
svc_gl.param_va, svc_gl.param_size, svc_gl.rel_va,
svc_gl.rel_payload, svc_gl.n_ptr, svc_gl.blob_len,
- replay ? 1u : 0u);
+ replay ? 1u : 0u, op_src);
+ /*
+ * SESSION 153: remember real operands for this channel so a later
+ * draw-only section on the same channel can still capture. A
+ * draw-synth capture carries no channel truth and is never stored.
+ */
+ if (op_src[0] != 'd')
+ svc_gl_hold_store(ch->h_object, svc_gl.prog_va,
+ svc_gl.param_va, svc_gl.param_size,
+ svc_gl.rel_va, svc_gl.rel_payload);
/* R16 bounded witness: what the synthesized QMD now schedules and
* where the code bytes came from. */
if (gsp_sema_dbg && svc_gl_prog_log < 8) {
@@ -7328,6 +7639,34 @@ static void svc_gl_flush(struct nv_vgpu_device *vgpu,
}
svc_gr_cand_capture(ch, &s2, n_recs ? &pr : NULL, n_recs, buf, len,
push_va);
+ /*
+ * r929 (Path A): a stream-derived capture (op_src=stream: the section
+ * carried its own operands) is a raw 3D method window. Mark it for
+ * verbatim replay - but only when the WHOLE push survived in raw[]
+ * (len == push_n*4): a truncated window would feed the engine a
+ * half-method stream. The window is SLICED from the section's own
+ * C597 SET_OBJECT (svc_gl.start_off) through the section close: the
+ * prelude before the bind carries the X server channel's own
+ * host-sem + 2D/CE ops, whose semaphore VAs belong to that client's
+ * VM and are unmapped in the replay channel (r929 v1: the first
+ * host-sem write faulted FAULT_PDE @ 0x1_2000b000 and the method
+ * walk died before any 3D method). Every VA the sliced window
+ * references is covered by the candidate's own mapped pages. The
+ * 448-word cap leaves headroom for the backend's 512-word method
+ * block (2 bind words + the window + the 8-word release tail).
+ * Draw-synth/hold captures are never marked.
+ */
+ if (op_src[0] == 's' && svc_gr_cand.valid &&
+ len == svc_gr_cand.push_n * 4 &&
+ svc_gl.start_off + 8u <= at_off &&
+ (at_off - svc_gl.start_off) / 4u <= 448u) {
+ svc_gr_cand.stream_3d = true;
+ svc_gr_cand.stream_off = svc_gl.start_off / 4u;
+ svc_gr_cand.stream_n = (at_off - svc_gl.start_off) / 4u;
+ pr_info("sema-gl: 3D stream marked h=0x%x push=0x%llx window=%u words @off=0x%x (verbatim replay from the C597 bind)\n",
+ ch->h_object, push_va, svc_gr_cand.stream_n,
+ svc_gl.start_off);
+ }
out:
/*
* SESSION 149 R15 (M1.1a): preserve the last valid release across
@@ -11369,26 +11708,50 @@ static u64 svc_pass_hash; /* r391f: this decode pass's push content hash */
* running ~115 ms BEFORE its stream's H2D soft copies; the ordering the
* driver relied on is only provable by reading the push's own methods.
* Bounded: gsp_sema_push_raw dumps, at most once per push VA.
+ * SESSION 153: gsp_sema_push_raw_small extends the same dump to the GL
+ * client's small pushes (16..511 B - the C597 draw/notify pushes are 32/64 B
+ * and were excluded by the >= 512 gate), with its own budget and a per
+ * (VA,len) once-only guard so a re-read of the same push shape does not
+ * flood.
*/
unsigned gsp_sema_push_raw;
+unsigned gsp_sema_push_raw_small;
+module_param_named(sema_push_raw_small, gsp_sema_push_raw_small, uint, 0644);
+MODULE_PARM_DESC(sema_push_raw_small,
+ "Verbatim decode dump for small GR pushes (16..511 B), bounded count (default 0)");
static u64 svc_pushraw_last_va;
+static u32 svc_pushraw_last_len;
static u32 svc_pushraw_n;
+static u32 svc_pushraw_small_n;
static void svc_push_raw_dump(const u8 *buf, u32 len, u64 push_va,
u32 h_object)
{
u32 off = 0, gi = 0;
- if (!gsp_sema_push_raw || svc_pushraw_n >= gsp_sema_push_raw)
+ if (len > 8192)
return;
- if (len < 512 || len > 8192)
- return;
- if (push_va == svc_pushraw_last_va)
+ if (len < 512) {
+ if (!gsp_sema_push_raw_small ||
+ svc_pushraw_small_n >= gsp_sema_push_raw_small)
+ return;
+ } else {
+ if (!gsp_sema_push_raw || svc_pushraw_n >= gsp_sema_push_raw)
+ return;
+ }
+ if (push_va == svc_pushraw_last_va && len == svc_pushraw_last_len)
return;
svc_pushraw_last_va = push_va;
- svc_pushraw_n++;
- pr_info("sema-pushraw: BEGIN h=0x%x va=0x%llx len=%u n=%u\n",
- h_object, push_va, len, svc_pushraw_n);
+ svc_pushraw_last_len = len;
+ if (len < 512) {
+ svc_pushraw_small_n++;
+ pr_info("sema-pushraw: BEGIN h=0x%x va=0x%llx len=%u n=%u (small)\n",
+ h_object, push_va, len, svc_pushraw_small_n);
+ } else {
+ svc_pushraw_n++;
+ pr_info("sema-pushraw: BEGIN h=0x%x va=0x%llx len=%u n=%u\n",
+ h_object, push_va, len, svc_pushraw_n);
+ }
while (off + 4 <= len && gi < 160) {
u32 hdr, op, cnt, subch, addr, v[4] = {0,0,0,0}, k, n;
@@ -11635,9 +11998,14 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
/*
* r481: verbatim method-stream dump for large GR-class pushes
* (default 0 = off). See svc_push_raw_dump.
+ * SESSION 153: the small-push variant (sema_push_raw_small) covers the
+ * GL client's 16..511 B C597 pushes with its own budget.
*/
if (gsp_sema_push_raw && ch && !ch->engine_type && len >= 512)
svc_push_raw_dump(buf, len, push_va, ch->h_object);
+ else if (gsp_sema_push_raw_small && ch && !ch->engine_type &&
+ len >= 16 && len < 512)
+ svc_push_raw_dump(buf, len, push_va, ch->h_object);
memset(&st, 0, sizeof(st));
st.push_va = push_va;
@@ -12079,8 +12447,22 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
memset(&svc_gl, 0, sizeof(svc_gl));
svc_gl.seen = true;
svc_gl.subch = subch;
+ svc_gl.start_off = off;
svc_gl.rel_va = keep_rel;
svc_gl.rel_payload = keep_pay;
+ /*
+ * SESSION 153: witness every C597 section open
+ * (bounded) - it is the recognizer-side proof
+ * that the client's own draw pushes are seen;
+ * the capture itself is witnessed by
+ * "sema-gl: section captured".
+ */
+ if (gsp_sema_dbg && svc_gl_open_log < 32) {
+ svc_gl_open_log++;
+ pr_info("sema-gl: section open h=0x%x push=0x%llx subch=%u trusted=%d\n",
+ ch->h_object, push_va, subch,
+ trusted);
+ }
} else if (svc_gl.seen) {
svc_gl_flush(vgpu, ch, &st, buf, len,
push_va, trusted, replay, off);
@@ -15491,6 +15873,27 @@ static u64 svc_lastgood_ring_va;
static u64 svc_lastgood_ring_pa;
+/*
+ * SESSION 154 (M-GL1.2): the PA ceiling for the push read. The GL client's
+ * sysmem allocations live ABOVE SEMA_PA_MAX (the ring scan already reads to
+ * gsp_sema_gr_scan_top for exactly this client - r908: its ring page at gpa
+ * 0x231e5d000); the content anchor pinned its push page at pa 0x2328e8000
+ * (r920, measured) and the old 6 GiB read bound then rejected the freshly
+ * pinned page (src=2 -> -EIO) while the stale window answered instead
+ * (src=3 garbage, every push "unreadable"). Use the same widened ceiling
+ * the scans use.
+ */
+static u64 svc_push_pa_ceiling(void)
+{
+ u64 top = READ_ONCE(gsp_sema_gr_scan_top);
+
+ if (top > SVC_GR_SCAN_TOP_MAX)
+ top = SVC_GR_SCAN_TOP_MAX;
+ if (top < SEMA_PA_MAX)
+ top = SEMA_PA_MAX;
+ return top;
+}
+
static int svc_read_va_push_ex(struct nv_vgpu_device *vgpu,
struct svc_channel *ch, u64 va, void *buf,
size_t len, bool skip_ipin)
@@ -15570,7 +15973,8 @@ static int svc_read_va_push_ex(struct nv_vgpu_device *vgpu,
break;
}
- if (ap == SVC_APERTURE_SYS && pa >= SEMA_PA_MAX) {
+ if (ap == SVC_APERTURE_SYS &&
+ pa >= svc_push_pa_ceiling()) {
last_rc = -EIO;
continue;
}
@@ -15940,10 +16344,14 @@ static int svc_chan_userd(struct nv_vgpu_device *vgpu, struct svc_channel *ch,
* FB-shadow sweep below can never reach it. The guest-RAM shape
* scan is sliced and budgeted like the ring scan; it validates
* every entry the candidate's put claims against the ring window
- * before latching (see svc_gr_ram_userd_scan).
+ * and admits the block only after its PUT advances across sightings
+ * (see svc_gr_ram_userd_scan; SESSION 154 liveness). The budget
+ * gate stays open while candidates are under watch, so the
+ * liveness confirmation cannot be starved by slice accounting.
*/
if (!ch->engine_type && ch->gr_ram_ring && !ch->userd_ok &&
- ch->gr_ram_userd_passes < svc_gr_ram_pass_budget()) {
+ (ch->gr_ram_userd_passes < svc_gr_ram_pass_budget() ||
+ ch->gr_ram_userd_watch_n)) {
u64 upa = 0;
u32 uput = 0, uget = 0;
@@ -15951,12 +16359,38 @@ static int svc_chan_userd(struct nv_vgpu_device *vgpu, struct svc_channel *ch,
ch->userd_pa = upa;
ch->userd_as = SVC_APERTURE_SYS;
ch->userd_ok = true;
- ch->last_get = 0;
+ /*
+ * SESSION 154 (M-GL1.2): seed the walk at the live
+ * block's PUT. The client's put is free-running
+ * (measured 0->3 with entries=2), so the pump's
+ * `put %= entries` alone can collapse onto
+ * last_get and consume NOTHING - r914 stayed at
+ * submissions=0 for minutes with put=12 % 2 = 0 and
+ * last_get=0. Consume up to entries-1 positions
+ * ENDING at the PUT: the newest slots the client is
+ * waiting on; normal slot-space tracking resumes
+ * from there.
+ */
+ if (ch->gpfifo_entries > 1 && uput) {
+ u32 span = uput < ch->gpfifo_entries ?
+ uput : ch->gpfifo_entries - 1;
+
+ ch->last_get = (uput % ch->gpfifo_entries +
+ ch->gpfifo_entries - span) %
+ ch->gpfifo_entries;
+ } else {
+ ch->last_get = 0;
+ }
+ ch->gr_ram_put_seeded = uput;
+ ch->gr_ram_userd_latch_put = uput;
+ ch->gr_ram_userd_latch_at = jiffies;
+ ch->gr_ram_userd_confirmed = false;
*put = uput;
*get = uget;
- pr_info("sema-chan: GR USERD h=0x%x rank=%u found by RAM scan -> pa=0x%llx (put=%u get=%u%s; entries ring-validated)\n",
+ pr_info("sema-chan: GR USERD h=0x%x rank=%u found by RAM scan -> pa=0x%llx (put=%u get=%u%s; entries ring-validated; speculative, pump confirms; walk seed last_get=%u)\n",
ch->h_object, ch->gr_index, upa, uput, uget,
- uget < uput ? "" : ", idle");
+ uget < uput ? "" : ", idle",
+ (u32)ch->last_get);
return 0;
}
}
@@ -17794,14 +18228,20 @@ static bool svc_gr_ram_ring_scan(struct nv_vgpu_device *vgpu,
* cursor-resumed budget as the ring scan (one SVC_GR_RAM_SLICE slice per
* pass, svc_gr_ram_pass_budget() slices before giving up).
*
- * A candidate is latched ONLY when every entry its PUT claims decodes to
- * a pushbuffer VA inside the ring's allocation window: the entries are
- * read at the ring VA's own offset (entry k at ring VA + k*8) and must
- * land within +/-SVC_GR_RAM_WIN/2 of the ring VA with a sane length -
- * the same gate the FB shape scan applies, so a coincidental put/get
- * pair costs a rejected candidate, never a wrong latch. Among valid
- * candidates a block with work PENDING (get < put) wins: the live block
- * of a stuck client always shows its unconsumed submission.
+ * A candidate passes the shape and entry gates first (every entry its PUT
+ * claims must decode to a pushbuffer VA inside the ring's allocation
+ * window: entry k at ring VA + (k % entries)*8, within +/-SVC_GR_RAM_WIN/2
+ * of the ring VA with a sane length), but admission is by LIVENESS, not by
+ * shape (SESSION 154): the GET/PUT gate alone admitted frozen or churning
+ * lookalike pages (r914: a low-RAM table whose +0x88/+0x8c words happened
+ * to read 12/8 and later 8/4 - adopted, the pump then read PUT=12 with
+ * entries=2, the modulo collapsed to 0 and NOTHING was consumed while the
+ * client retried for minutes). A passing candidate is WATCHED across
+ * passes; only a block whose PUT takes the client's own small step (+1 or
+ * +2) on two separate sightings is adopted as the live control block (the
+ * measured live block advances 1->2->3; the lookalikes sit still or jump
+ * by 4). The watch runs every call, so it keeps working past the slice
+ * budget (the gate in svc_chan_userd keeps calling while a watch is live).
*/
static bool svc_gr_ram_userd_scan(struct nv_vgpu_device *vgpu,
struct svc_channel *ch, u64 *pa_out,
@@ -17813,10 +18253,10 @@ static bool svc_gr_ram_userd_scan(struct nv_vgpu_device *vgpu,
u64 start = ch->gr_ram_userd_cursor;
u64 end, gpa;
u32 entries = ch->gpfifo_entries;
- u64 best = 0;
- u32 best_put = 0, best_get = 0;
- bool best_pending = false;
+ u64 live_pa = 0;
+ u32 live_put = 0, live_get = 0, live_prev = 0;
int ring_ap = SVC_APERTURE_SYS;
+ u32 wi;
int i;
unsigned long flags;
@@ -17835,6 +18275,83 @@ static bool svc_gr_ram_userd_scan(struct nv_vgpu_device *vgpu,
return false;
}
(void)svc_pin_lookup_any(ch->gpfifo_va, &ring_pa, &ring_ap);
+ /*
+ * SESSION 154 (M-GL1.2): liveness re-check of the watched candidates.
+ * Cheap (one 4-byte PUT read each, every pass) and the primary adoption
+ * site: a watched block whose PUT takes the client's own single +1 step
+ * is the live control block (the measured live block advances 1->2->3
+ * in 2-8 s steps; the registration window is 25-45 s, so one step must
+ * suffice). A block whose PUT jumps by a larger amount or decreases is
+ * a churning lookalike (r914's pa 0x9e200 marched -4; the dense
+ * lookalike pools churn +14) and its streak resets; one that goes
+ * unreadable or zero is dropped.
+ */
+ for (wi = 0; wi < ch->gr_ram_userd_watch_n; ) {
+ u64 wpa = ch->gr_ram_userd_watch[wi];
+ u32 hp = 0, hg = 0, last;
+
+ if (svc_read(vgpu, wpa + SVC_USERD_GP_PUT, &hp,
+ sizeof(hp)) || !hp || hp > 0x100000) {
+ ch->gr_ram_userd_watch_n--;
+ ch->gr_ram_userd_watch[wi] =
+ ch->gr_ram_userd_watch[ch->gr_ram_userd_watch_n];
+ ch->gr_ram_userd_watch_put[wi] =
+ ch->gr_ram_userd_watch_put[ch->gr_ram_userd_watch_n];
+ ch->gr_ram_userd_watch_hits[wi] =
+ ch->gr_ram_userd_watch_hits[ch->gr_ram_userd_watch_n];
+ continue;
+ }
+ last = ch->gr_ram_userd_watch_put[wi];
+ if (hp == last) {
+ wi++;
+ continue;
+ }
+ if (hp != last + 1) {
+ /*
+ * SESSION 154: a jump or a reset is the churn
+ * signature (the r916-r919 lookalike pools move by
+ * +4/-4/+14 or collapse to zero between sightings) -
+ * drop the entry instead of keeping a dead watch slot
+ * that would evict newer, better candidates.
+ */
+ ch->gr_ram_userd_watch_n--;
+ ch->gr_ram_userd_watch[wi] =
+ ch->gr_ram_userd_watch[ch->gr_ram_userd_watch_n];
+ ch->gr_ram_userd_watch_put[wi] =
+ ch->gr_ram_userd_watch_put[ch->gr_ram_userd_watch_n];
+ ch->gr_ram_userd_watch_hits[wi] =
+ ch->gr_ram_userd_watch_hits[ch->gr_ram_userd_watch_n];
+ continue;
+ }
+ ch->gr_ram_userd_watch_put[wi] = hp;
+ /*
+ * SESSION 154: ONE observed +1 step adopts. The live block
+ * advances ~2-8 s per step (measured) while the client's
+ * channel registration window is ~25-45 s, so waiting for
+ * a second step can outlive the window; a churning lookalike
+ * moves in larger jumps (measured +4/-4/+14) and a random
+ * page cannot produce this exact one-step signature with
+ * the shape+entry gates already applied.
+ */
+ ch->gr_ram_userd_watch_hits[wi]++;
+ (void)svc_read(vgpu, wpa + SVC_USERD_GP_GET, &hg,
+ sizeof(hg));
+ if (!hg || hg > hp) {
+ ch->gr_ram_userd_watch_hits[wi] = 0;
+ wi++;
+ continue;
+ }
+ live_pa = wpa;
+ live_put = hp;
+ live_get = hg;
+ live_prev = last;
+ ch->gr_ram_userd_watch_n = 0;
+ pr_info("sema-chan: GR USERD h=0x%x rank=%u LIVE (put %u -> %u across passes) -> pa=0x%llx put=%u get=%u\n",
+ ch->h_object, ch->gr_index, live_prev, live_put,
+ live_pa, live_put, live_get);
+ break;
+ }
+ if (!live_pa)
for (gpa = start; gpa + SEMA_PAGE_SIZE <= end;
gpa += SVC_SCAN_CHUNK) {
u64 chunk = min_t(u64, SVC_SCAN_CHUNK, end - gpa);
@@ -17844,7 +18361,7 @@ static bool svc_gr_ram_userd_scan(struct nv_vgpu_device *vgpu,
continue;
for (o = 0; o + 0x90 <= chunk; o += 0x100) {
u32 hp, hg, k;
- bool pending, ok = true;
+ bool ok = true;
memcpy(&hg, buf + o + SVC_USERD_GP_GET, 4);
memcpy(&hp, buf + o + SVC_USERD_GP_PUT, 4);
@@ -17857,12 +18374,15 @@ static bool svc_gr_ram_userd_scan(struct nv_vgpu_device *vgpu,
* far too weak: a low-RAM page at pa 0x7c000 carrying
* coincidental small numbers at +0x88/+0x8c was
* latched on r913 (the pass then read its put
- * forever and never advanced). Also accept a
- * free-running put (measured: the GL client's put
- * counts 0..3 with entries=2, so the old
- * put <= entries bound would reject the live block).
+ * forever and never advanced). SESSION 154: the put
+ * cap is 8 - the GL client's live block cycles 0..3
+ * with entries=2 (measured), while the r914 false
+ * latches read 12/8 and 16/16 and every previously
+ * measured lookalike counted 16 or more. Also accept
+ * a free-running put (the old put <= entries bound
+ * would reject the live block).
*/
- if (!hp || hp > 64 || hg > hp)
+ if (!hp || hp > 8 || hg > hp)
continue;
{
u32 z = 0, zi;
@@ -17916,24 +18436,57 @@ static bool svc_gr_ram_userd_scan(struct nv_vgpu_device *vgpu,
}
if (!ok)
continue;
- pending = hg < hp;
- /* First pending candidate wins; a pending
- * candidate replaces a non-pending one. */
- if (best && (best_pending || !pending))
- continue;
- best = gpa + o;
- best_put = hp;
- best_get = hg;
- best_pending = pending;
- if (pending)
- break;
+ /*
+ * SESSION 154: the live control block lives in a
+ * NEAR-EMPTY page (measured: put/get plus two more
+ * bytes non-zero - r914's live block; the X-channel FB
+ * block and the GL sysmem block both leave the leading
+ * 0x80 bytes zero). The r916-r919 candidate floods all
+ * came from dense pages (a dozen+ shape hits per page
+ * in tables and pools), so a page with more than 32
+ * non-zero bytes is rejected before it can occupy a
+ * watch slot.
+ */
+ {
+ u32 pstart = o & ~0xfffu;
+ u32 zc = 0, zi2;
+
+ for (zi2 = 0; zi2 < SEMA_PAGE_SIZE; zi2++)
+ if (!buf[pstart + zi2])
+ zc++;
+ if (zc < SEMA_PAGE_SIZE - 32)
+ continue;
+ }
+ /*
+ * SESSION 154 (M-GL1.2): a candidate that passed the
+ * shape gate, the entry validation, the page-density
+ * gate and the put cap is latched SPECULATIVELY. The
+ * old code latched on shape alone and a frozen/
+ * churning lookalike could hold the walk forever
+ * (r913: pa 0x7c000; r914: pa 0x9e200 at put=12);
+ * the strengthened gates reject those classes, and the
+ * pump now CONFIRMS the latch from its own per-pass
+ * read: a +1/+2 step confirms the block as the live
+ * one, a reset/jump or no movement within the bound
+ * unlatches it so the scan can adopt a better
+ * candidate (see svc_pump_channels).
+ */
+ live_pa = gpa + o;
+ live_put = hp;
+ live_get = hg;
+ ch->gr_ram_userd_watch_n = 0;
+ pr_info("sema-chan: GR USERD h=0x%x rank=%u LIVE (put step across passes) -> pa=0x%llx put=%u get=%u\n",
+ ch->h_object, ch->gr_index, live_pa,
+ live_put, live_get);
+ break;
}
- if (best_pending)
+ if (live_pa)
break;
}
ch->gr_ram_userd_cursor = (end >= top) ? 0 : end;
ch->gr_ram_userd_passes++;
- if (ch->gr_ram_userd_cursor == 0 && !ch->gr_ram_userd_logged) {
+ if (ch->gr_ram_userd_cursor == 0 && !ch->gr_ram_userd_logged &&
+ !ch->gr_ram_userd_watch_n) {
ch->gr_ram_userd_logged = true;
pr_info("sema-chan: GR USERD h=0x%x rank=%u RAM scan found no candidate in %u slice(s) (top 0x%llx)\n",
ch->h_object, ch->gr_index, ch->gr_ram_userd_passes,
@@ -17948,21 +18501,196 @@ static bool svc_gr_ram_userd_scan(struct nv_vgpu_device *vgpu,
for (i = 0; i < SVC_MAX_CHANNELS; i++) {
if (svc.chan[i].valid && svc.chan[i].vgpu == vgpu &&
svc.chan[i].h_object == ch->h_object) {
+ u32 wj;
+
svc.chan[i].gr_ram_userd_cursor =
ch->gr_ram_userd_cursor;
svc.chan[i].gr_ram_userd_passes =
ch->gr_ram_userd_passes;
svc.chan[i].gr_ram_userd_logged =
ch->gr_ram_userd_logged;
+ svc.chan[i].gr_ram_userd_watch_n =
+ ch->gr_ram_userd_watch_n;
+ for (wj = 0; wj < SVC_GR_USERD_WATCH; wj++) {
+ svc.chan[i].gr_ram_userd_watch[wj] =
+ ch->gr_ram_userd_watch[wj];
+ svc.chan[i].gr_ram_userd_watch_put[wj] =
+ ch->gr_ram_userd_watch_put[wj];
+ svc.chan[i].gr_ram_userd_watch_hits[wj] =
+ ch->gr_ram_userd_watch_hits[wj];
+ }
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&svc.chan_lock, flags);
+ if (!live_pa)
+ return false;
+ *pa_out = live_pa;
+ *put_out = live_put;
+ *get_out = live_get;
+ return true;
+}
+
+/*
+ * SESSION 154 (M-GL1.2): content anchor for a GR client push whose
+ * translation went stale. r913: the entry's read resolved through a
+ * VALIDATED pin that kept answering for a page the guest had since reused
+ * (`first8=00000001` through src=2) while the trusted walk answered from a
+ * wrong VASPACE (`pa=0x5c0000 aperture=vidmem plaus=0`) and the client's
+ * real push bytes sat in RAM on a page no registered directory covered.
+ * The client's submit pushes carry two stable anchors - the first method
+ * pair (SET_SUBDEVICE_MASK 0x0001fff0, measured on every GR submit push of
+ * this driver) and the release-group signature pair measured deep in the
+ * same pushes - so sweep guest RAM for them (one SVC_GR_RAM_SLICE slice per
+ * call, cursor-resumed, a few sweeps per entry VA), validate the page by
+ * re-reading it (prefix at the entry's own page offset + full method-stream
+ * parse + the signature present in the same page - a stale fragment page
+ * carrying only the signature fails), then pin it with svc_pin_store so the
+ * pump's retry reads the REAL bytes like any validated page.
+ */
+static const u8 svc_gr_anchor_pre[8] = {
+ 0xf0, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
+};
+static const u8 svc_gr_anchor_sig[16] = {
+ 0x43, 0x60, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x60, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00,
+};
+#define SVC_GR_ANCHOR_SWEEPS 3
+
+static bool svc_gr_anchor_scan(struct nv_vgpu_device *vgpu,
+ struct svc_channel *ch, u64 va, u32 len)
+{
+ static u8 *buf;
+ u64 top = gsp_sema_gr_scan_top;
+ u64 start = ch->gr_anchor_cursor;
+ u64 end, gpa, page_pa = 0;
+ u32 poff = (u32)(va & (SEMA_PAGE_SIZE - 1));
+ int i;
+ unsigned long flags;
+
+ if (len > SEMA_PAGE_SIZE)
+ return false;
+ if (ch->gr_anchor_va != va) {
+ ch->gr_anchor_va = va;
+ ch->gr_anchor_sweeps = 0;
+ ch->gr_anchor_cursor = 0;
+ start = 0;
+ }
+ if (ch->gr_anchor_sweeps >= SVC_GR_ANCHOR_SWEEPS)
+ return false;
+ if (top > SVC_GR_SCAN_TOP_MAX)
+ top = SVC_GR_SCAN_TOP_MAX;
+ if (top < SEMA_PAGE_SIZE || !len)
+ return false;
+ if (start >= top)
+ start = 0;
+ end = start + SVC_GR_RAM_SLICE;
+ if (end > top)
+ end = top;
+ if (!buf) {
+ buf = kmalloc(SVC_SCAN_CHUNK, GFP_KERNEL);
+ if (!buf)
+ return false;
+ }
+ if (!ch->gr_anchor_logged) {
+ ch->gr_anchor_logged = true;
+ pr_info("sema-anchor: h=0x%x va=0x%llx len=%u content anchor scan started\n",
+ ch->h_object, va, len);
+ }
+ for (gpa = start; !page_pa && gpa + SEMA_PAGE_SIZE <= end;
+ gpa += SVC_SCAN_CHUNK) {
+ u64 chunk = min_t(u64, SVC_SCAN_CHUNK, end - gpa);
+ u32 o;
+
+ if (svc_read(vgpu, gpa, buf, chunk))
+ continue;
+ for (o = 0; o + 24 <= chunk; o += 4) {
+ /*
+ * (a) the push's own first method pair - it sits at
+ * the entry VA's page offset, so only a hit whose
+ * page offset matches the entry's is a candidate.
+ */
+ if (!memcmp(buf + o, svc_gr_anchor_pre,
+ sizeof(svc_gr_anchor_pre)) &&
+ ((gpa + o) & (SEMA_PAGE_SIZE - 1)) == poff) {
+ page_pa = gpa + o - poff;
+ break;
+ }
+ /*
+ * (b) the release-group signature measured deep in
+ * the client's submit pushes; the page is validated
+ * below before it is pinned.
+ */
+ if (!memcmp(buf + o, svc_gr_anchor_sig,
+ sizeof(svc_gr_anchor_sig))) {
+ page_pa = (gpa + o) &
+ ~(u64)(SEMA_PAGE_SIZE - 1);
+ break;
+ }
+ }
+ }
+ ch->gr_anchor_cursor = (end >= top) ? 0 : end;
+ if (ch->gr_anchor_cursor == 0)
+ ch->gr_anchor_sweeps++;
+ spin_lock_irqsave(&svc.chan_lock, flags);
+ for (i = 0; i < SVC_MAX_CHANNELS; i++) {
+ if (svc.chan[i].valid && svc.chan[i].vgpu == vgpu &&
+ svc.chan[i].h_object == ch->h_object) {
+ svc.chan[i].gr_anchor_cursor = ch->gr_anchor_cursor;
+ svc.chan[i].gr_anchor_va = ch->gr_anchor_va;
+ svc.chan[i].gr_anchor_sweeps = ch->gr_anchor_sweeps;
+ svc.chan[i].gr_anchor_logged = ch->gr_anchor_logged;
break;
}
}
spin_unlock_irqrestore(&svc.chan_lock, flags);
- if (!best)
+ if (!page_pa)
return false;
- *pa_out = best;
- *put_out = best_put;
- *get_out = best_get;
+ /*
+ * Validate the candidate BEFORE pinning: the push's first method
+ * pair at the entry's own page offset, a full method-stream parse,
+ * and the release-group signature present in the same page (the
+ * remnant fragment at r914's pa 0x84aef000 carries the signature
+ * but fails the prefix/parse gate).
+ */
+ {
+ u8 *pb = kmalloc(SEMA_PAGE_SIZE, GFP_KERNEL);
+ u32 n = min_t(u32, len, SEMA_PAGE_SIZE - poff);
+ bool ok = false;
+
+ if (pb) {
+ const u8 *sig = NULL;
+
+ ok = !svc_read(vgpu, page_pa, pb, SEMA_PAGE_SIZE);
+ if (ok) {
+ u32 z;
+
+ for (z = 0; z + 16 <= SEMA_PAGE_SIZE; z++)
+ if (!memcmp(pb + z, svc_gr_anchor_sig,
+ sizeof(svc_gr_anchor_sig))) {
+ sig = pb + z;
+ break;
+ }
+ }
+ ok = ok && sig &&
+ !memcmp(pb + poff, svc_gr_anchor_pre,
+ sizeof(svc_gr_anchor_pre)) &&
+ svc_push_prefix_ok(pb + poff, n) &&
+ svc_method_stream_plausible(pb + poff, n);
+ kfree(pb);
+ }
+ if (!ok) {
+ pr_info("sema-anchor: h=0x%x va=0x%llx candidate pa=0x%llx rejected (content gate)\n",
+ ch->h_object, va, page_pa);
+ return false;
+ }
+ }
+ svc_pin_store(va & ~(u64)(SEMA_PAGE_SIZE - 1), page_pa,
+ SVC_APERTURE_SYS);
+ svc_lat_store(va & ~(u64)(SEMA_PAGE_SIZE - 1), page_pa,
+ SVC_APERTURE_SYS);
+ pr_info("sema-anchor: h=0x%x va=0x%llx -> pa=0x%llx (release-group signature anchored; pinned)\n",
+ ch->h_object, va, page_pa);
return true;
}
@@ -20306,6 +21034,14 @@ struct svc_fwd_req {
u32 gr_push_n;
u32 gr_push_qmd_hdr;
u32 gr_push_qmd_end;
+ /*
+ * r929 (GL 3D path): the captured raw 3D method window (gr_raw
+ * words gr_stream_off..gr_stream_off+gr_stream_n) replays verbatim;
+ * see struct gsp_phys_gr_submit.stream.
+ */
+ bool gr_stream_3d;
+ u32 gr_stream_off;
+ u32 gr_stream_n;
u32 gr_raw[SVC_GR_PUSH_WORDS];
u64 gr_qmd_dev_va;
/*
@@ -22743,6 +23479,16 @@ static void svc_fwd_gr_work(void)
gs.raw_words = svc_fwd_req.gr_push_n;
gs.raw_qmd_hdr = svc_fwd_req.gr_push_qmd_hdr;
gs.raw_qmd_end = svc_fwd_req.gr_push_qmd_end;
+ /*
+ * r929 (Path A): a 3D stream capture replays its raw window verbatim;
+ * the backend emits SET_OBJECT C597 + the window + the release tail
+ * instead of the compute prelude/QMD. The window is sliced from the
+ * section's C597 bind; see svc_gl_flush.
+ */
+ if (svc_fwd_req.gr_stream_3d) {
+ gs.stream = svc_fwd_req.gr_raw + svc_fwd_req.gr_stream_off;
+ gs.stream_n = svc_fwd_req.gr_stream_n;
+ }
gs.h_object = svc_fwd_req.gr_h_object;
/*
@@ -25086,6 +25832,10 @@ static int svc_gr_req_fill(struct nv_vgpu_device *vgpu,
svc_fwd_req.gr_push_n = svc_gr_cand.push_n;
svc_fwd_req.gr_push_qmd_hdr = svc_gr_cand.push_qmd_hdr;
svc_fwd_req.gr_push_qmd_end = svc_gr_cand.push_qmd_end;
+ /* r929 (Path A): carry the 3D method-window identity to the backend. */
+ svc_fwd_req.gr_stream_3d = svc_gr_cand.stream_3d;
+ svc_fwd_req.gr_stream_off = svc_gr_cand.stream_off;
+ svc_fwd_req.gr_stream_n = svc_gr_cand.stream_n;
memcpy(svc_fwd_req.gr_raw, svc_gr_cand.raw,
sizeof(svc_fwd_req.gr_raw));
svc_fwd_req.gr_qmd_dev_va = svc_gr_cand.qmd_dev_va;
@@ -28464,8 +29214,96 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
/* Initialize expected semaphore payload from the pool (if mapped). */
svc_init_expected_payload(vgpu, ch);
- if (put >= ch->gpfifo_entries)
+ /*
+ * SESSION 154 (M-GL1.2): speculative-latch confirmation for
+ * the RAM USERD. The scan latches a gated candidate right
+ * away (fast walk start); the pump is the judge: a +1/+2
+ * step of the block's PUT confirms it as the client's live
+ * control block, while a jump, a reset or no movement within
+ * the bound rejects it so the scan can adopt a better
+ * candidate. The live block advances 1->2->3 in 2-10 s
+ * (measured); the client's own release waits are ~11 s, so
+ * the no-advance bound must sit above those.
+ */
+ if (!ch->engine_type && ch->gr_ram_ring && ch->userd_ok &&
+ !ch->gr_ram_userd_confirmed) {
+ if (!ch->gr_ram_userd_latch_at)
+ ch->gr_ram_userd_latch_at = jiffies;
+ if (put == ch->gr_ram_userd_latch_put + 1 ||
+ put == ch->gr_ram_userd_latch_put + 2) {
+ ch->gr_ram_userd_confirmed = true;
+ pr_info("sema-chan: GR USERD h=0x%x pa=0x%llx speculative latch CONFIRMED (put %u -> %u)\n",
+ ch->h_object, ch->userd_pa,
+ ch->gr_ram_userd_latch_put, put);
+ } else if (put != ch->gr_ram_userd_latch_put ||
+ time_after(jiffies,
+ ch->gr_ram_userd_latch_at +
+ msecs_to_jiffies(15000))) {
+ unsigned long lflags;
+ int li;
+
+ pr_info("sema-chan: GR USERD h=0x%x pa=0x%llx speculative latch REJECTED (put=%u latch=%u%s)\n",
+ ch->h_object, ch->userd_pa, put,
+ ch->gr_ram_userd_latch_put,
+ put != ch->gr_ram_userd_latch_put ?
+ "" : " no advance in 15 s");
+ ch->userd_ok = false;
+ ch->userd_pa = 0;
+ ch->userd_as = 0;
+ ch->gr_ram_userd_latch_at = 0;
+ ch->gr_ram_userd_confirmed = false;
+ ch->gr_ram_put_seeded = 0;
+ ch->last_get = 0;
+ /*
+ * Persist immediately: the pump `continue`s
+ * here, skipping the end-of-pass write-back
+ * for this channel.
+ */
+ spin_lock_irqsave(&svc.chan_lock, lflags);
+ for (li = 0; li < SVC_MAX_CHANNELS; li++)
+ if (svc.chan[li].valid &&
+ svc.chan[li].vgpu == vgpu &&
+ svc.chan[li].h_object ==
+ ch->h_object) {
+ svc.chan[li].userd_ok = false;
+ svc.chan[li].userd_pa = 0;
+ svc.chan[li].userd_as = 0;
+ break;
+ }
+ spin_unlock_irqrestore(&svc.chan_lock, lflags);
+ continue;
+ }
+ }
+ if (put >= ch->gpfifo_entries) {
+ u32 put_raw = put;
+
put %= ch->gpfifo_entries;
+ /*
+ * SESSION 154 (M-GL1.2): a free-running producer (the
+ * GL client's control block counts past any small ring
+ * - put=12 with entries=2, measured) can collapse
+ * onto the pump's own position by the modulo: an even
+ * number of positions ahead and last_get == put, so
+ * the walk consumes NOTHING and the client's release
+ * is never applied (r914: submissions stayed 0 while
+ * the client retried for minutes). One-shot per put
+ * value: step the walk back one slot so it consumes
+ * the newest entry; normal slot-space tracking
+ * resumes from there and the next put value walks
+ * on.
+ */
+ if (ch->gpfifo_entries > 1 &&
+ put == (u32)ch->last_get &&
+ put_raw != ch->gr_ram_put_seeded) {
+ ch->gr_ram_put_seeded = put_raw;
+ ch->last_get = (put + ch->gpfifo_entries - 1) %
+ ch->gpfifo_entries;
+ pr_info_ratelimited("sema-chan: GR free-running put h=0x%x put=%u get=%u ent=%u -> walk seed last=%u (modulo collapse consumed nothing)\n",
+ ch->h_object, put_raw, get,
+ ch->gpfifo_entries,
+ (u32)ch->last_get);
+ }
+ }
/*
* A wrapped put means every slot may hold a new entry: clear
@@ -29186,6 +30024,47 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
}
/*
+ * SESSION 154 (M-GL1.2): a GR RAM-ring
+ * client push whose every primary
+ * source failed (r913: a validated pin
+ * kept answering for a page the guest
+ * reused and the wrong-VASPACE walk
+ * answer was implausible). The
+ * client's submit pushes carry stable
+ * content anchors, so sweep RAM for
+ * the page, validate it, pin it and
+ * re-read the entry through the fresh
+ * pin. When the anchor has not found
+ * the page yet, drop a stale validated
+ * pin (the read that failed came from
+ * src=2) so later reads prefer the
+ * walk and the anchor over it.
+ */
+ if (unreadable && !ch->engine_type &&
+ ch->gr_ram_ring && va && len) {
+ if (svc_gr_anchor_scan(vgpu, ch,
+ va, len) &&
+ !svc_read_va_push(vgpu, ch,
+ va, pb,
+ len) &&
+ svc_method_stream_plausible(pb,
+ len)) {
+ ch->rescue_last = 0;
+ ch->push_hold_until = 0;
+ svc.gpfifo_rescued++;
+ unreadable = false;
+ } else if (svc_read_last_src == 2) {
+ svc_pin_drop(va);
+ if (svc_pushpg_fb_log < 64) {
+ svc_pushpg_fb_log++;
+ pr_info("sema-pushpg: h=0x%x va=0x%llx stale validated pin dropped (content failed every source)\n",
+ ch->h_object,
+ va);
+ }
+ }
+ }
+
+ /*
* r715 pushdec witness: name the branch
* the entry takes straight after the
* read, before the rescue/hold paths.
@@ -31012,6 +31891,36 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
ch->gr_ram_userd_passes;
svc.chan[k].gr_ram_userd_logged =
ch->gr_ram_userd_logged;
+ /* SESSION 154 (M-GL1.2): liveness watch + the
+ * free-running-put seed + the push anchor
+ * cursor all survive the snapshot copy. */
+ {
+ u32 wj;
+
+ svc.chan[k].gr_ram_userd_watch_n =
+ ch->gr_ram_userd_watch_n;
+ for (wj = 0; wj < SVC_GR_USERD_WATCH;
+ wj++) {
+ svc.chan[k].gr_ram_userd_watch[wj] =
+ ch->gr_ram_userd_watch[wj];
+ svc.chan[k].gr_ram_userd_watch_put[wj] =
+ ch->gr_ram_userd_watch_put[wj];
+ svc.chan[k].gr_ram_userd_watch_hits[wj] =
+ ch->gr_ram_userd_watch_hits[wj];
+ }
+ }
+ svc.chan[k].gr_ram_put_seeded =
+ ch->gr_ram_put_seeded;
+ svc.chan[k].gr_ram_userd_latch_put =
+ ch->gr_ram_userd_latch_put;
+ svc.chan[k].gr_ram_userd_latch_at =
+ ch->gr_ram_userd_latch_at;
+ svc.chan[k].gr_ram_userd_confirmed =
+ ch->gr_ram_userd_confirmed;
+ svc.chan[k].gr_anchor_cursor = ch->gr_anchor_cursor;
+ svc.chan[k].gr_anchor_va = ch->gr_anchor_va;
+ svc.chan[k].gr_anchor_sweeps = ch->gr_anchor_sweeps;
+ svc.chan[k].gr_anchor_logged = ch->gr_anchor_logged;
/* SESSION 149 R15: M1.1b keep-alive state. */
svc.chan[k].gr_ram_kick = ch->gr_ram_kick;
svc.chan[k].gr_ram_refreshes =