summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/gsp_sema_service.c188
1 files changed, 140 insertions, 48 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 41a5c58..ef89a52 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -8409,6 +8409,51 @@ static bool svc_gr_cand_push_seen(u64 push_va)
return false;
}
+/*
+ * r391f: once-only identity for GRCE inline writes. A push's
+ * LOAD_INLINE_DATA runs author bytes into the vidmem shadow; a re-decode of
+ * the same push (replay/rescue/rehold) must not re-run them - the target
+ * page may have been superseded in between (victory_reduce: the client's
+ * cudaMemset inline write re-zeroed the accumulator after the launch's
+ * copy-out, the sum=0 class). Keyed by (push_va, push content hash, dst,
+ * bytes): the release payload inside every push makes the hash unique per
+ * push, so a new client's identical-looking write never collides. Session
+ * scoped (cleared by svc_session_flush). Service-thread only.
+ */
+#define SVC_INLINE_ONCE_SLOTS 64
+static struct {
+ u64 push_va;
+ u64 hash;
+ u64 dst;
+ u32 bytes;
+} svc_inline_once[SVC_INLINE_ONCE_SLOTS];
+static u32 svc_inline_once_next;
+
+static bool svc_inline_once_seen(u64 push_va, u64 hash, u64 dst, u32 bytes)
+{
+ u32 i;
+
+ if (!push_va)
+ return false;
+ for (i = 0; i < SVC_INLINE_ONCE_SLOTS; i++)
+ if (svc_inline_once[i].push_va == push_va &&
+ svc_inline_once[i].hash == hash &&
+ svc_inline_once[i].dst == dst &&
+ svc_inline_once[i].bytes == bytes)
+ return true;
+ return false;
+}
+
+static void svc_inline_once_store(u64 push_va, u64 hash, u64 dst, u32 bytes)
+{
+ u32 i = svc_inline_once_next++ % SVC_INLINE_ONCE_SLOTS;
+
+ svc_inline_once[i].push_va = push_va;
+ svc_inline_once[i].hash = hash;
+ svc_inline_once[i].dst = dst;
+ svc_inline_once[i].bytes = bytes;
+}
+
static void svc_qmd_accum(struct nv_vgpu_device *vgpu, struct svc_channel *ch,
struct svc_push_state *st, u32 addr, u32 count,
const u8 *data, const u8 *push, u32 push_len,
@@ -8540,6 +8585,8 @@ static void svc_qmd_accum(struct nv_vgpu_device *vgpu, struct svc_channel *ch,
}
}
+static u64 svc_pass_hash; /* r391f: this decode pass's push content hash */
+
static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
u32 len, u64 page_pa, bool allow_ops,
struct svc_channel *ch, u32 entry_idx,
@@ -8548,6 +8595,9 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
{
struct svc_push_state st;
+ /* r391f: the inline-write once-only key needs the push's identity. */
+ svc_pass_hash = svc_push_hash(buf, len);
+
/*
* r316: pending releases record the channel they were decoded for;
* the verified pool resolver is channel-keyed (see svc_pending_add
@@ -8777,27 +8827,28 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
slot_bytes = 0x8000 - 4;
}
/*
- * r391f once-only (the sum=0 class): a REPLAYED
- * entry must not re-run its inline writes. The ops
- * are already gated by allow_ops (entry_replay
- * clears it), but the inline-write gate used
- * `trusted || allow_ops` - and a replay is trusted
- * by construction - so every re-decode of an
- * already-processed push re-wrote its inline data
- * into the vidmem shadow. victory_reduce's
+ * r391f once-only (the sum=0 class): the same
+ * inline write must not run twice for one push
+ * identity. The ops are gated by allow_ops
+ * (entry_replay clears it), but the inline-write
+ * gate `trusted || allow_ops` re-ran the write on
+ * every re-decode (a replay is trusted by
+ * construction): victory_reduce's
* cudaMemset(d_sum,0,8) is exactly such an inline
* write (observed: `inline write
* dst=0x7845ef000000 -> pa=0x7c00000 bytes=8
* data8=0` firing 86 us before the D2H readback,
* zeroing the accumulator the launch had just
- * written - the readback then reads zeros). The
- * r383 replay-capture path only needs to CAPTURE
- * the launch; the inline bytes were authored on the
- * first (non-replay) pass.
+ * written - the client then read zeros). A coarse
+ * !replay gate is wrong (a push whose first pass
+ * was untrusted must still author its bytes on the
+ * later trusted pass); key the write by
+ * (push_va, push content hash, dst, bytes) - a new
+ * push's release payload makes the hash unique -
+ * and skip only a genuine duplicate. The table is
+ * session-scoped (a new client starts clean).
*/
- if ((trusted || allow_ops) && !replay &&
- !gsp_sema_skip_ops &&
- st.have_out && st.line_length) {
+ {
u32 bytes = slot_run ? slot_bytes :
st.line_length *
(st.gr_line_count ?
@@ -8806,42 +8857,81 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
if (bytes > have)
bytes = have;
- svc_grce_inline_write(vgpu, st.offset_out,
- buf + off + 4, bytes,
- ch->h_object);
- /*
- * fail4: hardware semantics - a LOAD_INLINE_DATA
- * run advances the destination cursor by the
- * bytes it wrote. Without this, the consecutive
- * runs of a split 64 KB upload all land at the
- * first destination and the page ends up with
- * only the last run's words (the RAWA junk).
- */
- st.offset_out += bytes;
/*
- * fail4: name the header that follows each big
- * LID run - the split 64 KB upload's other
- * payload carriers sit exactly there.
+ * r391f once-only (the sum=0 class): the same
+ * inline write must not run twice for one push
+ * identity. The ops are gated by allow_ops
+ * (entry_replay clears it), but the
+ * inline-write gate `trusted || allow_ops`
+ * re-ran the write on every re-decode (a replay
+ * is trusted by construction): victory_reduce's
+ * cudaMemset(d_sum,0,8) is exactly such an
+ * inline write (observed: `inline write
+ * dst=0x7845ef000000 -> pa=0x7c00000 bytes=8
+ * data8=0` firing 86 us before the D2H
+ * readback, zeroing the accumulator the launch
+ * had just written - the client then read
+ * zeros). A coarse !replay gate is wrong (a push
+ * whose first pass was untrusted must still
+ * author its bytes on the later trusted pass);
+ * key the write by (push_va, push content hash,
+ * dst, bytes) - the release payload inside every
+ * push makes the hash unique per push - and skip
+ * only a genuine duplicate. Session-scoped: a new
+ * client starts clean.
*/
- if (gsp_sema_dbg && len >= 4096 &&
- svc.next_hdr_log < 24) {
- u32 noff = off + 4 + count * 4;
- u32 nh = 0, nw = 0;
-
- if (noff + 4 <= len)
- memcpy(&nh, buf + noff, 4);
- if (noff + 8 <= len)
- memcpy(&nw, buf + noff + 4, 4);
- svc.next_hdr_log++;
- pr_info("sema-next-hdr: h=0x%x push=0x%llx after=0x%x cnt=%u nxt_hdr=0x%08x nxt_w0=%08x cursor=0x%llx\n",
- ch->h_object, push_va, off,
- count, nh, nw, st.offset_out);
+ if ((trusted || allow_ops) && !gsp_sema_skip_ops &&
+ st.have_out && st.line_length && bytes &&
+ !svc_inline_once_seen(push_va, svc_pass_hash,
+ st.offset_out,
+ bytes)) {
+ svc_inline_once_store(push_va,
+ svc_pass_hash,
+ st.offset_out,
+ bytes);
+ svc_grce_inline_write(vgpu, st.offset_out,
+ buf + off + 4,
+ bytes,
+ ch->h_object);
+ /*
+ * fail4: hardware semantics - a
+ * LOAD_INLINE_DATA run advances the
+ * destination cursor by the bytes it
+ * wrote. Without this, the consecutive
+ * runs of a split 64 KB upload all land
+ * at the first destination and the page
+ * ends up with only the last run's
+ * words (the RAWA junk).
+ */
+ st.offset_out += bytes;
+ /*
+ * fail4: name the header that follows
+ * each big LID run - the split 64 KB
+ * upload's other payload carriers sit
+ * exactly there.
+ */
+ if (gsp_sema_dbg && len >= 4096 &&
+ svc.next_hdr_log < 24) {
+ u32 noff = off + 4 + count * 4;
+ u32 nh = 0, nw = 0;
+
+ if (noff + 4 <= len)
+ memcpy(&nh, buf + noff, 4);
+ if (noff + 8 <= len)
+ memcpy(&nw, buf + noff + 4,
+ 4);
+ svc.next_hdr_log++;
+ pr_info("sema-next-hdr: h=0x%x push=0x%llx after=0x%x cnt=%u nxt_hdr=0x%08x nxt_w0=%08x cursor=0x%llx\n",
+ ch->h_object, push_va,
+ off, count, nh, nw,
+ st.offset_out);
+ }
+ } else if (gsp_sema_dbg && svc.grce_log < 64) {
+ svc.grce_log++;
+ pr_info("sema-grce: inline data not run (trusted=%d allow=%d have_out=%d line=%u) h=0x%x\n",
+ trusted, allow_ops, st.have_out,
+ st.line_length, ch->h_object);
}
- } else if (gsp_sema_dbg && svc.grce_log < 64) {
- svc.grce_log++;
- pr_info("sema-grce: inline data not run (trusted=%d allow=%d have_out=%d line=%u) h=0x%x\n",
- trusted, allow_ops, st.have_out,
- st.line_length, ch->h_object);
}
/*
* Record the destination and the payload bytes for
@@ -10819,6 +10909,8 @@ static void svc_session_flush(void)
memset(svc_retry_q, 0, sizeof(svc_retry_q));
memset(svc_pool_pa, 0, sizeof(svc_pool_pa));
memset(svc_fwd_rel, 0, sizeof(svc_fwd_rel));
+ memset(svc_inline_once, 0, sizeof(svc_inline_once));
+ svc_inline_once_next = 0;
memset(svc_grce_dst_ring, 0, sizeof(svc_grce_dst_ring));
for (i = 0; i < SVC_VAS_SLOTS; i++) {
if (svc_vas[i].data)