diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-08 23:22:07 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-08 23:22:07 -0400 |
| commit | 06deaccd97b69f0beffe7d98ff9bfc1b0428591e (patch) | |
| tree | 43581557c4cb8147e8dd8b4d2c57ce2aa54c5bab /kernel | |
| parent | c31485b90d903bf8d3b20805b6aee2591911ad63 (diff) | |
| download | open-iov-06deaccd97b69f0beffe7d98ff9bfc1b0428591e.tar.gz open-iov-06deaccd97b69f0beffe7d98ff9bfc1b0428591e.zip | |
vgsp-sema: flush per-session discovery state when the last channel closes (ISSUE-0002)
A second CUDA client in the same guest runs in a new UVM VA space that
reuses the same VAs backed by new pages. The service's discovery state
(merged scan map, pinned push-buffer pairs, discovered page-directory
latch, direct_base, pool-PA cache, CE ring-done ranks, retry queue,
forward-release records, GRCE dst ring records, pending releases, VA
spaces) is learned from the live client and was never reset, so the next
session inherited translations for the previous session's pages: its
rings never auto-pinned, releases queued unresolved in svc_pending and
the client spun at its first channel. One clean boot always works, so
make the next session look like one: when the channel set empties mark
a flush; the next rebuild (forced immediately) resets the same state a
fresh boot starts with, and the new client re-discovers from its own
content.
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/gsp_sema_service.c | 90 |
1 files changed, 89 insertions, 1 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 611bb35..3b56c36 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -392,6 +392,21 @@ struct svc_pb_hit { static struct svc_pb_hit svc_pb[SVC_PB_MAX]; static u32 svc_pb_n; static u32 svc_ring_n; + +/* + * ISSUE-0002 multi-session: every translation source below (the merged scan + * map, the pinned push-buffer pairs, the discovered page-directory latch and + * the rebuilt direct_base) is learned from the live client's own page tables + * and push content. When the last channel of a client closes, a later client + * runs in a new UVM VA space that reuses the same VAs backed by new pages; + * anything learned from the previous session then answers for the wrong + * session (resolutions miss, releases queue in svc_pending and the new + * client spins). One clean boot always works, so make the next session look + * like one: when the channel set empties, request a flush and let the next + * rebuild start from the same empty state as a fresh boot. + */ +static u8 svc_sess_flush; +static unsigned long svc_ring_done; /* CE ranks whose ring is pinned (session state) */ static u8 svc_pb_passes; static u64 svc_pb_last; /* @@ -966,6 +981,19 @@ void gsp_sema_service_channel_del(u32 h_object) break; } } + /* + * ISSUE-0002: when the last channel closes, the next client is a new + * session in a new UVM VA space; its discovery state must not inherit + * this session's map/pins/page-directory latch. Request the flush + * here; it executes at the next rebuild (the only writer of that + * state) just like a fresh boot would start empty. + */ + for (i = 0; i < SVC_MAX_CHANNELS; i++) { + if (svc.chan[i].valid) + break; + } + if (i == SVC_MAX_CHANNELS) + svc_sess_flush = 1; spin_unlock_irqrestore(&svc.chan_lock, flags); /* @@ -2889,6 +2917,8 @@ static int svc_map_lookup_range(u64 gpu_va, u32 from, u32 to, u64 *pa) return -ENOENT; } +static void svc_session_flush(void); /* defined next to the pending queue */ + static void svc_rebuild(struct nv_vgpu_device *vgpu) { static u8 *buf; @@ -2899,6 +2929,15 @@ static void svc_rebuild(struct nv_vgpu_device *vgpu) u32 best_count = 0; u32 old_n; + /* + * ISSUE-0002: executed only between client sessions (the last + * channel has closed), so no live session loses translations. + */ + if (svc_sess_flush) { + svc_sess_flush = 0; + svc_session_flush(); + } + if (!buf) { buf = kmalloc(SEMA_PAGE_SIZE, GFP_KERNEL); if (!buf) @@ -6251,6 +6290,55 @@ static void svc_pending_add(u64 va, u32 payload, u32 op) } /* + * ISSUE-0002: reset every discovery artifact that was learned from the + * client that just closed (see svc_sess_flush). Runs at the top of + * svc_rebuild, after the last channel of the previous session unregistered, + * so the next client re-discovers its own map/pins/page directory from its + * own content exactly like a fresh boot - which is the configuration that + * is reproducibly green. Held releases belong to waiters that no longer + * exist; drop them instead of resolving them against the next session. + */ +static void svc_session_flush(void) +{ + unsigned long flags; + u32 i; + + svc.map_n = 0; + svc_pb_n = 0; + svc_pd_found = 0; + svc_pd_attempts = 0; + svc_pd_best = 0; + svc_pd_cands = 0; + svc_pd_ev_hash = 0; + svc.direct_base = 0; + svc_pending_n = 0; + svc_pending_next = 0; + svc_pool_pa_n = 0; + svc_pool_pa_next = 0; + svc_retry_next = 0; + svc_fwd_rel_n = 0; + svc_grce_dst_ring_next = 0; + svc_ring_done = 0; + memset(svc_retry_q, 0, sizeof(svc_retry_q)); + memset(svc_pool_pa, 0, sizeof(svc_pool_pa)); + memset(svc_fwd_rel, 0, sizeof(svc_fwd_rel)); + memset(svc_grce_dst_ring, 0, sizeof(svc_grce_dst_ring)); + for (i = 0; i < SVC_VAS_SLOTS; i++) { + if (svc_vas[i].data) + kfree(svc_vas[i].data); + svc_vas[i].data = NULL; + svc_vas[i].va_page = 0; + } + svc_vas_n = 0; + memset(svc.lat, 0, sizeof(svc.lat)); + spin_lock_irqsave(&svc.vas_lock, flags); + for (i = 0; i < SVC_MAX_VASPACES; i++) + svc.vas[i].valid = false; + spin_unlock_irqrestore(&svc.vas_lock, flags); + pr_info("sema-svc: session flush (all channels closed): discovery state reset for the next client\n"); +} + +/* * 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 @@ -13431,7 +13519,7 @@ static int svc_thread(void *arg) svc_handle_map_add(); svc_handle_chan_dump(vgpu); - if (!svc.direct_base || + if (svc_sess_flush || !svc.direct_base || time_after(jiffies, (unsigned long)(svc.last_build + 10 * HZ))) svc_rebuild(vgpu); |
