diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-09 14:59:41 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-09 14:59:41 -0400 |
| commit | 12c52b35252d8f46db1b7294ec8439530e4b71b9 (patch) | |
| tree | 99545bf50ec44231a6f23a694ecde354937480b8 | |
| parent | 088fa8f3e854ff33fe2d1188d2d237942956b918 (diff) | |
| download | open-iov-12c52b35252d8f46db1b7294ec8439530e4b71b9.tar.gz open-iov-12c52b35252d8f46db1b7294ec8439530e4b71b9.zip | |
vgsp-sema: CE USERD is layout-only; never latch the registered stale block (ISSUE-0002)
The pump calls svc_chan_userd before the ring is pinned; the old
candidate list then accepted the registered pool-relative userdMem
offset (stale cross-session 1/1) and userd_ok froze it for the session
(r42 c2 rank0 userd=0xf0000 while the layout block 0x90000 is the one
the guest writes). For CE channels: wait for the ring, validate its
entry0 (a CE push VA + len), then accept the layout USERD at
ring+0x10000 even while idle; anything else returns -ENOENT so the next
pass retries. Registered-offset candidates remain for non-CE channels.
| -rw-r--r-- | kernel/gsp_sema_service.c | 95 |
1 files changed, 49 insertions, 46 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 4cc1624..07aa8fa 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -7715,56 +7715,59 @@ static int svc_chan_userd(struct nv_vgpu_device *vgpu, struct svc_channel *ch, : SVC_APERTURE_FB; /* - * ISSUE-0002 (r40): prefer the layout-derived CE USERD and accept it - * while it is still idle. In a second session the registered userdMem - * offset (pool-relative, see below) aliases a DIFFERENT rank's real - * USERD whose stale put/get from the previous session won the - * coherent-pair probe while this channel's true block was still 0/0: - * c2 rank0 read rank3's stale 1/1 at FB 0xf0000 and the pump never - * followed rank0's own GP_PUT. The true CE USERD sits at - * ring + 0x10000 in the ring's aperture - confirmed by the guest - * CPU mapping of gpFifoGpuVa (GPU VA gpfifo+0x20000 maps to BAR - * offset ring+0x10000) and by every CE rank that resolved correctly. - * An idle (0/0) read is accepted here because the guest writes the - * block on the channel's first push; only unreadable or incoherent - * candidates fall through to the old list. + * ISSUE-0002 (r40/r42): CE USERD selection is layout-driven and must + * not fall back to the registered userdMem offset. That offset is + * pool-relative (see below) and in a second session it aliases a + * DIFFERENT rank's real USERD whose stale put/get from the previous + * session wins the coherent-pair probe while this channel's true + * block is still idle: c2 rank0 read rank3's stale 1/1 at FB 0xf0000 + * and the pump never followed rank0's own GP_PUT (r40/r42). The true + * CE USERD sits at ring + 0x10000 in the ring's aperture - confirmed + * by the guest CPU mapping of gpFifoGpuVa (GPU VA gpfifo+0x20000 maps + * to BAR offset ring+0x10000) and by every CE rank that resolved + * correctly. * - * The ring itself must validate first (entry0 = a CE push VA + len, - * same gate as svc_ring_autopin): r41 showed svc_lookup can answer a - * fresh gpfifo VA with an unrelated FB offset (rank4 -> 0x90000), and - * an idle acceptance on that bogus base cemented the wrong USERD. - */ - if (ring_pa && ch->engine_type != 0) { + * Ordering trap: this function is called on passes BEFORE the ring is + * pinned, and the old list then accepted the stale registered block. + * For CE channels therefore: no ring -> keep waiting; ring must + * validate (entry0 = a CE push VA + len, the same gate as + * svc_ring_autopin); then the layout USERD may be accepted while + * still idle (0/0) because the guest writes it on the first push. + * Any other outcome returns -ENOENT so the pump retries next pass - + * never the registered candidate. + */ + if (ch->engine_type != 0) { u8 e[SVC_GPFIFO_ENTRY_SIZE]; - u32 e0, e1, len; + u32 e0, e1, len, p = 0, g = 0; u64 va; - bool ring_ok = false; + int rc; - if (!svc_read_ap(vgpu, ring_pa, ring_ap, 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; - ring_ok = !(va & 0xfffff) && - va >= 0x120000000ULL && va < 0x121000000ULL && - len >= 4 && len <= 0x10000; - } - if (ring_ok) { - u32 p = 0, g = 0; - int rc = svc_userd_probe(vgpu, ring_pa + 0x10000, - ring_ap, ch->gpfifo_entries, - &p, &g); - - if (rc == 0 || rc == -EAGAIN) { - ch->userd_pa = ring_pa + 0x10000; - ch->userd_as = ring_ap; - ch->userd_ok = true; - *put = p; - *get = g; - return 0; - } - } + if (!ring_pa) + return -ENOENT; + /* + * svc_lookup can answer a fresh gpfifo VA with an + * unrelated FB offset (r41 rank4 -> 0x90000); validate the + * ring content before deriving anything from it. + */ + if (svc_read_ap(vgpu, ring_pa, ring_ap, e, sizeof(e))) + return -ENOENT; + 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 < 0x120000000ULL || + va >= 0x121000000ULL || len < 4 || len > 0x10000) + return -ENOENT; + rc = svc_userd_probe(vgpu, ring_pa + 0x10000, ring_ap, + ch->gpfifo_entries, &p, &g); + if (rc && rc != -EAGAIN) + return -ENOENT; + ch->userd_pa = ring_pa + 0x10000; + ch->userd_as = ring_ap; + ch->userd_ok = true; + *put = p; + *get = g; + return 0; } if (ring_pa) { |
