summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-08 11:58:47 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-08 11:58:47 -0400
commitb345bc644520a7537b48a30bd03b4ca65587a2a3 (patch)
tree580bcc50e028998e1a9e0e309993b84b22498e94
parent8c72fa6aa5547ff6ee2452b0ff58e2a71124b1f4 (diff)
downloadopen-iov-b345bc644520a7537b48a30bd03b4ca65587a2a3.tar.gz
open-iov-b345bc644520a7537b48a30bd03b4ca65587a2a3.zip
vgsp-sema: reachable GR counter publish + injected-pin override at autoreg (FORENSICS-v2 H3 + anomaly 6)
FORENSICS-v2 H3: svc_counter_publish() was reachable only at the end of a fully pumped channel; when a GR channel's ring or USERD cannot be read the per-channel loop early-`continue`s above that call (USERD not resolvable, GP_PUT unreadable), so pub_calls stayed 0 even with all 20 GR counter slots registered and the client's tracker waiting on them. Duplicate the publish into the safety-net block at the top of the per-channel loop, next to svc_counter_autoreg/svc_counter_flush_pending: - the value is a real GPFIFO put read from the channel's registered USERD; when the USERD is not readable nothing is written, so a guessed put can never advance the client past work the pump cannot see; - the target is the proof-gated slot registration (injected pin or the read-back-validated derive), never an unproven page; - the same relay-hold guard as the end-of-pass call applies. The end-of-pass site now publishes for CE channels only, so a GR pass does not publish twice. Anomaly 6: an earlier-derived counter_pa could disagree with a later-arriving injected pin (r1: derived 0x123a43ff0 vs pin-backed 0x123a28ff0) and nothing re-checked the pin while counter_pa was already registered - a publish would then advance a page the client never polls. svc_counter_autoreg() now re-checks the rank's slot against the injected pin on every pass and re-registers (under chan_lock) on a mismatch, which also makes the new publish proof-gated through the pin. Evidence: FORENSICS-v2 H3 + anomaly 6; 11-hostdmesg-v2-r1.log:2429-2714 (20x "not at fb ... probe rc=-11", pub_calls=0, gr_rel=0)."
-rw-r--r--kernel/gsp_sema_service.c78
1 files changed, 74 insertions, 4 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 50639e3..ddc7078 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -4703,13 +4703,50 @@ static void svc_counter_autoreg(struct nv_vgpu_device *vgpu,
if (ch->engine_type || n_gr <= 0)
return;
+ if (ch->gr_index >= n_gr)
+ return;
+ va = 0x20400fec0ULL + (u64)(n_gr - 1 - ch->gr_index) * 0x10ULL;
if (ch->counter_pa) {
+ /*
+ * FORENSICS-v2 anomaly 6: a slot derived in an earlier pass
+ * can disagree with a later-arriving injected pin (r1:
+ * derived 0x123a43ff0 vs pin-backed 0x123a28ff0, off by
+ * 0x1b000). Injected pins are the authoritative guest truth -
+ * svc_counter_learn re-checks them when a release decodes -
+ * but nothing re-checked them while counter_pa was already
+ * registered and no push had been decoded yet, so a publish
+ * would have advanced a page the client never polls. Re-check
+ * the rank's slot on every pass and re-register on a mismatch
+ * so the publish is proof-gated through the pin.
+ */
+ u64 ipa = 0;
+ int iap = SVC_APERTURE_SYS;
+
+ if (!svc_ipin_lookup(va, &ipa, &iap) &&
+ iap == SVC_APERTURE_SYS &&
+ (ipa & ~0x3ULL) != ch->counter_pa) {
+ unsigned long flags;
+ u64 old = ch->counter_pa;
+ int j;
+
+ spin_lock_irqsave(&svc.chan_lock, flags);
+ ch->counter_pa = ipa & ~0x3ULL;
+ for (j = 0; j < SVC_MAX_CHANNELS; j++) {
+ if (svc.chan[j].valid &&
+ svc.chan[j].h_object == ch->h_object) {
+ svc.chan[j].counter_pa =
+ ch->counter_pa;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&svc.chan_lock, flags);
+ pr_info("sema-chan: counter pin override h=0x%x rank=%u pa=0x%llx (was 0x%llx; injected pin is authoritative)\n",
+ ch->h_object, ch->gr_index,
+ ch->counter_pa, old);
+ }
svc_counter_flush_pending(vgpu, ch);
return;
}
- if (ch->gr_index >= n_gr)
- return;
- va = 0x20400fec0ULL + (u64)(n_gr - 1 - ch->gr_index) * 0x10ULL;
/*
* The injected-pin table is the authoritative source; only it may
* shadow a self-discovered map entry (which can hold a stale
@@ -11193,8 +11230,34 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
* remains.
*/
if (!ch->engine_type) {
+ u32 pput = 0;
+
svc_counter_autoreg(vgpu, ch, n_gr);
svc_counter_flush_pending(vgpu, ch);
+ /*
+ * H3 (FORENSICS-v2): the end-of-pass publish below is
+ * unreachable on the passes that early-`continue`
+ * before it (USERD not resolvable, GP_PUT unreadable)
+ * even though the counter slot is registered and the
+ * client's tracker waits on it - v2 had pub_calls=0
+ * with 20/20 slots registered and the GR ctx-init
+ * releases unread. Publish here, next to the safety
+ * net, from the registered USERD when it is readable.
+ * The value is a real GPFIFO put read from the
+ * channel's own control block, never a guess, and the
+ * target is the proof-gated slot registration
+ * (injected pin, or the read-back-validated derive of
+ * svc_counter_autoreg). Skip while a relay hold owns
+ * the channel, exactly like the end-of-pass call.
+ */
+ if (ch->counter_pa && ch->userd_pa &&
+ !(svc_fwd_hold.active &&
+ svc_fwd_hold.vgpu == vgpu &&
+ svc_fwd_hold.ch_handle == ch->h_object) &&
+ !svc_read_ap(vgpu,
+ ch->userd_pa + SVC_USERD_GP_PUT,
+ ch->userd_as, &pput, sizeof(pput)))
+ svc_counter_publish(vgpu, ch, pput);
}
/*
@@ -12385,8 +12448,15 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu)
* safety net at the top of the pass, so this must not
* re-register: once per pass is enough and the derivation is
* not free.
+ *
+ * H3 (FORENSICS-v2): GR channels publish in the safety-net
+ * block at the top of the pass - the early `continue`s above
+ * make this call unreachable exactly when the ring or USERD
+ * cannot be read, which is the case the publish must still
+ * cover. Keep this site for the CE channels only, so a GR
+ * pass does not publish twice.
*/
- if (!held)
+ if (!held && ch->engine_type)
svc_counter_publish(vgpu, ch, put);
spin_lock_irqsave(&svc.chan_lock, flags);