diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-22 13:10:38 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-22 13:10:38 -0400 |
| commit | bd2c763a92bb6b41272b0c2633120efaae8195d7 (patch) | |
| tree | b0e27a2ce9b06148e73ca0ca62bd40451292e3b4 /kernel | |
| parent | 6019b88222f37fe90d5de2c60d29c1dd7cc59565 (diff) | |
| download | open-iov-bd2c763a92bb6b41272b0c2633120efaae8195d7.tar.gz open-iov-bd2c763a92bb6b41272b0c2633120efaae8195d7.zip | |
r471 the counter-publish clamp: never advance the guest's completion word past a push whose content was still unwritten when its entry was consumed. The r477 failing probe run showed the reuse hazard directly: the channel registered with a residual USERD put (4) and ring slots 1-3 all-zero; the pump consumed them (the r470 hold only delays) and the end-of-pass publish then wrote val=2 while the client's own counter was still 1 - the D2H wait was satisfied before the copy ran and the readback drained a reused staging buffer holding the PREVIOUS copy's bytes (h_c[2] = c0's data). New per-channel gr_bad_put (lowest all-zero GR slot + 1, persisted in the pump write-back): set when a zero-content GR entry is held/consumed, cleared when a decode reads non-zero content (the rewinds re-read entries constantly, so it heals within passes); svc_counter_publish clamps val to it with a bounded pub-clamp witness.
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/gsp_sema_service.c | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 28e5906..ea96595 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -420,6 +420,18 @@ struct svc_channel { u64 gr_nocap_va; /* push VA the window was opened for */ unsigned long gr_nocap_since; /* absolute first-seen stamp */ /* + * r471: the completion-counter publish must never advance the + * guest's word past a push whose content was still unwritten when + * the entry was consumed (the streams_events staging-reuse + * corruption: the publish satisfied the client's D2H wait before + * its copy ran, and the readback drained a staging buffer that + * still held the PREVIOUS copy's bytes). Lowest ring slot consumed + * with all-zero content; 0 = none. Cleared when a later decode of + * a GR push reads non-zero content (the content landed - the rewinds + * re-read entries constantly, so this heals within a pass or two). + */ + u32 gr_bad_put; + /* * STALLFREE r225: stale-epoch / stale-replay wedge watchdog. A * channel that cannot make forward progress (last_get pinned behind * put, or a residual session put that never drops) is detected here @@ -15097,6 +15109,25 @@ static void svc_counter_publish(struct nv_vgpu_device *vgpu, return; val = put; + /* + * r471: never advance the guest's completion word past a push whose + * content was still unwritten when its entry was consumed. The + * clamp position is the lowest all-zero GR slot (gr_bad_put = slot + * + 1; 0 = none). Without it the publish can satisfy a D2H wait + * before its copy ran and the client drains a reused staging buffer + * holding the previous copy's bytes (streams_events h_c[2] = c0). + * The clamp heals when the rewinds re-read the slot with real + * content; the real completions still arrive through the decoded + * releases. + */ + if (!ch->engine_type && ch->gr_bad_put && val >= ch->gr_bad_put) { + val = ch->gr_bad_put - 1; + if (gsp_sema_dbg && svc.counter_pub_dbg < 96) { + svc.counter_pub_dbg++; + pr_info("sema-diag: pub-clamp h=0x%x pa=0x%llx val=%u (bad=%u) cur=%u\n", + ch->h_object, pa, val, ch->gr_bad_put, cur); + } + } if (val == ch->counter_last) return; /* @@ -21512,6 +21543,32 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) break; } } + /* + * r471: publish clamp bookkeeping. + * A zero-content GR entry that is + * held or consumed must never let + * the end-of-pass counter publish + * advance the guest's completion + * word past it (the client would + * satisfy its readback wait before + * the real push/copy ran - the + * staging buffer still holds the + * previous copy's bytes). Record + * the lowest zero slot; a decode + * that reads non-zero content + * clears the clamp. + */ + if (!ch->engine_type) { + if (all_zero) { + if (!ch->gr_bad_put || + (u32)idx + 1 < + ch->gr_bad_put) + ch->gr_bad_put = + (u32)idx + 1; + } else if (va && len) { + ch->gr_bad_put = 0; + } + } if (!ch->engine_type && va && len && gsp_phys_gr_forward_enabled() && (svc_gr_qmd_seen > 0 || @@ -22303,6 +22360,10 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) svc.chan[k].push_hold_until = ch->push_hold_until; svc.chan[k].defer_since = ch->defer_since; svc.chan[k].put_prev = ch->put_prev; + /* r471: the publish clamp watermark is set + * and cleared on the pump thread; persist it + * so the next pass still clamps. */ + svc.chan[k].gr_bad_put = ch->gr_bad_put; /* * Persist the cheap-wait window and the content * watermark: the pump works on a snapshot, so |
