diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-08 11:33:42 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-08 11:33:42 -0400 |
| commit | 9d484f295d96bf8d28d889df6274439efd5e4b55 (patch) | |
| tree | 5a84b5681f4de1abaec7c752a68d61b968ecd5f0 | |
| parent | f8edfb21e0880935fbe5aa62996fcd9af64c32ff (diff) | |
| download | open-iov-9d484f295d96bf8d28d889df6274439efd5e4b55.tar.gz open-iov-9d484f295d96bf8d28d889df6274439efd5e4b55.zip | |
vgsp-sema: retryable page-directory discovery from fresh guest-truth pairs (ISSUE-0001)
svc_pd_discover() latched svc_pd_tried before its first scan, so a call
that ran before the client's page tables existed (or before enough
verified push-buffer pairs were known) disabled the one kernel-native
source that translates pool/push windows the content-scanned map and the
published directories miss, for the rest of the boot. That source is what
svc_lookup_trusted() and svc_pool_pa_resolve() fall back to when the
userspace watcher pins race.
Make discovery retryable and evidence-driven: an attempt that cannot run
does not latch, a scan runs only when the sample-set fingerprint changed,
and the total is capped at SVC_PD_ATTEMPTS_MAX per boot - the same order
as the old per-pairing re-arm, which this supersedes (the stale re-arm in
svc_pb_pair is removed).
Evidence: ISSUE-0001, SA-STALL 3.2/3.4 (the deferred pool release of the
last CE push with no translation when watchers race).
| -rw-r--r-- | kernel/gsp_sema_service.c | 59 |
1 files changed, 50 insertions, 9 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 30b349b..5e887f1 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -1298,7 +1298,23 @@ static int svc_walk_vaspaces(struct nv_vgpu_device *vgpu, u64 va, static u64 svc_pd_base; static int svc_pd_ap; static u32 svc_pd_entries; -static u8 svc_pd_tried; +/* + * Discovery is retryable, not one-shot (ISSUE-0001 durable direction). + * + * The first call can happen before the client has written the page tables + * (the sva/spa samples do not exist yet) or before enough verified pairs are + * known; the old latch (`svc_pd_tried = 1` before any scan) then disabled the + * only kernel-native source that covers pool/push windows the content-scanned + * map and the published directories miss, for the rest of the boot, even + * though the samples it needs appear seconds later. Re-arm on new evidence + * instead: an attempt that could not run does not latch, and a scan runs only + * when the sample set changed since the last one (fingerprint), bounded by + * SVC_PD_ATTEMPTS_MAX scans per boot - the same order as the old + * one-per-pairing re-arm. + */ +#define SVC_PD_ATTEMPTS_MAX 8 +static u8 svc_pd_attempts; /* scans actually run (not sample misses) */ +static u64 svc_pd_ev_hash; /* fingerprint of the sample set last scanned */ static u8 svc_pd_found; #define SVC_PD_SAMPLES 8 #define SVC_PD_MIN_HITS 4 @@ -1343,9 +1359,8 @@ static void svc_pd_discover(struct nv_vgpu_device *vgpu) u8 *buf; int pass; - if (svc_pd_tried) + if (svc_pd_found || svc_pd_attempts >= SVC_PD_ATTEMPTS_MAX) return; - svc_pd_tried = 1; /* * Pinned push-buffer pairs are the strongest samples available: they @@ -1365,7 +1380,7 @@ static void svc_pd_discover(struct nv_vgpu_device *vgpu) if (n < SVC_PD_MIN_HITS) { n = 0; if (svc.map_n < SVC_PD_SAMPLES) - return; + return; /* no evidence yet: retry when pairs appear */ step = svc.map_n / SVC_PD_SAMPLES; for (i = 0; i < SVC_PD_SAMPLES; i++) { sva[i] = svc.map[i * step].gpu_page; @@ -1377,6 +1392,24 @@ static void svc_pd_discover(struct nv_vgpu_device *vgpu) } if (!n) return; + /* + * One scan per distinct sample set: a miss on the same evidence cannot + * succeed on a retry, and the scan costs a full RAM walk. A changed + * sample set (a new pinned pair, or a rebuilt map) is new evidence and + * re-arms the scan. The fingerprint uses the values, not just the + * count, because reaching four pinned pairs replaces the map samples + * one-for-one and can leave the count unchanged. + */ + { + u64 ev = n; + + for (i = 0; i < n; i++) + ev = (ev + sva[i] + (spa[i] << 12)) * 1000003; + if (svc_pd_attempts && ev == svc_pd_ev_hash) + return; + svc_pd_ev_hash = ev; + } + svc_pd_attempts++; /* Guest RAM first, then the emulated vidmem. Page directories are page * aligned, and every VA here is below 2^47, so the root entry that @@ -1970,7 +2003,14 @@ static int svc_lookup_trusted(struct nv_vgpu_device *vgpu, u64 va, u64 *addr, svc_lat_store(va, *addr, *ap); return 0; } - if (!svc_pd_tried) { + if (!svc_pd_found) { + /* + * Retryable discovery: on a boot where the directory is found + * only after the client's page tables appear, a later call + * with fresh verified pairs re-runs the scan. svc_pd_discover + * self-gates (evidence, attempt cap), so this is cheap when + * nothing changed. + */ svc_pd_discover(vgpu); if (!svc_walk_discovered(vgpu, va & ~0xfffULL, addr, ap)) { *addr += va & 0xfff; @@ -7594,11 +7634,12 @@ static void svc_pb_pair(struct nv_vgpu_device *vgpu, struct svc_channel *ch) /* * The pinned pairs are verified samples of UVM's own page directory; - * let the directory discovery run again with them, so the walk can - * translate the parts of the pool no base page describes. + * the next discovery call sees the grown sample set as fresh evidence + * and re-runs the scan (svc_pd_discover's attempt/evidence gates keep + * that bounded), so the walk can translate the parts of the pool no + * base page describes. On the old one-shot latch this pairing was the + * only re-arm; the retryable discovery no longer needs it. */ - if (!svc_pd_found) - svc_pd_tried = 0; pr_info("sema-svc: push-buffer pinned h=0x%x rank=%u slot=%u va=0x%llx -> pa=0x%llx (first push len=%u)\n", ch->h_object, ch->ce_index, want, va, hit_pa, len); |
