summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-08 11:07:42 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-08 11:07:42 -0400
commitd3e67a9e9f9db2f5f4b5d5d04635b61ac6c566aa (patch)
treeb58b52df758bea0694a2e7cc05aa46bcf7b6b7c3
parent54bc80ae33addabc73a5bc1b0881423a63bc9862 (diff)
downloadopen-iov-d3e67a9e9f9db2f5f4b5d5d04635b61ac6c566aa.tar.gz
open-iov-d3e67a9e9f9db2f5f4b5d5d04635b61ac6c566aa.zip
vgsp-sema: kernel-native pool release translation + starvation telemetry (ISSUE-0001)
svc_pool_pa_resolve() could translate an UVM pool/waiter page only from watcher pins, the read-back cache, the self-discovered directory and a targeted rm_mem scan. When the userspace watcher races a boot and the self-discovered directory has no entry yet, a starved completion release sits in svc_pending[] forever (p13-r4: applied frozen at 89/90, gr_rel=0), the guest tracker never wakes and CUDA blocks at DEVICE. Add: - svc_walk_vaspaces() (the driver-published page directories - the walker svc_lookup_trusted always used, previously absent here); - a final proof-gated svc_lookup_trusted() fallback (also the only path that triggers svc_pd_discover()). Both read-back proven like every other source. Plus per-entry starvation telemetry (tries + bounded logs) so a future stall names the exact va.
-rw-r--r--kernel/gsp_sema_service.c100
1 files changed, 98 insertions, 2 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 48c070b..30b349b 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -2288,6 +2288,46 @@ static int svc_pool_pa_resolve(struct nv_vgpu_device *vgpu, u32 h, u64 va,
return 0;
}
}
+ /*
+ * Kernel-native driver-published ground truth: walk the VAS page
+ * directories the guest's driver publishes to us via
+ * svc_walk_vaspaces - the same walker svc_lookup_trusted has always
+ * used for ring/VA translations, but deliberately absent from this
+ * pool resolver until now.
+ *
+ * The pool resolver could previously translate only from
+ * watcher-injected pins, the read-back cache, the self-discovered
+ * directory and a targeted rm_mem scan. On boots where the userspace
+ * watcher races (its kprobes arm after the client's first CE push,
+ * the scanner misses the injection window, ...) and the
+ * self-discovered directory has not learned the pool's page tables
+ * yet, every one of those sources can miss. The completion release
+ * for the client's last CE push then sits in svc_pending[] forever:
+ * svc_apply_pending() logs "not translatable yet: keep" every pass,
+ * the guest's uvm_tracker_wait_for_entry never wakes, the client
+ * never creates its GR channels, and the CUDA context setup spins at
+ * DEVICE with an empty GR ring. That is the stalled-boot signature
+ * (ISSUE-0001); walking the driver-published directories removes
+ * the watcher race from the completion path instead of retrying
+ * around it. Read-back proven like every other source, so a stale
+ * directory entry cannot poison a release.
+ */
+ if (!svc_walk_vaspaces(vgpu, page, &wpa, &wap)) {
+ u64 exact = (wpa & ~(u64)(SEMA_PAGE_SIZE - 1)) |
+ (va & (SEMA_PAGE_SIZE - 1));
+
+ if (!svc_read_ap(vgpu, exact, svc_ap_of(wap), &probe,
+ sizeof(probe))) {
+ svc_pool_pa_store(va, exact, wap);
+ svc_tr_clear_failure(page);
+ if (src)
+ *src = "vaspaces";
+ svc_pool_pa_note(h, va, exact, "vaspaces");
+ *addr = exact;
+ *ap = wap;
+ return 0;
+ }
+ }
if (!svc_targeted_resolve(vgpu, va, &pa)) {
/* Read back before trusting: an unreadable page is not kept. */
if (!svc_read_ap(vgpu, pa, SVC_APERTURE_SYS, &probe,
@@ -2302,6 +2342,29 @@ static int svc_pool_pa_resolve(struct nv_vgpu_device *vgpu, u32 h, u64 va,
return 0;
}
}
+ /*
+ * Last resort: the general resolver chain (watcher pins of any kind,
+ * the learned latency/map caches, the driver-published and
+ * self-discovered walks, then svc_pd_discover(), whose one-shot
+ * page-directory discovery is NOT reachable from any source above -
+ * svc_pd_discover() only ever runs from inside svc_lookup_trusted()).
+ * Without this call the pool resolver could miss forever on a boot
+ * where the self-discovered directory has not been populated yet
+ * even though the driver-published page tables are present in guest
+ * RAM. Proof-gated like every other source.
+ */
+ if (!svc_lookup_trusted(vgpu, va, &pa, ap)) {
+ if (!svc_read_ap(vgpu, pa, svc_ap_of(*ap), &probe,
+ sizeof(probe))) {
+ svc_pool_pa_store(va, pa, *ap);
+ svc_tr_clear_failure(page);
+ if (src)
+ *src = "trusted";
+ svc_pool_pa_note(h, va, pa, "trusted");
+ *addr = pa;
+ return 0;
+ }
+ }
return -ENOENT;
}
@@ -5976,6 +6039,16 @@ struct svc_pending_release {
u64 va;
u32 payload;
u32 op; /* 0 = RELEASE, 1 = REDUCTION (add payload) */
+ /*
+ * Resolution attempts that found no translation for this VA.
+ * Telemetry for ISSUE-0001: a pending release that keeps failing to
+ * translate starves the guest's tracker wait forever (the client
+ * never reaches GR setup). Historically that state was invisible in
+ * the logs - svc_apply_pending() just retried quietly. The counter
+ * makes the starvation nameable (VA + payload + age) whenever it
+ * happens again.
+ */
+ u32 tries;
};
static struct svc_pending_release svc_pending[SVC_PENDING_MAX];
@@ -6000,6 +6073,21 @@ static void svc_pending_add(u64 va, u32 payload, u32 op)
svc_pending[i].va = va;
svc_pending[i].payload = payload;
svc_pending[i].op = op;
+ svc_pending[i].tries = 0;
+}
+
+/*
+ * Loud, bounded telemetry for a starved deferred release (ISSUE-0001).
+ * Called from the two "no translation yet, keep" paths of
+ * svc_apply_pending() with an incremented try count; logs at 4/64/1024
+ * attempts so a live stall names the exact VA that is starving the guest
+ * tracker without flooding a healthy run.
+ */
+static void svc_pending_note_starved(struct svc_pending_release *p)
+{
+ if (p->tries == 4 || p->tries == 64 || p->tries == 1024)
+ pr_info("sema-svc: pending release starved va=0x%llx payload=%u tries=%u (no VA->PA for the pool/waiter page; the guest tracker cannot complete until this applies)\n",
+ p->va, p->payload, p->tries);
}
static void svc_apply_pending(struct nv_vgpu_device *vgpu)
@@ -6022,16 +6110,24 @@ static void svc_apply_pending(struct nv_vgpu_device *vgpu)
*/
if (svc_pool_pa_resolve(vgpu, 0, p->va, &ppa, &pap,
NULL)) {
- i++; /* not translatable yet: keep */
+ /* not translatable yet: keep (ISSUE-0001) */
+ p->tries++;
+ svc_pending_note_starved(p);
+ i++;
continue;
}
if (svc_read_ap(vgpu, ppa, svc_ap_of(pap), &cur,
sizeof(cur))) {
+ p->tries++;
+ svc_pending_note_starved(p);
i++;
continue;
}
} else if (svc_sem_read(vgpu, p->va, &cur)) {
- i++; /* translation still missing: keep waiting */
+ /* translation still missing: keep waiting */
+ p->tries++;
+ svc_pending_note_starved(p);
+ i++;
continue;
}
if (p->payload <= cur) {