1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
#!/usr/bin/env python3
"""s02 - counter publication: which of N slots advanced, and when?
Real scenario (Handoff-vGPU-IO.md PART 46-64, 2026-09-24..2026-09-29):
``cuCtxCreate`` waits on 20 GR per-channel completion counters. The host saw
the client's releases but could not translate the counter page; for many boots
only 4 of 20 client-visible slots ever moved (PART 49). The exact slot layout
(page VA ``0x20400f000``, slots at ``+0xec0``, 16-byte stride) was only proven
by a raw live page dump (PART 64, 2026-09-29 ~16:55), and the host raced a
~2-3 s pin path against the client's ~2 s GR-phase deadline. Static reading
could not tell which slots were published: the page's GPA changed every boot
and stale translations pointed at the previous client's page (PART 51).
Analog: post 20 sequential commands and watch the demo's published result
field; prove every slot was observed, with the publish PC recorded per slot.
"""
from __future__ import annotations
import os
try: # package import (tests) or direct script execution (run_all)
from . import _common as C
except ImportError:
import sys as _sys
_sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import _common as C
SLOTS = 20
def run() -> dict:
import greeneye as ge
backend = C.choose_backend("native")
out = C.artifact_dir("s02_counter_publication")
commands = [
'ge.launch(kernel=DEMO_ELF, backend="%s")' % backend,
"s.watch(gsp_ctrl+0x40, size=8, kind='w', callback=record pc) # publication",
"for slot in 1..20: write mailbox cmd=5 (STATE); run(duration=0.05)",
"read gsp_ctrl.result after every slot; compare against the expected stage value",
]
scenario = C.scenario(
ids=[
"Handoff-vGPU-IO.md PART 49/50 (2026-09-29): only 4/20 counters moved; all 20 reached 1 after the queue fix",
"PART 64 (2026-09-29 ~16:55): raw page dump proves 20 slots at 16-byte stride",
"PART 46/48 (2026-09-29): rescue/sweep latency and tracker waits",
],
baseline=(
"PARTs 46->64: ~5 days of sessions; PART 49 'only 4 of 20 counters "
"ever moved'; each boot re-derived the slot layout because the "
"counter page GPA changed (PART 51/53)"
),
projection=(
"on real silicon the 20 slots are per-channel memory pages; the "
"analog publishes 20 logical slots through the same watchpoint "
"shape, one store each"
),
blocked=True,
device="H2.2 device model + real GSP-RM counter pages",
)
C.render_banner(
"s02_counter_publication",
"Of the N client-visible slots, which ones advanced, and where was each published?",
"20 GR per-channel completion counters: 4/20 published for days until a live page dump",
scenario["static_baseline"],
commands,
)
session = ge.launch(kernel=C.DEMO_ELF, backend=backend, name="vd-s02")
slots = []
try:
ctrl = session.symbols.lookup("gsp_ctrl")
publishes = []
session.watch(ctrl + C.CTRL_RESULT, size=8, kind="w", callback=lambda hit: publishes.append(hit.pc))
session.run(duration=0.25)
before = session.memory.read_u64(ctrl + C.CTRL_RPC_COUNT)
for slot in range(SLOTS):
session.memory.write_u64(ctrl + C.CTRL_MAILBOX, 5) # STATE
session.run(duration=0.05)
value = session.memory.read_u64(ctrl + C.CTRL_RESULT)
slots.append(
{
"slot": slot + 1,
"result": value,
"result_hex": f"{value:#x}",
"published": value in (0x10, 0x20, 0x30, 0x40),
}
)
after = session.memory.read_u64(ctrl + C.CTRL_RPC_COUNT)
published = [s for s in slots if s["published"]]
payload = {
"success": len(published) == SLOTS and (after - before) == SLOTS,
"backend": backend,
"slots_requested": SLOTS,
"slots_published": len(published),
"rpc_count_delta": after - before,
"watch_stores": len(publishes),
"distinct_values": sorted({s["result_hex"] for s in published}),
"per_slot": slots,
"publish_pcs": sorted(set(publishes)),
"question": f"did all {SLOTS} slots publish, and can each publication be attributed?",
"answer": (
f"{len(published)}/{SLOTS} slots published ({after - before} dispatcher "
f"completions, {len(publishes)} stores to gsp_ctrl.result, values "
f"{sorted({s['result_hex'] for s in published})}); every store PC is captured, "
"exactly the evidence class that took a live 512-byte page dump on the real path"
),
"success_criteria": {
"pass": f"{SLOTS}/{SLOTS} slots observed with rpc_count advancing once per slot",
"fail": "any slot does not publish or the count delta disagrees",
},
"real_case": scenario,
}
C.write_json(os.path.join(out, "counters.json"), payload)
finally:
session.close()
return payload
def main() -> int:
return 0 if C.timed_demo(run, C.artifact_dir("s02_counter_publication"))["success"] else 1
if __name__ == "__main__":
raise SystemExit(main())
|