summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_sema_service.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-22 06:18:15 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-22 06:18:15 -0400
commit5daaa0b540ff0a2a570b9b339f551e6b69edaab5 (patch)
tree42607cd93f981f2e13c0a1c99b6a95f1a6be1462 /kernel/gsp_sema_service.c
parent773e9294e53c82961420de03f0d170f54565baef (diff)
downloadopen-iov-5daaa0b540ff0a2a570b9b339f551e6b69edaab5.tar.gz
open-iov-5daaa0b540ff0a2a570b9b339f551e6b69edaab5.zip
r457 free the copy staging snapshot only on consumption/slot-reuse/skip/TTL (not on opdef_clear)
The r456 change kept the snapshot alive across opdef APPLY, but the r457 mode-C run proved a second freer: svc_opdef_clear's FINISH path (entry consume via svc_replay_note) freed the b1 H2D's snapshot while the copy was still owed. The pin-batch rewind then REPLAYED the entry, the re-capture read the driver's REFILLED staging and the deferred execution wrote b2's bytes into db1. Evidence (r457, mode C, owner 0x5c00005c/1/0627e693d05aab3f): 191607.816388 copy snapshot captured dst=0x72e318c20000 src=0x72e31c400000 first4=00000000 3e800000 3f000000 3f400000 (b1) 191608.798838 copy snapshot captured <same key> first4=00000000 3d800000 3e000000 3e400000 (b2 - refilled staging) 191611.404931 sema-bigcopy dst_va=0x72e318c20000 src_va=0x72e31c400000 dst16_after=000000000000803d0000003e0000403e (b2 landed in db1) The op never succeeded (rc=-2 every pass), so the only free that could have dropped the first snapshot is the opdef_clear owner free. New lifecycle: capture once -> free on the copy's own successful consumption -> free on ring-slot reuse with a new push hash -> free when a re-decode skips an already-done op -> 30 s TTL. svc_copy_snap_free_owner keeps its rationale but is no longer called (the r444 cross-entry poisoning stays closed by the owner key + eviction).
Diffstat (limited to 'kernel/gsp_sema_service.c')
-rw-r--r--kernel/gsp_sema_service.c62
1 files changed, 33 insertions, 29 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index bcd8ac3..ba9d3da 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -5997,14 +5997,18 @@ static void svc_copy_snap_free(u64 dst, u64 src, size_t total,
}
/*
- * r444: drop every snapshot still owned by one entry. A snapshot that was
- * not consumed by the entry's own copy execution (the copy ran via the
- * forwarded hardware path, or a post-execution re-decode re-captured the
- * unchanged staging) has no future consumer once the entry's release is
- * delivered - leaving it behind let a LATER entry whose copy shares the
- * (dst,src,total) triple execute from the stale bytes.
- */
-static void svc_copy_snap_free_owner(u32 owner_ch, u32 owner_idx)
+ * r456/r457: the snapshot lifecycle is capture-once -> free on the copy's
+ * own successful consumption (svc_copy) -> free on ring-slot reuse with a
+ * new push hash (svc_copy_snap_capture) -> free when a re-decode skips an
+ * already-done op (svc_run_ops) -> TTL. The old per-entry owner free was
+ * removed from every opdef_clear caller: an entry whose release was
+ * delivered can still be REPLAYED (the pin-batch rewind) with its copy
+ * still owed, and freeing at that point let the replay re-capture the
+ * driver's refilled ping-pong staging (the r457 mode-C b1 H2D wrote b2's
+ * bytes into db1). A stale snapshot can only ever serve its own
+ * (ch, idx, hash) owner, so the r444 cross-entry poisoning stays closed.
+ */
+static void __maybe_unused svc_copy_snap_free_owner(u32 owner_ch, u32 owner_idx)
{
u32 i;
@@ -15907,28 +15911,28 @@ static void svc_opdef_clear(struct svc_opdef_ent *e, u32 caller)
e->n ? e->rel[0].payload : 0u);
}
/*
- * r444: the entry is being consumed; no retry can need its
- * staging snapshot any more (see svc_copy_snap_free_owner).
- *
- * r456: EXCEPT on the APPLY path. The apply runs at the head of a
- * re-decode pass of an entry whose copy is still held for its
- * operand pins; the pass's own op execution (which consumes the
- * snapshot on success) follows the apply. Freeing here destroyed
- * the pipeline-order snapshot before it could ever be read and
- * the next pass's guard re-capture then read the driver's
- * REFILLED ping-pong staging - the r455 operand-shift: the a1 H2D
- * executed ~3 s after its push with a2's bytes (owner
- * 0x5c00005c/0/6076682fa76b8792, snapshot first4 flipped from
- * 00000000 3f000000... to 00000000 3e000000... across retries;
- * dst16_after=000000000000003e0000803e0000c03e proves the a2
- * pattern landed in da1's page). The snapshot now survives the
- * retries and is consumed (and freed) by the copy that finally
- * executes; svc_copy_snap_free_owner still runs on every genuine
- * consume path (FINISH/FLUSH/DROP/STALE/COUNTED/SWEEP/STOP) and
- * the TTL covers the rest.
+ * r456/r457: the snapshot lifecycle is now "capture once, consume
+ * on the copy's success, evict on ring-slot reuse, TTL". Every
+ * opdef_clear caller can still re-decode the entry:
+ * - APPLY (0): the pass's own op execution (which consumes the
+ * snapshot on success) follows the apply;
+ * - FINISH (1): the entry can be REPLAYED after a consume (the
+ * pin-batch rewind replays entries while mappings stream in),
+ * and an owed copy still needs the pipeline-order bytes;
+ * - FLUSH (2): the op stays skipped this pass but a later replay
+ * may run it.
+ * The r457 mode-C evidence: the b1 H2D's snapshot was captured at
+ * 191607.816388 with b1's bytes (first4=00000000 3e800000 ...),
+ * FINISH freed it on the entry's consume, the rewind replay
+ * re-captured at 191608.798838 from the REFILLED staging
+ * (first4=00000000 3d800000 ... = b2) and the deferred execution
+ * wrote b2's bytes into db1 (dst16_after=000000000000803d...).
+ * The r444 leak the old free guarded against is already covered
+ * by the owner key (a stale snapshot can only ever serve its own
+ * (ch, idx, hash)), the slot-reuse eviction in
+ * svc_copy_snap_capture, the ops_done skip free in svc_run_ops
+ * and the 30 s TTL.
*/
- if (e->active && caller != SVC_OPDEF_CLR_APPLY)
- svc_copy_snap_free_owner(e->h_object, e->entry_idx);
memset(e, 0, sizeof(*e));
}