diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-08 11:33:54 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-08 11:33:54 -0400 |
| commit | 0cb2a7ee0c6300415e4265cabb1f3007b8519c58 (patch) | |
| tree | 076f8c377bd377173bdcbccee2aad08f82996d69 /kernel | |
| parent | 9d484f295d96bf8d28d889df6274439efd5e4b55 (diff) | |
| download | open-iov-0cb2a7ee0c6300415e4265cabb1f3007b8519c58.tar.gz open-iov-0cb2a7ee0c6300415e4265cabb1f3007b8519c58.zip | |
vgsp-sema: keep push-buffer base discovery alive until the CE windows drain (ISSUE-0001)
svc_pb_discover() ran with a flat 12-pass budget for the whole boot and
then retired, even when a registered CE channel had no content-discovered
push-buffer base yet. A channel that appeared after the budget was spent
(including one reappearing with a new client session) then had no paired
window, so every push went through the slower per-page rescue and the
userspace watcher pin stream.
Stop the pass only after SVC_PB_IDLE_PASSES consecutive passes with no
new base AND with every registered CE channel either paired or having a
base recorded for its release slot, with a hard cap (SVC_PB_PASS_MAX) so
a channel that genuinely has no base page cannot scan forever. The 3 s
rate limit and chunked walk keep the service thread responsive; the
per-pass log now carries the idle count.
Evidence: ISSUE-0001; v2-r1 counter freeze (pb=6/12, see
backups/evidence-20261002-victory/11-hostdmesg-v2-r1.log) and the SA-STALL
class where the CE window was reached only after discovery had stopped.
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/gsp_sema_service.c | 82 |
1 files changed, 67 insertions, 15 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 5e887f1..8d223d8 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -389,6 +389,19 @@ static u32 svc_pb_n; static u32 svc_ring_n; static u8 svc_pb_passes; static u64 svc_pb_last; +/* + * Pass budget for svc_pb_discover: stop after SVC_PB_IDLE_PASSES + * consecutive passes with no new base (the discovery has drained), with a + * hard cap so a channel that has no push-buffer base page cannot keep the + * pass running for the boot. The old code stopped at a flat 12 passes, + * which could retire discovery before a later channel/session's base + * appeared (ISSUE-0001 durable direction). + */ +#define SVC_PB_IDLE_PASSES 6 +#define SVC_PB_PASS_MAX 48 +static u32 svc_pb_idle; /* consecutive passes with no new base */ +static u32 svc_pb_last_n; /* bases before the active pass */ +static unsigned long svc_pb_done; /* CE ranks whose push buffer is pinned */ static bool svc_pb_first_push(const u8 *buf) { @@ -6534,30 +6547,67 @@ static void svc_pb_discover(struct nv_vgpu_device *vgpu) unsigned long flags; u8 *buf; u64 gpa; - bool ce = false; + bool ce = false, need = false; int i; - if (svc_pb_passes >= 12 || svc_pb_n >= SVC_PB_MAX) - return; - if (time_before(jiffies, (unsigned long)svc_pb_last + 3 * HZ)) - return; - spin_lock_irqsave(&svc.chan_lock, flags); - for (i = 0; i < SVC_MAX_CHANNELS; i++) - if (svc.chan[i].valid && svc.chan[i].engine_type) { - ce = true; - break; - } + for (i = 0; i < SVC_MAX_CHANNELS; i++) { + u32 slot, k; + bool have = false; + + if (!svc.chan[i].valid || !svc.chan[i].engine_type) + continue; + ce = true; + if (svc.chan[i].ce_index >= 32) + continue; + if (test_bit(svc.chan[i].ce_index, &svc_pb_done)) + continue; + /* + * "Need" means no content-discovered base exists for this + * channel's release slot yet. Once the base is recorded the + * pass can idle out even if the pump has not paired the + * window; pairing is a pump concern, not a discovery one. + */ + slot = 4 * svc.chan[i].ce_index; + for (k = 0; k < svc_pb_n; k++) + if (svc_pb[k].slot == slot) { + have = true; + break; + } + if (!have) + need = true; + } spin_unlock_irqrestore(&svc.chan_lock, flags); if (!ce) return; + /* + * Discovery stays alive while it is still finding bases or while a + * registered CE channel has no paired base (ISSUE-0001 durable + * direction). The old flat 12-pass budget stopped the pass even when + * a channel had appeared (or reappeared with a new client session) + * after the budget was spent, so its push-buffer base was never + * paired and every push depended on the slower per-page rescue and + * watcher pins. Stop only after SVC_PB_IDLE_PASSES consecutive + * passes with no new base AND no unpaired channel, with a hard cap + * so a channel that genuinely has no base page cannot scan forever. + * The 3 s rate limit and the chunked walk keep the service thread + * responsive. + */ + if (svc_pb_n >= SVC_PB_MAX || svc_pb_passes >= SVC_PB_PASS_MAX) + return; + if (svc_pb_idle >= SVC_PB_IDLE_PASSES && !need) + return; + if (time_before(jiffies, (unsigned long)svc_pb_last + 3 * HZ)) + return; + buf = kmalloc(SVC_SCAN_CHUNK, GFP_KERNEL); if (!buf) return; svc_pb_passes++; svc_pb_last = jiffies; + svc_pb_last_n = svc_pb_n; for (gpa = 0; gpa < SEMA_PA_MAX; gpa += SVC_SCAN_CHUNK) { u32 o; @@ -6567,8 +6617,12 @@ static void svc_pb_discover(struct nv_vgpu_device *vgpu) svc_pb_note(vgpu, buf + o, gpa + o); } kfree(buf); - pr_info("sema-svc: push-buffer discovery pass %u done (%u bases, ring %u)\n", - svc_pb_passes, svc_pb_n, svc_ring_n); + if (svc_pb_n > svc_pb_last_n) + svc_pb_idle = 0; + else + svc_pb_idle++; + pr_info("sema-svc: push-buffer discovery pass %u done (%u bases, ring %u, idle %u)\n", + svc_pb_passes, svc_pb_n, svc_ring_n, svc_pb_idle); } /* @@ -7587,8 +7641,6 @@ static void svc_ring_autopin(struct nv_vgpu_device *vgpu, * carries the push-buffer VA, the base page was found by content and its * release names the channel's slot (4 bytes per rank). */ -static unsigned long svc_pb_done; /* CE ranks whose push buffer is pinned */ - static void svc_pb_pair(struct nv_vgpu_device *vgpu, struct svc_channel *ch) { u64 hit_pa = 0, va = 0; |
