summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-28 09:23:51 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-28 09:23:51 -0400
commitf3c4419a64b69f85b8a7170ff5d15c0bef802a02 (patch)
tree4ca1eb843040421765579074065e8697cc90b3e3 /kernel
parentbde9352a0f84bdf0ef7bdfdc150f5f11a9200af3 (diff)
downloadopen-iov-f3c4419a64b69f85b8a7170ff5d15c0bef802a02.tar.gz
open-iov-f3c4419a64b69f85b8a7170ff5d15c0bef802a02.zip
sema: GR ring autopin must not trust an early base or a pre-existing pin
Two failure modes kept every GR channel dark despite the ring layout being known: - The CE region size was latched on the first autopin attempt, which can run before all CE channels are registered; a too-small base then points every GR candidate at a chunk whose entry 0 is zeros, and it stays wrong for the life of the boot. Recompute the base from the live CE ranks on every pass. - An existing pin for a ring VA (e.g. from a convention guess) made the autopin mark the rank done without validating the ring - a wrong USERD is then never corrected. Validate the candidate chunk (entry 0 decode + USERD put/get) first and only fall back to trusting an existing pin when the chunk does not validate; on success overwrite the pin. Also log one diagnostic line per GR rank when the chunk does not validate yet, so a silent autopin failure is visible in dmesg. Live: fresh boot, no manual ring pins - 20/20 GR channels auto-pinned (correct USERD 0x142000 + 0x3000*i) and ran entry 0 (get=1 sub=1) once the push pages were bridged.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_sema_service.c105
1 files changed, 62 insertions, 43 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 7f39209..bf6bb01 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -2779,13 +2779,15 @@ static int svc_chan_entry0(struct nv_vgpu_device *vgpu, struct svc_channel *ch,
bool gsp_sema_gr_rings = true;
+static unsigned long svc_gr_ring_tried; /* ranks that got a diagnostic line */
+
static void svc_gr_ring_autopin(struct nv_vgpu_device *vgpu,
struct svc_channel *ch)
{
- u64 pa;
- int ap = SVC_APERTURE_SYS;
+ u64 base, pa, pin_pa, va = 0;
+ int pin_ap = SVC_APERTURE_SYS;
u8 e[SVC_GPFIFO_ENTRY_SIZE];
- u32 e0, e1, va, len, put = 0, get = 0;
+ u32 e0 = 0, e1 = 0, len = 0, put = 0, get = 0, max_ce = 0;
unsigned long flags;
int k;
@@ -2795,52 +2797,69 @@ static void svc_gr_ring_autopin(struct nv_vgpu_device *vgpu,
return;
if (test_bit(ch->gr_index, &svc_gr_ring_done))
return;
- if (!svc_pin_lookup(ch->gpfifo_va, &pa, &ap)) {
- /* Already translated (bridge/manual pin): nothing to do. */
- set_bit(ch->gr_index, &svc_gr_ring_done);
- svc_ring_n++;
- return;
- }
- if (!svc_gr_ring_base) {
- u32 max_ce = 0;
+ /*
+ * Recompute the CE region size on every attempt. An early attempt can
+ * run before all CE channels are registered; a latched-too-early base
+ * would point at the wrong chunk forever.
+ */
+ spin_lock_irqsave(&svc.chan_lock, flags);
+ for (k = 0; k < SVC_MAX_CHANNELS; k++)
+ if (svc.chan[k].valid && svc.chan[k].engine_type &&
+ svc.chan[k].ce_index + 1 > max_ce)
+ max_ce = svc.chan[k].ce_index + 1;
+ spin_unlock_irqrestore(&svc.chan_lock, flags);
+ if (!max_ce)
+ return; /* wait until the CE channels are known */
- spin_lock_irqsave(&svc.chan_lock, flags);
- for (k = 0; k < SVC_MAX_CHANNELS; k++)
- if (svc.chan[k].valid && svc.chan[k].engine_type &&
- svc.chan[k].ce_index + 1 > max_ce)
- max_ce = svc.chan[k].ce_index + 1;
- spin_unlock_irqrestore(&svc.chan_lock, flags);
- if (!max_ce)
- return; /* wait until the CE channels are known */
- svc_gr_ring_base = SVC_CE_RING_FB_BASE +
- SVC_CE_RING_FB_STRIDE * max_ce;
+ base = SVC_CE_RING_FB_BASE + SVC_CE_RING_FB_STRIDE * max_ce;
+ pa = base + SVC_GR_RING_STRIDE * ch->gr_index;
+
+ /*
+ * Validate the candidate chunk before trusting anything else: an
+ * existing pin may come from a convention guess that is wrong for
+ * this window (observed: a bogus vidmem offset for a GR ring VA,
+ * which made the old "already translated" shortcut skip the ring
+ * with a wrong USERD forever).
+ */
+ if (!svc_read_ap(vgpu, pa, SVC_APERTURE_FB, e, sizeof(e))) {
+ memcpy(&e0, e, 4);
+ memcpy(&e1, e + 4, 4);
+ va = ((u64)(e1 & 0xff) << 32) | (u64)(e0 & 0xfffffffc);
+ len = ((e1 >> 10) & 0x1fffff) << 2;
+ if (!(va & 0xfffff) && va >= SEMA_VA_LO && va < SEMA_VA_HI &&
+ len >= 4 && len <= 0x10000 &&
+ !svc_userd_probe(vgpu, pa + SVC_GR_USERD_OFF,
+ SVC_APERTURE_FB, ch->gpfifo_entries,
+ &put, &get)) {
+ svc_pin_store(ch->gpfifo_va, pa, SVC_APERTURE_FB);
+ svc_lat_store(ch->gpfifo_va, pa, SVC_APERTURE_FB);
+ ch->userd_pa = pa + SVC_GR_USERD_OFF;
+ ch->userd_as = SVC_APERTURE_FB;
+ ch->userd_ok = true;
+ svc_gr_ring_base = base;
+ set_bit(ch->gr_index, &svc_gr_ring_done);
+ svc_ring_n++;
+ pr_info("sema-chan: auto-pinned GR ring h=0x%x rank=%u va=0x%llx -> fb 0x%llx (entry0 va=0x%llx len=%u put=%u get=%u)\n",
+ ch->h_object, ch->gr_index, ch->gpfifo_va, pa,
+ va, len, put, get);
+ return;
+ }
}
- pa = svc_gr_ring_base + SVC_GR_RING_STRIDE * ch->gr_index;
- if (svc_read_ap(vgpu, pa, SVC_APERTURE_FB, e, sizeof(e)))
- return;
- memcpy(&e0, e, 4);
- memcpy(&e1, e + 4, 4);
- va = ((u64)(e1 & 0xff) << 32) | (u64)(e0 & 0xfffffffc);
- len = ((e1 >> 10) & 0x1fffff) << 2;
- if ((va & 0xfffff) || va < SEMA_VA_LO || va >= SEMA_VA_HI ||
- len < 4 || len > 0x10000)
- return;
- if (svc_userd_probe(vgpu, pa + SVC_GR_USERD_OFF, SVC_APERTURE_FB,
- ch->gpfifo_entries, &put, &get))
+ /*
+ * The candidate chunk did not validate. If another source already
+ * translates this ring VA (bridge or manual pin), trust it.
+ */
+ if (!svc_pin_lookup(ch->gpfifo_va, &pin_pa, &pin_ap)) {
+ set_bit(ch->gr_index, &svc_gr_ring_done);
+ svc_ring_n++;
return;
+ }
- svc_pin_store(ch->gpfifo_va, pa, SVC_APERTURE_FB);
- svc_lat_store(ch->gpfifo_va, pa, SVC_APERTURE_FB);
- ch->userd_pa = pa + SVC_GR_USERD_OFF;
- ch->userd_as = SVC_APERTURE_FB;
- ch->userd_ok = true;
- set_bit(ch->gr_index, &svc_gr_ring_done);
- svc_ring_n++;
- pr_info("sema-chan: auto-pinned GR ring h=0x%x rank=%u va=0x%llx -> fb 0x%llx (entry0 va=0x%llx len=%u put=%u get=%u)\n",
- ch->h_object, ch->gr_index, ch->gpfifo_va, pa, va, len, put,
- get);
+ if (!test_and_set_bit(ch->gr_index, &svc_gr_ring_tried))
+ pr_info("sema-chan: GR ring h=0x%x rank=%u not at fb 0x%llx yet (entry e0=0x%x e1=0x%x va=0x%llx len=%u)\n",
+ ch->h_object, ch->gr_index, pa, e0, e1, va, len);
}
static void svc_ring_autopin(struct nv_vgpu_device *vgpu,