From 70198e7f0cde3768e00f7bbabe94fd26a2fcfa54 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Fri, 9 Oct 2026 15:54:23 -0400 Subject: vgsp-sema: resync a CE channel past residual USERD state on counter reset (ISSUE-0002) r48: the layout USERD of the second session's rank0 was accepted with the previous session's residual PUT=134; the pump consumed 34 stale slots, then the guest's fresh counter reset PUT to 1 and the pump wrapped: last_get never matched the guest's sequence and the live first push was not processed. The client was later seen pushing 216-byte no-release entries with crel frozen. A fresh channel's first push always publishes PUT<=1 and the client waits for its release before a second push, so a PUT>=2 at discovery is residual: seed last_get past it and resync to slot 0 when the guest's own counter reset drops PUT below put_prev. Clear the wait state once entries are consumed without a reset so later genuine wraps keep the normal wrap path. --- kernel/gsp_sema_service.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 1637d63..c545242 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -155,6 +155,18 @@ struct svc_channel { u64 userd_pa; u32 userd_as; /* NV_ADDR_SYSMEM / NV_ADDR_FBMEM */ bool userd_ok; /* userd_pa reads a plausible put/get */ + bool userd_wait_fresh; + /* ISSUE-0002 r48: the layout USERD was accepted + * with a PUT >= 2, i.e. with entries visible at + * discovery that can only be the PREVIOUS + * session's residual state (a fresh channel's + * first push always publishes PUT <= 1, and the + * client waits for that push's release before a + * second one). last_get was seeded past the + * residual entries; the guest's own counter + * reset (PUT dropping below put_prev) resyncs + * last_get to slot 0 so the live first push is + * processed instead of skipped. */ bool logged; /* one state log per channel */ u64 instance_pa; u64 notifier_pa; /* error notifier: NvNotification slots */ @@ -7830,6 +7842,22 @@ static int svc_chan_userd(struct nv_vgpu_device *vgpu, struct svc_channel *ch, ch->userd_pa = ring_pa + 0x10000; ch->userd_as = ring_ap; ch->userd_ok = true; + /* + * ISSUE-0002 (r48): a PUT >= 2 at discovery can only be + * the previous session's residual state - the client + * always publishes PUT=1 for a fresh channel's first + * push and waits for its release before a second. Seed + * last_get past the residual entries so the pump does + * not consume stale slots and desync from the guest's + * counter reset (r48: rank0 accepted PUT=134, the pump + * ran 34 stale entries, then the guest reset PUT=1 and + * the pump wrapped; the live first push was never + * processed). The pump detects the reset as a PUT drop + * and resyncs last_get to slot 0. + */ + ch->userd_wait_fresh = p >= 2; + if (ch->userd_wait_fresh) + ch->last_get = p; *put = p; *get = g; if (ch->ce_index < 32) @@ -12448,8 +12476,23 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) * processed (a rewind replay of the previous epoch must not * suppress them). */ - if (put < ch->put_prev) + if (put < ch->put_prev) { + /* + * ISSUE-0002 (r48): the layout USERD was accepted with + * residual entries (PUT >= 2, userd_wait_fresh); the + * guest's fresh counter reset now dropped PUT below + * put_prev. Resync last_get to slot 0 so the live + * first push is processed instead of skipped (the + * guest's push sequence restarts at slot 0). A later + * wrap on an established channel is unaffected: the + * flag is cleared by this first resync. + */ + if (ch->userd_wait_fresh) { + ch->last_get = 0; + ch->userd_wait_fresh = false; + } bitmap_zero(ch->slot_done_epoch, SVC_GPFIFO_MAX_BITS); + } ch->put_prev = put; /* @@ -13750,6 +13793,14 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) } spin_unlock_irqrestore(&svc.chan_lock, flags); + /* + * ISSUE-0002 (r48): a wait-fresh CE channel whose put only + * advanced (no counter reset seen) is genuinely running from + * the accepted position; drop the fresh-resync state so a + * later wrap is handled as a normal wrap. + */ + if (ch->userd_wait_fresh && ch->last_get != get_before) + ch->userd_wait_fresh = false; if (!held && ch->last_get != put) stalled = true; else if (!held && ch->last_get != get_before && -- cgit v1.2.3