summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-23 18:40:12 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-23 18:40:12 -0400
commit41f6bf1a07fbf33036d31fe64c98d64cfe621e67 (patch)
treed748c222cb7170ef165f100cb9658b1b8e78f8a2
parent4f28cbed07a20b290891e7325a07072326c78167 (diff)
downloadopen-iov-41f6bf1a07fbf33036d31fe64c98d64cfe621e67.tar.gz
open-iov-41f6bf1a07fbf33036d31fe64c98d64cfe621e67.zip
r576 diag: launch-decision + event-post payload witnesses (gsp_phys_gr_dec_log, gsp_rm_ev_log)
The GR launch-decision dump logs, for the first N draws whose fill saw dropped pages / a partial argument set or whose decision point carries a dstpend/copy-pending verdict: the decision verdicts, the launch's resolved data pages (VA -> guest PA), the live deferred-write records and the pending CE opdef entries - the operand evidence needed to explain a red draw of the readback lottery (the wrong-but-similar page hypothesis). The event-post witness logs every EVENT_POST_EVENT record actually sent with its full payload (hClient/hEvent/notifyIndex/data) and send result, plus a sentinel when post_all_events found valid events but posted none while none were dedup-skipped. This is the evidence needed when a client parks waiting for a completion wake-up that never arrives (the 2nd-client device-state spin): it shows whether the post was generated, for whom, and whether the status-queue write accepted it or dropped it (burst cap / ring full). Both are inert by default (budget 0).
-rw-r--r--kernel/gsp_rm_emulation.c80
-rw-r--r--kernel/gsp_rm_emulation.h9
-rw-r--r--kernel/gsp_sema_service.c68
-rw-r--r--kernel/gsp_sema_service.h11
-rw-r--r--kernel/main.c24
5 files changed, 182 insertions, 10 deletions
diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c
index 90556f2..d381c65 100644
--- a/kernel/gsp_rm_emulation.c
+++ b/kernel/gsp_rm_emulation.c
@@ -726,6 +726,34 @@ static int gsp_rm_send(struct gsp_emu_context *ctx, u32 function,
}
/*
+ * r576 DIAG: event-post payload witness (gsp_rm_ev_log=N budget per boot).
+ * Every EVENT_POST_EVENT record actually sent is logged with its full
+ * payload and send result: when a client parks waiting for a completion
+ * wake-up that never arrives, this shows whether the post was generated,
+ * with which hClient/hEvent, and whether the status-queue write accepted it
+ * (rc=0) or dropped it (burst cap / ring full => rc<0). The companion
+ * sentinel fires only when post_all_events found valid events but could not
+ * post any of them while none were dedup-skipped - the "client waits, host
+ * thinks it posted" anomaly. 0 (default) = off.
+ */
+unsigned gsp_rm_ev_log_max;
+static unsigned gsp_rm_ev_log;
+
+static void gsp_rm_evlog_post(struct nv_vgpu_device *vgpu,
+ const struct gsp_rm_emu *rm, u32 idx,
+ const char *src, int rc)
+{
+ if (!gsp_rm_ev_log_max || gsp_rm_ev_log >= gsp_rm_ev_log_max)
+ return;
+ gsp_rm_ev_log++;
+ nv_vgpu_info(vgpu,
+ "vgsp-rm: EVPOST %s ev[%u] cl=0x%x ev=0x%x nidx=0x%x data=0x%x rc=%d",
+ src, idx, rm->events[idx].h_client,
+ rm->events[idx].h_event, rm->events[idx].notify_index,
+ rm->events[idx].data, rc);
+}
+
+/*
* Post the GPU event that tells the guest's CPU-side RM a channel's
* submitted work is complete. A real GPU raises an interrupt, the host GSP
* turns it into an NV_VGPU_MSG_EVENT_POST_EVENT record for the client's
@@ -763,9 +791,16 @@ int gsp_rm_post_channel_event(struct nv_vgpu_device *vgpu, u32 h_src)
memcpy(payload + 8, &rm->events[i].notify_index, 4);
memcpy(payload + 12, &rm->events[i].data, 4);
/* info16@16, status@20, eventDataSize@24, bNotifyList@28 = 0 */
- if (gsp_rm_send_flags(ctx, GSPRM_RPC_EVENT_POST_EVENT, 0, 0,
- payload, sizeof(payload), true))
- continue;
+ {
+ int rc = gsp_rm_send_flags(ctx,
+ GSPRM_RPC_EVENT_POST_EVENT,
+ 0, 0, payload,
+ sizeof(payload), true);
+
+ gsp_rm_evlog_post(vgpu, rm, i, "chan", rc);
+ if (rc)
+ continue;
+ }
rm->events[i].last_post = jiffies;
posted++;
}
@@ -785,10 +820,17 @@ int gsp_rm_post_channel_event(struct nv_vgpu_device *vgpu, u32 h_src)
memcpy(payload + 4, &rm->events[i].h_event, 4);
memcpy(payload + 8, &rm->events[i].notify_index, 4);
memcpy(payload + 12, &rm->events[i].data, 4);
- if (gsp_rm_send_flags(ctx, GSPRM_RPC_EVENT_POST_EVENT,
- 0, 0, payload,
- sizeof(payload), true))
- continue;
+ {
+ int rc = gsp_rm_send_flags(ctx,
+ GSPRM_RPC_EVENT_POST_EVENT,
+ 0, 0, payload,
+ sizeof(payload),
+ true);
+
+ gsp_rm_evlog_post(vgpu, rm, i, "chanfb", rc);
+ if (rc)
+ continue;
+ }
rm->events[i].last_post = jiffies;
posted++;
}
@@ -909,13 +951,31 @@ int gsp_rm_post_all_events(struct nv_vgpu_device *vgpu)
memcpy(payload + 4, &rm->events[i].h_event, 4);
memcpy(payload + 8, &rm->events[i].notify_index, 4);
memcpy(payload + 12, &rm->events[i].data, 4);
- if (gsp_rm_send(ctx, GSPRM_RPC_EVENT_POST_EVENT, 0, 0,
- payload, sizeof(payload)))
- continue;
+ {
+ int rc = gsp_rm_send(ctx, GSPRM_RPC_EVENT_POST_EVENT,
+ 0, 0, payload, sizeof(payload));
+
+ gsp_rm_evlog_post(vgpu, rm, i, "all", rc);
+ if (rc)
+ continue;
+ }
rm->events[i].last_post = jiffies;
n++;
}
+ /*
+ * r576 DIAG: the anomaly sentinel - valid events existed, none were
+ * dedup-skipped, yet not one post could be sent. Every send rc is in
+ * the EVPOST lines above (budget permitting).
+ */
+ if (!n && !skipped && gsp_rm_ev_log_max &&
+ gsp_rm_ev_log < gsp_rm_ev_log_max) {
+ gsp_rm_ev_log++;
+ nv_vgpu_info(vgpu,
+ "vgsp-rm: EVPOST all: no postable event (events_n=%u dropped=%llu)",
+ rm->events_n, rm->events_dropped);
+ }
+
if (n)
return 0;
/* -EAGAIN: valid events existed but every one was dedup-skipped
diff --git a/kernel/gsp_rm_emulation.h b/kernel/gsp_rm_emulation.h
index 45fcc64..9dc354d 100644
--- a/kernel/gsp_rm_emulation.h
+++ b/kernel/gsp_rm_emulation.h
@@ -207,6 +207,15 @@ struct gsp_rm_emu {
unsigned long event_burst_at; /* m171: burst window start (time-based reset) */
};
+/*
+ * r576 DIAG: per-boot witness budget for GSP event-post payloads (default 0
+ * = off). Nonzero logs every EVENT_POST_EVENT record actually sent (slot,
+ * hClient, hEvent, notifyIndex, data, send rc) and the sentinel case of a
+ * post_all_events call that found nothing postable - the evidence needed
+ * when a client parks waiting for a completion wake-up that never arrives.
+ */
+extern unsigned gsp_rm_ev_log_max;
+
int gsp_rm_emu_init(struct nv_vgpu_device *vgpu, struct gsp_emu_context *ctx);
void gsp_rm_emu_fini(struct gsp_emu_context *ctx);
int gsp_rm_post_channel_event(struct nv_vgpu_device *vgpu, u32 h_src);
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index fbc0772..dfe7c3f 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -20986,6 +20986,65 @@ static bool svc_copy_pending(void)
return pending;
}
+/*
+ * r576 DIAG: the GR launch-decision witness (gsp_phys_gr_dec_log=N budget
+ * per boot). Logged from svc_gr_request right after the fill, for launches
+ * whose fill saw dropped pages / a partial argument set or whose decision
+ * point carries a dstpend/copy-pending verdict - i.e. exactly the draws the
+ * r566/r567/r568 guards can defer or (on their blind spots) forward. The
+ * data-page list is the operand evidence: each VA -> guest PA the engine
+ * will read; a wrong-but-valid PA here (vs the page the client's H2D wrote
+ * for the same VA) is the failure mechanism the readback lottery draws.
+ */
+unsigned gsp_phys_gr_dec_log_max;
+static unsigned svc_gr_dec_log;
+
+static void svc_gr_dec_dump(const struct svc_channel *ch)
+{
+ bool dstpend, copypend;
+ u32 i, n = 0;
+
+ if (svc_gr_dec_log >= gsp_phys_gr_dec_log_max)
+ return;
+ svc_gr_dec_log++;
+
+ dstpend = svc_gr_dstpend_overlap();
+ copypend = svc_copy_pending();
+
+ pr_info("vgsp-gr: GR-DEC h=0x%x push=0x%llx n_data=%u dropped=%u partial=%d stalled=%d dstpend=%d copypend=%d defer_exp=%d\n",
+ ch->h_object, svc_gr_cand.push_va, svc_fwd_req.gr_n_data,
+ svc_fwd_req.gr_dropped,
+ svc_fwd_req.gr_arg_partial ? 1 : 0,
+ svc_gr_data_stalled ? 1 : 0,
+ dstpend ? 1 : 0, copypend ? 1 : 0,
+ svc_gr_defer_expired(svc_gr_cand.push_va) ? 1 : 0);
+
+ for (i = 0; i < svc_fwd_req.gr_n_data && i < 32; i++)
+ pr_info("vgsp-gr: GR-DEC data[%u] va=0x%llx pa=0x%llx ap=%u reloc=0x%llx\n",
+ i, svc_fwd_req.gr_data[i].va,
+ svc_fwd_req.gr_data[i].pa, svc_fwd_req.gr_data[i].ap,
+ svc_fwd_req.gr_data[i].reloc_va);
+
+ for (i = 0, n = 0; i < svc_dstpend_n && n < 16; i++) {
+ if (!svc_dstpend[i].va_page)
+ continue;
+ n++;
+ pr_info("vgsp-gr: GR-DEC dstpend va=0x%llx age_ms=%u\n",
+ svc_dstpend[i].va_page,
+ jiffies_to_msecs(jiffies - svc_dstpend[i].since));
+ }
+
+ for (i = 0, n = 0; i < SVC_OPDEF_SLOTS && n < 8; i++) {
+ if (!svc_opdef[i].active || !svc_opdef[i].n)
+ continue;
+ n++;
+ pr_info("vgsp-gr: GR-DEC opdef h=0x%x entry=%u n=%u age_ms=%u\n",
+ svc_opdef[i].h_object, svc_opdef[i].entry_idx,
+ svc_opdef[i].n,
+ jiffies_to_msecs(jiffies - svc_opdef[i].since));
+ }
+}
+
static bool svc_gr_request(struct nv_vgpu_device *vgpu,
const struct svc_channel *ch)
{
@@ -21084,6 +21143,15 @@ static bool svc_gr_request(struct nv_vgpu_device *vgpu,
return false;
}
/*
+ * r576 DIAG: witness the decision inputs of the draws the guards
+ * care about (see svc_gr_dec_dump).
+ */
+ if (gsp_phys_gr_dec_log_max &&
+ (svc_fwd_req.gr_dropped > 0 || svc_fwd_req.gr_arg_partial ||
+ svc_gr_data_stalled || svc_gr_dstpend_overlap() ||
+ svc_copy_pending()))
+ svc_gr_dec_dump(ch);
+ /*
* r541: launch-ordering guard. The fill's data pages are what the
* engine will read; if any of them is the target of a device copy
* that decoded but has NOT executed yet (svc_dstpend_note - the
diff --git a/kernel/gsp_sema_service.h b/kernel/gsp_sema_service.h
index ee5c48a..31512ef 100644
--- a/kernel/gsp_sema_service.h
+++ b/kernel/gsp_sema_service.h
@@ -292,6 +292,17 @@ extern unsigned gsp_phys_gr_hold_max_ms;
* hold and its absolute bound apply.
*/
extern unsigned gsp_phys_gr_fill_hold;
+
+/*
+ * r576 DIAG: per-boot witness budget for the GR launch-decision dump
+ * (default 0 = off). Nonzero logs, for the first N launches whose fill saw
+ * dropped pages / a partial argument set or whose decision point had a
+ * dstpend/copy-pending verdict: the decision verdicts, the launch's
+ * resolved data pages (VA -> guest PA), the live deferred-write records and
+ * the pending CE opdef entries - the evidence needed to explain a red draw
+ * of the readback lottery.
+ */
+extern unsigned gsp_phys_gr_dec_log_max;
extern unsigned gsp_sema_walk_trace;
/*
diff --git a/kernel/main.c b/kernel/main.c
index 3ee5c23..d0eb2b4 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -2788,6 +2788,30 @@ MODULE_PARM_DESC(gsp_phys_gr_hold_max_ms, "Absolute GR fill hold bound in ms, ke
*/
module_param_named(gsp_phys_gr_fill_hold, gsp_phys_gr_fill_hold, uint, 0644);
MODULE_PARM_DESC(gsp_phys_gr_fill_hold, "GR fill hold on unresolved operand pages (default 0: log-only, never hold)");
+
+/*
+ * r576 DIAG: per-boot witness budget for the GR launch-decision dump (see
+ * svc_gr_dec_dump in gsp_sema_service.c). Nonzero logs, for the first N
+ * launches whose fill saw dropped pages / a partial argument set or whose
+ * decision point carries a dstpend/copy-pending verdict: the decision
+ * verdicts, the launch's resolved data pages (VA -> guest PA), the live
+ * deferred-write records and the pending CE opdef entries. 0 (default) =
+ * off.
+ */
+module_param_named(gsp_phys_gr_dec_log, gsp_phys_gr_dec_log_max, uint, 0644);
+MODULE_PARM_DESC(gsp_phys_gr_dec_log, "GR launch-decision dump budget per boot (default 0: off)");
+
+/*
+ * r576 DIAG: per-boot witness budget for GSP event-post payloads (see
+ * gsp_rm_evlog_post in gsp_rm_emulation.c). Nonzero logs every
+ * EVENT_POST_EVENT record actually sent, with its full payload
+ * (hClient/hEvent/notifyIndex/data) and send result - the evidence needed
+ * when a client parks waiting for a completion wake-up that never arrives.
+ * 0 (default) = off.
+ */
+module_param_named(gsp_rm_ev_log, gsp_rm_ev_log_max, uint, 0644);
+MODULE_PARM_DESC(gsp_rm_ev_log, "GSP event-post payload witness budget per boot (default 0: off)");
+
module_param_named(gsp_sema_walk_trace, gsp_sema_walk_trace, uint, 0644);
MODULE_PARM_DESC(gsp_sema_walk_trace, "Trace GMMU walks for unresolved high-VA operands (default 0: off)");
module_param_named(gsp_sema_winfill, gsp_sema_winfill, bool, 0644);