From b839a12de675818f41a8cb388e5663d804c2a1f7 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Sun, 11 Oct 2026 17:23:45 -0400 Subject: vgsp-sema: session boundary is a client change, not the last channel closing (ISSUE-0002) r79 regression from keeping the CeUtils scrubber channel registered (client-scoped deletion): the session flush trigger required EVERY channel to close, and the scrubber never closes, so the per-session discovery reset never ran and the next client inherited the previous session's map/pins (the r9 class; the first client of r79 wedged the guest on an rwsem with no output). The session is now keyed by owning client: channel_add records svc_sess_client and requests the flush when a channel of a DIFFERENT client registers while the previous session is live (then waits for it, exactly like a pending flush); channel_del's flush trigger counts only channels of the current session client, so persistent kernel/GPU-global channels (the scrubber) no longer keep a dead session open. --- kernel/gsp_sema_service.c | 50 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) (limited to 'kernel') diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 638a866..9f967a5 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -451,6 +451,11 @@ static u32 svc_ring_n; */ static u8 svc_sess_flush; static u8 svc_sess_active; /* a channel of a session has registered (until flush) */ +static u32 svc_sess_client; /* owning client of the current session's channels. + * The kernel/GPU-global channels (the CeUtils + * scrubber) persist across clients; the session + * boundary is a change of owning client, not the + * last channel closing (ISSUE-0002 r79). */ static struct completion svc_sess_flush_done; /* a pending flush has executed */ static unsigned long svc_ring_done; /* CE ranks whose ring is pinned (session state) */ static u8 svc_pb_passes; @@ -949,6 +954,20 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc) if (!desc->engine_type) WRITE_ONCE(svc_gr_content_rearm, true); + /* + * ISSUE-0002 (r79): the kernel/GPU-global channels (the CeUtils + * scrubber, entries=4096) persist across client sessions, so the + * "last channel closed" trigger never fires once they are kept + * registered. The real session boundary is a change of owning + * client: when a channel of a new client registers while the + * previous session's state is still live, request the same flush a + * session end would (the wait below then lets svc_thread execute it + * before this session's state exists). + */ + if (desc->h_client && svc_sess_client && + desc->h_client != svc_sess_client && svc_sess_active) + WRITE_ONCE(svc_sess_flush, 1); + /* * ISSUE-0002: a session-end flush may still be pending on svc_thread. * It must not execute after this session's state exists (r9: the @@ -1050,6 +1069,8 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc) } ch->valid = true; svc_sess_active = 1; + if (desc->h_client) + svc_sess_client = desc->h_client; ch->h_object = desc->h_object; ch->h_client = desc->h_client; ch->engine_type = desc->engine_type; @@ -1136,19 +1157,28 @@ void gsp_sema_service_channel_del(u32 h_object, u32 h_client) 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. Only the transition - * from "a session existed" to "empty" may request it: redundant - * deletes for handles that never registered would otherwise re-flush - * (and wipe) a session that started after the real one ended. + * ISSUE-0002: when the last channel of the current session's client + * 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. Only the transition from "a session existed" to + * "empty" may request it: redundant deletes for handles that never + * registered would otherwise re-flush (and wipe) a session that + * started after the real one ended. Persistent kernel/GPU-global + * channels (a different owning client, e.g. the CeUtils scrubber) do + * not keep the session open (ISSUE-0002 r79: with the scrubber kept + * registered the old all-channels check never fired and the next + * client inherited stale state). */ if (svc_sess_active) { for (i = 0; i < SVC_MAX_CHANNELS; i++) { - if (svc.chan[i].valid) - break; + if (!svc.chan[i].valid) + continue; + if (svc_sess_client && svc.chan[i].h_client && + svc.chan[i].h_client != svc_sess_client) + continue; + break; } if (i == SVC_MAX_CHANNELS) { svc_sess_active = 0; -- cgit v1.2.3