summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-08 11:33:54 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-08 11:33:54 -0400
commit0cb2a7ee0c6300415e4265cabb1f3007b8519c58 (patch)
tree076f8c377bd377173bdcbccee2aad08f82996d69
parent9d484f295d96bf8d28d889df6274439efd5e4b55 (diff)
downloadopen-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.
-rw-r--r--kernel/gsp_sema_service.c82
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;