summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-11-10 05:52:17 -0500
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-11-10 05:52:17 -0500
commitebf3130f4a2ee733e696e65249d908668fe78d4b (patch)
treec93e7b577c980a061eb567ec525a420ba8900c97 /kernel
parent90ab9baa309ae3c54d371546872c918c4c801920 (diff)
downloadopen-iov-ebf3130f4a2ee733e696e65249d908668fe78d4b.tar.gz
open-iov-ebf3130f4a2ee733e696e65249d908668fe78d4b.zip
vgsp: fix X DMA init + GR backend timeouts (CONT.399)
X-DMA-FIX (gsp_sema_service.c): - svc_chan_userd: run the pristine-FB USERD scan when the r496b sweep ran on a resolved-but-unvalidated ring (ring_pa!=0 but no entry decoded), not only when the ring is untranslated. A stale pin/foreign VAS left the channel without a USERD and X's 10 s pushbuffer NOP test timed out ('Failed to initialize DMA'). - svc_fb_userd_pristine_scan: claim-check with (off,off) so a second GR channel is not considered foreign against the first adopter's ring_off=0. - svc_chan_vas_candidate_promote: register the channel's own parked COPY_SERVER_PDES candidate at the pump turn once its GPFIFO entry 0 content-validates, instead of waiting for the 1000 ms age-out. - pump: fall back to svc_channel_owner_vas when hVASpace==0 (driver 'attach to the TSG address space') so the GR client ring resolves. PAIR-SAFE (gsp_phys_booter.c): default sema_gl_pair_close_entry and sema_gl_sph_bind_pair_safe ON. An unclosed BEGIN/END pair made the replayed window raise ILLEGAL_METHOD_IN_BEGIN_END (Class Error 0x2018 EC 0x0d), the FE parked before the release semaphore, and draws timed out (backend_timeout). Diagnostics: CB-DIAG/CB-DIAG2 per-push fixed-CB capture summary. Verified: X direct-rendering green on attempt 1; work_gr backend_timeout=0 (was 6), backend_ok=5-7.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_phys_booter.c4
-rw-r--r--kernel/gsp_sema_service.c233
2 files changed, 228 insertions, 9 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 733937c..189d8b7 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -18567,7 +18567,7 @@ MODULE_PARM_DESC(sema_gl_pair_orphan_drop,
* window, then let the normal walk rebase the model. Gated: default 0
* (legacy bytes).
*/
-static bool gsp_phys_rmcall_gr_pair_close_entry;
+static bool gsp_phys_rmcall_gr_pair_close_entry = true; /* PAIR-SAFE: default ON (CONT.399) */
module_param_named(sema_gl_pair_close_entry,
gsp_phys_rmcall_gr_pair_close_entry, bool, 0644);
MODULE_PARM_DESC(sema_gl_pair_close_entry,
@@ -18585,7 +18585,7 @@ MODULE_PARM_DESC(sema_gl_pair_close_entry,
* SET_PIPELINE_SHADER TYPE and REGISTER_COUNT are emitted BEFORE its SPH
* address pair (the client's own stage-1 order). Default 0 = legacy bytes.
*/
-static bool gsp_phys_rmcall_gr_sph_bind_pair_safe;
+static bool gsp_phys_rmcall_gr_sph_bind_pair_safe = true; /* PAIR-SAFE: default ON (CONT.399) */
module_param_named(sema_gl_sph_bind_pair_safe,
gsp_phys_rmcall_gr_sph_bind_pair_safe, bool, 0644);
MODULE_PARM_DESC(sema_gl_sph_bind_pair_safe,
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 12c6afd..079fce4 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -16345,6 +16345,9 @@ static u64 svc_gl_cb_sel_bc;
static bool svc_gl_cb_sel_seen;
static u32 svc_gl_cb_scan_log;
static u32 svc_gl_cb_apply_log;
+/* CB-DIAG (2026-11-10): bounded per-push capture summary. */
+static u32 svc_gl_cb_diag_log;
+#define SVC_GL_CB_DIAG_MAX 48u
#define SVC_GL_CB_IMG_LIMIT SEMA_PAGE_SIZE
@@ -16365,6 +16368,11 @@ static void svc_gl_cb_upload_scan(struct nv_vgpu_device *vgpu, u32 h_object,
u64 base = 0;
bool dirty = false;
u32 i;
+ /* CB-DIAG counters */
+ u32 nhdr = 0, nover = 0, nmatch = 0;
+ u32 n3800 = 0, n39c8 = 0, n39d0 = 0, n2380 = 0;
+ u32 n238c = 0, n2390 = 0, n3460 = 0;
+ u32 b3800_first = 0xffffffffu;
(void)vgpu;
if ((h_object & 0xfffff000u) != 0xbeef0000u || !buf || len < 16u)
@@ -16382,6 +16390,7 @@ static void svc_gl_cb_upload_scan(struct nv_vgpu_device *vgpu, u32 h_object,
u32 hdr, op, cnt, addr, w;
memcpy(&hdr, buf + off, 4);
+ nhdr++;
if (!hdr) {
off += 4u;
continue;
@@ -16396,6 +16405,17 @@ static void svc_gl_cb_upload_scan(struct nv_vgpu_device *vgpu, u32 h_object,
if (op == 4u) {
/* IMMD: 13-bit payload; offset/selector only. */
w = (hdr >> 16) & 0x1fffu;
+ if (addr == 0x3800u) {
+ n3800++;
+ if (b3800_first == 0xffffffffu)
+ b3800_first = w << 2;
+ } else if (addr == 0x238cu) {
+ n238c++;
+ } else if (addr == 0x2384u || addr == 0x2388u) {
+ n2380++;
+ } else if (addr == 0x3460u) {
+ n3460++;
+ }
if (addr == 0x3460u) {
base = (u64)w << 8;
if (base && base != svc_gl_cb_img_base)
@@ -16442,9 +16462,23 @@ static void svc_gl_cb_upload_scan(struct nv_vgpu_device *vgpu, u32 h_object,
* later, exactly like svc_scratch18_watch does, so the
* constant stream after the data block is still found.
*/
+ nover++;
off += 4u;
continue;
}
+ nmatch++;
+ if (addr == 0x238cu)
+ n238c++;
+ else if (addr >= 0x2380u && addr <= 0x238cu)
+ n2380++;
+ else if (addr == 0x3460u)
+ n3460++;
+ else if (addr == 0x39c8u)
+ n39c8++;
+ else if (addr == 0x39d0u)
+ n39d0++;
+ else if (addr >= 0x2390u && addr < 0x2390u + 0x1000u)
+ n2390++;
if (addr == 0x238cu) {
memcpy(&w, buf + off + 4u, 4);
svc_gl_cb_off = w;
@@ -16557,6 +16591,57 @@ static void svc_gl_cb_upload_scan(struct nv_vgpu_device *vgpu, u32 h_object,
}
off += 4u + cnt * 4u;
}
+ /*
+ * CB-DIAG (2026-11-10): one bounded line per beef push so a live run
+ * shows whether the MVP upload reaches the image at byte 0x80. If
+ * n3800>0 but b3800_first>=0x1000, the client uploads its transform
+ * at a CB offset this 4 KB image cannot hold; if n39c8/n2390==0 the
+ * upload-bearing push was never decoded; if m80 is non-zero but
+ * mvp=0 the captured block is not the projection.
+ */
+ if (svc_gl_cb_diag_log < SVC_GL_CB_DIAG_MAX) {
+ svc_gl_cb_diag_log++;
+ pr_info("vgsp-sema: sema-gl: CB-DIAG h=0x%x push=0x%llx len=%u hdr=%u run=%u over=%u match=%u[3800=%u 39c8=%u 39d0=%u 2380=%u 238c=%u 2390=%u 3460=%u] b3800first=0x%x words=%u off=0x%x base=0x%llx valid=%u mvp=%u m80=%08x %08x %08x %08x row3=%08x %08x %08x %08x\n",
+ h_object, push_va, len, nhdr, runs, nover, nmatch,
+ n3800, n39c8, n39d0, n2380, n238c, n2390, n3460,
+ b3800_first, words, svc_gl_cb_off, svc_gl_cb_img_base,
+ svc_gl_cb_img_valid ? 1u : 0u,
+ svc_gl_cb_mvp_ok(svc_gl_cb_img) ? 1u : 0u,
+ get_unaligned_le32(svc_gl_cb_img + 0x80),
+ get_unaligned_le32(svc_gl_cb_img + 0x84),
+ get_unaligned_le32(svc_gl_cb_img + 0x88),
+ get_unaligned_le32(svc_gl_cb_img + 0x8c),
+ get_unaligned_le32(svc_gl_cb_img + 0xb0),
+ get_unaligned_le32(svc_gl_cb_img + 0xb4),
+ get_unaligned_le32(svc_gl_cb_img + 0xb8),
+ get_unaligned_le32(svc_gl_cb_img + 0xbc));
+ {
+ u32 nz = 0, q;
+
+ for (q = 0; q < 0x100u; q++)
+ if (svc_gl_cb_img[q])
+ nz++;
+ pr_info("vgsp-sema: sema-gl: CB-DIAG2 h=0x%x img[00..1c]=%08x %08x %08x %08x %08x %08x %08x %08x img[90..ac]=%08x %08x %08x %08x %08x %08x %08x %08x nz=0x%x\n",
+ h_object,
+ get_unaligned_le32(svc_gl_cb_img + 0x00),
+ get_unaligned_le32(svc_gl_cb_img + 0x04),
+ get_unaligned_le32(svc_gl_cb_img + 0x08),
+ get_unaligned_le32(svc_gl_cb_img + 0x0c),
+ get_unaligned_le32(svc_gl_cb_img + 0x10),
+ get_unaligned_le32(svc_gl_cb_img + 0x14),
+ get_unaligned_le32(svc_gl_cb_img + 0x18),
+ get_unaligned_le32(svc_gl_cb_img + 0x1c),
+ get_unaligned_le32(svc_gl_cb_img + 0x90),
+ get_unaligned_le32(svc_gl_cb_img + 0x94),
+ get_unaligned_le32(svc_gl_cb_img + 0x98),
+ get_unaligned_le32(svc_gl_cb_img + 0x9c),
+ get_unaligned_le32(svc_gl_cb_img + 0xa0),
+ get_unaligned_le32(svc_gl_cb_img + 0xa4),
+ get_unaligned_le32(svc_gl_cb_img + 0xa8),
+ get_unaligned_le32(svc_gl_cb_img + 0xac),
+ nz);
+ }
+ }
if (!dirty || !words)
return;
svc_gl_cb_img_words += words;
@@ -24730,7 +24815,18 @@ static bool svc_fb_userd_pristine_scan(struct nv_vgpu_device *vgpu,
memchr_inv(base + off + SVC_USERD_GP_GET + 8, 0,
0x100 - SVC_USERD_GP_GET - 8))
continue;
- if (svc_fb_claim_foreign(vgpu, ch->h_object, 0, off))
+ /*
+ * X-DMA-FIX: pass the candidate offset as both ring and USERD.
+ * The old (0, off) form could not match a live CE rank's
+ * convention ring (ring_off == conv), but it also made every
+ * later pristine scan foreign against the first pristine
+ * adopter (which stores ring_off=0): (0 == 0) matched the
+ * ring_off check unconditionally. Matching the actual offset
+ * against both stored fields keeps the CE convention
+ * reservation and the one-owner-per-block rule while allowing
+ * a second GR channel its own block.
+ */
+ if (svc_fb_claim_foreign(vgpu, ch->h_object, off, off))
continue;
*off_out = off;
*put_out = hp;
@@ -24962,6 +25058,20 @@ static int svc_chan_userd(struct nv_vgpu_device *vgpu, struct svc_channel *ch,
{
u64 cand[8];
u32 as[8], nc = 0, i;
+ /*
+ * X-DMA-FIX (2026-11-10): "translated" is not "validated". svc_lookup
+ * can answer from a stale pin or a foreign VAS with a page whose
+ * GPFIFO entries are zero/foreign; the r496b FB sweep then rejects
+ * every candidate while ring_pa != 0, and the old !ring_pa pristine
+ * gate skipped the fallback in exactly that state. The channel then
+ * stayed without a USERD, the pump never wrote GP_GET, and the Xorg
+ * DDX pushbuffer NOP test timed out (10 s) -> "Failed to initialize
+ * DMA". Track whether the sweep actually ran and whether any entry
+ * decoded into this channel's push window so the pristine scan can
+ * still run for a resolved-but-unvalidated ring.
+ */
+ bool ring_entry_ok = false; /* a ring entry decoded in this sweep */
+ bool ring_sweep_ran = false; /* the r496b FB sweep ran this call */
int ring_alt = ring_ap == SVC_APERTURE_FB ? SVC_APERTURE_SYS
: SVC_APERTURE_FB;
@@ -25392,6 +25502,7 @@ ce_fb_scan:
ch->fb_userd_scan_at = jiffies;
ch->fb_userd_scan_n++;
ch->userd_scans++;
+ ring_sweep_ran = true;
lim = min_t(u64, svc_fb_shadow_size(vgpu), 0x20000000ULL);
if (base && lim) {
for (off = 0; off + 0xa0 <= lim; off += 0x200) {
@@ -25507,6 +25618,16 @@ ce_fb_scan:
rklen = elen;
break;
}
+ /*
+ * X-DMA-FIX: an entry that decodes into
+ * this channel's push window proves the
+ * resolved page really carries this
+ * channel's GPFIFO. This is the only
+ * "ring validated" signal the pristine
+ * gate below may use; wused only says the
+ * walk fallback was needed.
+ */
+ ring_entry_ok = true;
if (ch->engine_type &&
k == (u32)((hp - 1) %
ch->gpfifo_entries))
@@ -25632,17 +25753,34 @@ ce_fb_scan:
* starts iterating entries as soon as the ring walk lands, inside
* the driver's 10 s window.
*/
- if (!ch->engine_type && !ring_pa &&
+ /*
+ * X-DMA-FIX (2026-11-10): run the pristine scan when the ring is not
+ * translated (!ring_pa, as before) OR when the r496b sweep ran this
+ * call on a resolved ring but could not validate a single entry
+ * (ring_sweep_ran && !ring_entry_ok). A RAM-anchored ring
+ * (gr_ram_ring) keeps its own guest-RAM USERD scan and is excluded.
+ */
+ if (!ch->engine_type &&
+ (!ring_pa ||
+ (ring_sweep_ran && !ring_entry_ok && !ch->gr_ram_ring)) &&
ch->fb_userd_scan_n < 512 &&
- (!ch->fb_userd_scan_at ||
+ (!ch->fb_userd_scan_at || ring_sweep_ran ||
time_after_eq(jiffies,
(unsigned long)ch->fb_userd_scan_at +
msecs_to_jiffies(25)))) {
u64 off = 0;
u32 p = 0, g = 0;
- ch->fb_userd_scan_at = jiffies;
- ch->fb_userd_scan_n++;
+ /*
+ * X-DMA-FIX: the sweep above already spent this call's scan
+ * slot. Counting the combined call twice would halve the
+ * 512-scan (12.8 s at 25 ms) window the X 10 s DMA test
+ * relies on. Only a standalone pristine scan pays here.
+ */
+ if (!ring_sweep_ran) {
+ ch->fb_userd_scan_at = jiffies;
+ ch->fb_userd_scan_n++;
+ }
if (svc_fb_userd_pristine_scan(vgpu, ch, &off, &p, &g)) {
ch->userd_pa = off;
ch->userd_as = SVC_APERTURE_FB;
@@ -25650,9 +25788,11 @@ ce_fb_scan:
svc_fb_claim_add(ch->h_object, 0, off);
*put = p;
*get = g;
- pr_info("sema-chan: GR USERD h=0x%x rank=%u found by pristine-FB scan at fb 0x%llx (put=%u get=%u; ring 0x%llx not translated yet)\n",
+ pr_info("sema-chan: GR USERD h=0x%x rank=%u found by pristine-FB scan at fb 0x%llx (put=%u get=%u; ring va 0x%llx pa 0x%llx %s)\n",
ch->h_object, ch->gr_index, off, p, g,
- ch->gpfifo_va);
+ ch->gpfifo_va, ring_pa,
+ ring_pa ? "resolved but not validated"
+ : "not translated yet");
return 0;
}
}
@@ -29161,6 +29301,64 @@ static void svc_xvas_pdb_discover(struct nv_vgpu_device *vgpu, u64 ring_va,
}
}
+/*
+ * X-DMA-FIX (2026-11-10): register the channel's own parked
+ * COPY_SERVER_PDES candidate as soon as a pump pass can content-validate it
+ * against this channel's GPFIFO, instead of waiting for the age-out. The
+ * existing verifier is anchored to the current ring_pa; when the lookup
+ * answered a stale/foreign page that pair is wrong and the candidate never
+ * matches until the 1000 ms age-out registers it UNVERIFIED (r1585: first
+ * USERD sweep at +15 ms, age-out at +1.36 s, X's DMA test window 10 s).
+ * Anchor on the channel's own gpfifo_va and its entry 0 content instead.
+ */
+static void svc_chan_vas_candidate_promote(struct nv_vgpu_device *vgpu,
+ struct svc_channel *ch)
+{
+ u32 h = ch->h_vaspace;
+ int i;
+
+ if (!h || svc_vaspace_known(h) || !ch->gpfifo_va ||
+ ch->gpfifo_entries < 2)
+ return;
+ for (i = 0; i < SVC_VAS_CAND_MAX; i++) {
+ u64 wpa = 0;
+ int wap = SVC_APERTURE_SYS;
+ u8 e[SVC_GPFIFO_ENTRY_SIZE];
+ u32 e0 = 0, e1 = 0, len;
+ u64 eva;
+ struct sema_vaspace_desc d;
+
+ if (!svc_vas_cand[i].valid || svc_vas_cand[i].h_vaspace != h)
+ continue;
+ if (svc_gmmu_walk(vgpu, svc_vas_cand[i].pdb,
+ svc_vas_cand[i].entries,
+ ch->gpfifo_va & ~(u64)(SEMA_PAGE_SIZE - 1),
+ svc_vas_cand[i].vidmem ? SVC_APERTURE_FB
+ : SVC_APERTURE_SYS,
+ &wpa, &wap, false))
+ continue;
+ if (svc_read_ap(vgpu, wpa, wap, e, sizeof(e)))
+ continue;
+ memcpy(&e0, e, 4);
+ memcpy(&e1, e + 4, 4);
+ eva = ((u64)(e1 & 0xff) << 32) | (u64)(e0 & 0xfffffffc);
+ len = ((e1 >> 10) & 0x1fffff) << 2;
+ if (!eva || len < 4 || len > 0x10000 ||
+ !svc_gr_entry_win_ok(ch->gpfifo_va, eva))
+ continue;
+ memset(&d, 0, sizeof(d));
+ d.h_vaspace = h;
+ d.pdb = svc_vas_cand[i].pdb;
+ d.num_entries = svc_vas_cand[i].entries;
+ d.b_vidmem = svc_vas_cand[i].vidmem;
+ gsp_sema_service_vaspace_add(&d);
+ svc_vas_cand[i].valid = false;
+ pr_info("sema-svc: VAS 0x%x registered at channel 0x%x turn (parked candidate content-validated: gpfifo 0x%llx entry0 va=0x%llx len=%u)\n",
+ h, ch->h_object, ch->gpfifo_va, eva, len);
+ return;
+ }
+}
+
static void svc_gr_ring_autopin(struct nv_vgpu_device *vgpu,
struct svc_channel *ch)
{
@@ -40857,7 +41055,24 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
* entries through this channel's VAS.
*/
svc_cur_vas = ch->h_vaspace;
+ /*
+ * CONT.395/X-DMA-FIX: hVASpace==0 is the driver's "attach to
+ * the TSG address space". The GR client channel (0xbeef0100)
+ * publishes vas=0 while its CE siblings (0xbeef0101/0102) carry
+ * the real VAS 0xbeef0300; without this fallback svc_lookup
+ * cannot resolve the GR ring (ring_pa=0) and the beef draw
+ * stream is never decoded, so the fixed-CB scan never runs.
+ */
+ if (!svc_cur_vas)
+ svc_cur_vas = svc_channel_owner_vas(vgpu, ch->h_object);
+ /*
+ * X-DMA-FIX: the channel's own parked VAS must be usable before
+ * the first ring lookup/USERD sweep; the age-out registers it
+ * only after the X client's 10 s window. Content-gate it
+ * against this channel's GPFIFO.
+ */
+ svc_chan_vas_candidate_promote(vgpu, ch);
svc_ring_autopin(vgpu, ch);
/*
* SESSION 153-CONTINUED-75: heal a GR ring whose translation
@@ -41619,6 +41834,10 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
* svc_chanvas_lookup). Refreshed per channel turn; a channel
* without a known VAS clears it back to the old chain. */
svc_cur_vas = ch->h_vaspace;
+ /* CONT.395: hVASpace==0 attaches to the TSG VAS - use the
+ * sibling/owner VAS so releases resolve for the GR client. */
+ if (!svc_cur_vas)
+ svc_cur_vas = svc_channel_owner_vas(vgpu, ch->h_object);
/*
* C4 (GAP-1 H1): the GR walk witness - does put advance, and