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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
|
# DUAL-REPORT.md — Stage B+C (minimal) per-mdev separation for dual-guest CUDA
**Date:** 2026-10-15
**Worktree:** `/home/rmcore/Git/integdev_gpu_drv/open-iov-dual` (branch `dual`, base `ecfb1278`)
**Commits:** `81c11ca0` (stage B+C separation), `5769d5c4` (CUDA-bridge owner gating)
**Build:** `cd kernel && make -j8` — clean success, `vgsp.ko` linked. No module
was loaded, no device touched, no guest run (offline implementation only).
Implementation of stages B+C (minimal) of
`analysis-2026-10-15/AGENT-dual-guest-inventory.md` §3.1/§3.2 (items G1–G8),
against the user scope: G1, G2, G3, G5-minimal, G6, G7, G8(a).
Explicitly **not** implemented: G4 remainder (per-guest learned tables),
G10 per-mdev debugfs views, parallel forwarding, boot_stage split.
---
## 1. Per-item status table
| # | Item | Status | Change site (file:line @ `5769d5c4`) | Verified by |
|---|------|--------|--------------------------------------|-------------|
| G1 | Sema-service thread services ALL open mdevs each pass | DONE | `kernel/gsp_sema_service.c:16034` (`svc_thread`): collects every open mdev into `vgpus[]` (:16060–:16075) and runs the pass body per guest; `svc_handle_map_add()` drains once per pass; the scans/hint loop is per guest (:16174–:16226). Per-guest rebuild freshness (`sess->last_build`, :16105–:16119). | build only (`make -j8`) |
| G2 | Channel table keyed by `(vgpu, h_object)`; per-guest rank; `vgpu` field in `svc_channel` | DONE | struct field `kernel/gsp_sema_service.c:165`; match in `channel_add` :1154 + `same` :1168; rank loops :1204–:1221; `channel_del` :1328; pump snapshot :13653; pump writeback :15201; counter paths :5198/:5271/:5360/:5551/:5600/:5626/:5686; `svc_pb_discover` :7821; `svc_gr_content_discover` :9103; `svc_gr_ring_autopin` :9445; chan dump :15516. Registration carries the owner: `kernel/gsp_rm_emulation.c:2360`; free passes it: :3248; desc field `kernel/gsp_sema_service.h:30`. | build only |
| G3 | Per-vgpu session active/flush flags; departing guest never flushes a live guest | DONE (staged semantics — see §3.1) | session records + helpers `kernel/gsp_sema_service.c:468–:520` (struct :473, `svc_guest_get` :489); `channel_add` marks active :1242; `channel_del` deactivates per guest and requests the flush only when **no** guest has a live channel :1356–:1378; `gsp_sema_service_vgpu_cleanup` :16364 (session end on mdev remove). | build only |
| G4 (remainder) | Per-guest learned tables (map/pin/vas/pending/…) | NOT IN SCOPE (documented risk §5.1) | — | — |
| G5 (minimal) | Forward request/hold carry the owning vgpu; single global in-flight kept | DONE | req already carried `vgpu` (verified, set at :12972 GR `svc_gr_req_fill`, :13304 CE); hold abort keyed `(vgpu, handle)` :11091; dead-owner guards on both resolution paths :11928 (`svc_vgpu_open`), :11956 (`gone_finish`), :12019 (`stop_flush`); operand translation through the request's vgpu :10922. Follow-on: CUDA-bridge fallback queue now carries its owner (`kernel/gsp_cuda_bridge.c:143`, :624, :985, :1353). | build only |
| G6 | Per-mdev guest-RAM backing {file, path, ranges} + per-mdev selector | DONE | records `kernel/gsp_phys_gpu.c:1264–:1373`; `gsp_phys_guest_ram_configure_vgpu` :1494; `gsp_phys_guest_ram_vgpu_clear` :1586; clear-all :1564; active :1604; state (:1646, legacy format byte-identical); debugfs writer `kernel/gsp_phys_booter.c:20487` (4-token legacy or 5-token `<uuid>` selector); selector resolve :20542 via `gsp_phys_open_mdev_by_name` (`gsp_phys_gpu.c:1356`). | build only |
| G7 | `gpa_to_hpa` takes the requesting vgpu; all call sites plumbed | DONE | `gsp_phys_gpa_to_hpa_vgpu` `kernel/gsp_phys_gpu.c:1737`; wrapper `gsp_phys_gpa_to_hpa` :1827. Call sites: `gsp_sema_service.c:10922` (forwarding operands — passes the request's vgpu); `gsp_phys_booter.c:9738`, :19125, :19212 (single-guest guest-GPA pin — explicit `NULL` = first open mdev, see §3.2); no others remain (`grep`). | build only |
| G8(a) | Guest-GPA pin explicitly single-guest; refusal + log | DONE | `kernel/gsp_phys_booter.c:20430–:20436`: pin refused `-EBUSY` with a log when `gsp_phys_open_mdev_count() > 1` (`gsp_phys_gpu.c:1333`); existing different-GPA refusal kept. Boot-ladder trigger call sites documented as first-open. | build only |
| G10 | Per-mdev debugfs views | NOT IN SCOPE | — | — |
Diff stats (`git diff --stat ecfb1278..HEAD`):
```
kernel/gsp_cuda_bridge.c | 92 ++++++-
kernel/gsp_phys_booter.c | 115 +++++++--
kernel/gsp_phys_gpu.c | 432 ++++++++++++++++++++++++++-----
kernel/gsp_phys_gpu.h | 35 ++-
kernel/gsp_rm_emulation.c | 3 +-
kernel/gsp_sema_service.c | 644 +++++++++++++++++++++++++++++++++-----------
kernel/gsp_sema_service.h | 20 +-
kernel/main.c | 10 +
8 files changed, 1098 insertions(+), 253 deletions(-)
```
### 1.1 One-mdev equivalence argument (explicit fast paths)
With exactly ONE open mdev every changed path degenerates to the old one:
1. **Thread (G1):** `vgpus[]` has one entry; the per-guest loop body is the
historical pass in the historical order (probe → walk_probe → chan_dump →
scrub → rebuild → pump → bridge → pending → sweep, then the scan loop).
`svc_handle_map_add()` runs once per pass (`gsp_sema_service.c:16085`),
exactly as before.
2. **Channels (G2):** `vgpu` is constant, so `(vgpu, h_object)` matches the
same slots as `h_object` alone; rank loops count the same set; the pump
snapshot/writeback select the same channels.
3. **Session/flush (G3):** the single guest's last-channel close empties the
global table → `svc_sess_active = 0; svc_sess_flush = 1` at the same
transition as before.
4. **Guest RAM (G6/G7):** a 4-token `gsp-phys-guest-ram` write configures the
legacy record with byte-identical log lines; `gpa_to_hpa_vgpu(vgpu…)`
resolves the same first-open mdev and the same legacy record; the
`gsp-phys-state` `guest_ram:` field renders the byte-identical
`active=1 path=… ranges=… gpa=…` text (and `active=0` when unconfigured).
5. **Forwarding (G5):** `svc_fwd_req`/`svc_fwd_hold` set and read the same
vgpu the single pump always used; the dead-owner guards never trigger while
the guest is open; the CUDA bridge's `head_owner == vgpu` is always true.
6. **Log lines relied on by the campaign** are unchanged: `sema-svc:`,
`vgsp-gr:`, `GR LAUNCH submitted`, `GUEST GR LAUNCH OK`, `REAL GR SEM OK`,
`backend start: joined the live module-owned claim (READY)`, `guest RAM
configured:` / `guest RAM released:`. Debugfs file names are unchanged
(the selector is an extra token on the same file). Parameter defaults are
untouched.
---
## 2. What changed, by item
### G6 — per-mdev guest-RAM backing (`gsp_phys_gpu.c`, `gsp_phys_booter.c`)
* `guest_ram_file`/`guest_ram_path`/`guest_ram_ranges` globals replaced by
`guest_ram_legacy` (slot for the historical top-level write) + up to 4
`guest_ram_mdevs[]` records (vgpu-pointer keyed), all under the existing
`guest_ram_lock`.
* New API: `gsp_phys_guest_ram_configure_vgpu()`,
`gsp_phys_guest_ram_vgpu_clear()`, `gsp_phys_open_mdev_count()`,
`gsp_phys_open_mdev_by_name()` (exports + prototypes in `gsp_phys_gpu.h`).
* debugfs `gsp-phys-guest-ram` accepts both
`"<path> <gpa_base> <off> <size>"` (legacy: first/only mdev) and
`"<mdev-uuid> <path> <gpa_base> <off> <size>"` (per-mdev). Unknown selector
→ `-ENODEV` with a log; `"-"` clears every record. Token count (4 vs 5)
discriminates; paths/UUIDs contain no spaces.
* mdev removal releases the record (`main.c:1750`
`gsp_phys_guest_ram_vgpu_clear(vgpu)`); physical release clears all records
(`gsp_phys_guest_ram_clear()`).
### G7 — vgpu-aware GPA→HPA (`gsp_phys_gpu.c` + call sites)
* `gsp_phys_gpa_to_hpa_vgpu(vgpu, gpa, *hpa)`:
* `vgpu == NULL` → first open mdev (historical);
* named vgpu whose session is closed → `-ENODEV` (never retargeted);
* translation uses the vgpu's own record, else the legacy record when the
vgpu is the first open mdev.
* `gsp_phys_gpa_to_hpa()` kept as a thin `NULL` wrapper.
* Forwarding operands (`svc_fwd_operand_hpa`) pass the request's vgpu; the
boot-ladder guest-GPA pin paths pass `NULL` on purpose (§3.2).
### G1 — sema-service thread (`gsp_sema_service.c`)
* Per pass: bounded list walk collects all open mdevs (≤ `SVC_MAX_GUESTS`=8,
the driver's per-type ceiling) and runs the full per-guest body for each.
* Per-guest `svc_rebuild` freshness so the first guest's 10 s tick cannot
starve the second guest of rebuilds.
* `pumped` is an aggregate: while any guest is progressing, the pass loops
tightly and skips the heavy scans, as before.
### G2 — channel identity (`gsp_sema_service.c`, `gsp_rm_emulation.c`)
* `svc_channel.vgpu`; add/del match `(vgpu, h_object)`; `same` (re-register)
requires the same owner; ranks count only same-owner channels; counter,
scrubber-adjacent, opdef-drop, push-buffer discovery, GR content discovery,
GR ring autopin max-CE scan, pump snapshot and pump write-back are all
filtered by the owning vgpu.
* Scrubber channel state (`svc_scrub` → `svc_scrubs[]`) made per guest: the
authored finish/USERD words are written through the owning guest's address
space, never through whichever guest opened first.
### G3 — sessions (`gsp_sema_service.c`)
* `svc_guests[]` records: `active` + `last_build`, pointer-keyed, freed on
mdev cleanup. `channel_add` marks active; `channel_del` deactivates the
owner when its last channel closes and requests the global flush only when
**no guest** has a live channel (staged semantics, §3.1).
* New `gsp_sema_service_vgpu_cleanup(vgpu)` (called from
`main.c:1749`): quiesces forwarding, drops the guest's hold (pure clear —
never a resolution through a vanishing device), removes its channels,
drops its opdef release slots, frees its session and scrubber records.
### G5 (minimal) — forwarding identity (`gsp_sema_service.c`, `gsp_cuda_bridge.c`)
* The single-inflight request keeps ONE global slot (serialization kept) but
carries its owner: CE requests set `svc_fwd_req.vgpu` (:13304), GR requests
set it in `svc_gr_req_fill` (:12972), and the operand resolver translates
through it.
* Hold abort keyed by `(vgpu, handle)`; both hold resolution paths
(`svc_fwd_hold_gone_finish`, `svc_fwd_hold_stop_flush`) and the pump's
cross-vgpu resolution check `svc_vgpu_open()` before ever dereferencing the
stored device and drop the hold when its owner is gone.
* CUDA bridge (the GR fallback under the single-inflight slot): request and
pending records now carry the owning mdev, the owner is part of the dedup
identity, only the owner's pass marshals/finishes, and stale-owner records
are dropped. This was not itemised in the inventory, but without it the
expected dual steady state (guest B's forward refused while A's hold is
active) would marshal guest B's fallback job through guest A.
### G8(a) — guest-GPA pin
* Refused while more than one mdev is open, with an explicit log; the pin
remains a single-guest test aid translating through the first open mdev.
---
## 3. Deviations from the inventory plan (deliberate, minimal)
### 3.1 G3 flush is deferred, not per-guest-selective
The inventory asks to "flush only the departing vgpu's learned state". The
learned tables (`svc.map`, `svc.pin`, `svc.ipin`, `svc.lat`, `svc.vas*`,
`svc_pool_pa`, `svc_pending`, `svc_retry_q`, `svc_pb[]`, rank bitmaps) are
still shared — that is the explicitly out-of-scope G4 remainder. With shared
tables a selective flush is impossible; the only safe minimal semantic is:
**deactivate the departing guest's session and defer the flush while any other
guest still has live channels.** One guest: identical to today (last-channel
close is the global empty transition). Consequence for T5: guest B's
close→reopen while A is live does not wipe B's stale learned entries (they
must survive for A), so B's *second* session may hit the ISSUE-0002
contamination class for its own VAs. Closing that hole is the G4 minimum
(per-entry owner tags + per-guest lookup filter).
### 3.2 Boot-ladder guest-GPA call sites pass `NULL`
`gsp_phys_rmcall_pdb_build` (booter :9738) and triggers 6/7 (:19125, :19212)
have no request context; the only user of that GPA is the single-guest
test-only pin, which is now refused while >1 mdev is open (G8a). `NULL` is
the documented first-open resolution — identical behaviour for every
single-guest run, and explicitly not per-mdev.
### 3.3 Debugfs selector: extra token, not a subdir/per-uuid file
The hard requirement says "same debugfs file names". The recommended subdir
per UUID / `gsp-phys-guest-ram-<uuid>` file would add files; the optional
first-token UUID keeps the tree and the historical 4-token write untouched
while giving per-mdev targeting. The per-mdev form is additive: 5 tokens.
### 3.4 Scrubber registration binds to the first open mdev
`sema_scrubbuf_add` (debug tooling written by the guest scanner) has no
selector; it binds to the first open mdev's scrubber record (single-guest
convention, like the scanner itself) and is refused with no open mdev.
### 3.5 CUDA bridge owner gating (follow-on commit)
See §2/G5. Default-on (`cuda_bridge=1`); the change is inert for one guest.
---
## 4. Self-check: module-global session/pump state audit
Every read of module-global session/pump state after this change, classified:
| # | Global state | Sites | Status |
|---|--------------|-------|--------|
| 1 | channel table `svc.chan[]` | add :1154, del :1338, ranks :1204–:1221, counters :5198/:5271/:5360/:5551/:5600/:5626/:5686, pb says :7821, gr-content :9103, gr autopin :9445, pump :13653/:15201, chan dump :15516 | **fixed** per `(vgpu, h)` |
| 2 | session flags `svc_sess_active/flush` | :468–:520, :1356–:1378, :16364 | **fixed** (per-guest records drive the global flush) |
| 3 | scrubber `svc_scrub` | :535–:580, :1272–:1295, :15973, :15912 | **fixed** per guest |
| 4 | hold `svc_fwd_hold` | :11010–:12100 (:11956/:12019 guards) | **fixed**: vgpu-keyed abort + dead-owner guards |
| 5 | request `svc_fwd_req` | :10648, :12972/:13304, :13140–:13170 | **fixed**: owner carried; single-inflight kept |
| 6 | bridge req/pend | `gsp_cuda_bridge.c` (:143, :624, :985, :1353) | **fixed**: owner carried, owner-only marshal/finish |
| 7 | opdef slots | :11185–:11500 (drop_gone :11468) | pre-existing per `(vgpu,h)`; `drop_gone` filter **fixed** |
| 8 | learned map `svc.map` / `svc.map_n` / `svc.map_hash` | lookup :3300+, rebuild :3366 | **shared (G4)** — risk §5.1 |
| 9 | pins `svc.pin[]/ipin[]/svc_ipin_alt` | :2023–:2235, flush :7176 | **shared (G4)** — risk §5.1 |
| 10 | `svc.lat[]`, `svc.vas[]`, VA shadow, `svc_hot` | :1818+, :1878+, :2011 | **shared (G4)** — risk §5.1 |
| 11 | `svc_pending[]`, `svc_retry_q[]`, `svc_pool_pa*`, `svc_cnt_der` | :7081+, :885+, :2586+, :5335 | **shared (G4)** — risk §5.1 (pending is the top cross-guest write vector) |
| 12 | rank latches `svc_ring_done`, `svc_pb_done`, `svc_gr_ring_done`, `svc_pb[]`, gr-content state | :355, :341–:347, :501–:511 | **shared latches (G4)** — re-arm is content-validated/idempotent; extra re-scans possible |
| 13 | `svc_userd_sweep_at/n` (ce_index) | :13782+ | **shared (G4)** — equal ranks across guests share sweep slots |
| 14 | per-pass capture `svc_fwd_cand`, `svc_gr_cand`, `svc_fwd_rel` | :3896+, :2587+, :7233 | serially reused by the single service thread; safe with one in-flight, listed for completeness |
| 15 | debug surfaces reading the first open mdev | `add_map` rewind :15693, `dump_vidmem` :15783, `find_vidmem` :15819, `counter_add` :15868, scrubbuf :15912 | **debug tooling, single-guest by design** — do not use in dual runs |
| 16 | `gsp_phys_gpa_to_hpa(NULL,…)` (boot-ladder pin) | booter :9738/:19125/:19212 | **by design** first-open (§3.2, G8a refuses dual) |
| 17 | last-build freshness `svc.last_build` | :3570, :16105–:16114 | **fixed**: per-guest gate; `svc.last_build` kept as the published stamp |
| 18 | bridge debugfs FIFO views (job/in/out) | `gsp_cuda_bridge.c` | one global daemon FIFO; single-guest tooling surface |
No call site that carries a requesting/owning vgpu still matches session state
by handle alone in the paths in scope.
---
## 5. Risks (ranked)
1. **Shared learned tables (G4 remainder) — highest.** Cross-guest resolution
is still possible: a lookup for guest B's VA can hit guest A's pin/map
entry with the same VA; `svc_pending[]`/`svc_retry_q[]` entries carry no
owner, so a queued release decoded for B can be applied through A (or
vice-versa). Expected symptoms under T2/T4: wrong results, stalls,
`rel-apply` writes to the wrong guest. This is the next planned change
(G4 minimum: owner tag on pin/map/lat/pending + per-guest lookup filter).
2. **mdev-removal lifetime race.** The pump can be mid-pass for a vgpu whose
fd just closed; the vgpu struct is freed after `vfio_put_device()` in the
remove path. Pre-existing class (single-guest too), reduced by
`gsp_sema_service_vgpu_cleanup` (channels/hold/records cleared before the
free), not fully closed (the pump pass itself is not synchronised).
3. **G3 staged semantics (T5).** Guest B reopen while A is live inherits B's
previous learned entries (see §3.1). A's next run is protected (no flush
while A is live) — that is the T5 guard — but B's own reopen may hit the
ISSUE-0002 wedge until G4-minimum.
4. **Shared rank latches/pairing (`svc_pb[]`, done bitmaps, sweep arrays).**
Content validation makes re-arms idempotent, but equal ranks across guests
can pair the wrong push-buffer base records and share USERD sweep slots;
possible slowdowns/mis-pairs under concurrent CE load.
5. **Legacy guest-RAM write semantics.** With two guests, the 4-token write
configures the legacy record served by the first open mdev; a script that
writes it expecting "the new guest" will bind guest A. Use the 5-token
UUID form for the second guest.
6. **Reducer scope:** no boot_stage split, no per-mdev debugfs views, no
parallel forwarding — per plan. The single physical GR channel stays safe
under `gsp_phys_rm_lock` (inventory §4), serialized at launch granularity.
---
## 6. Live-validation plan (offline work ended here)
All of the following is for the LEAD under the supervised main-job rules
(no unattended cycles; stock guests; evidence per run). The module was NOT
loaded by this task.
### 6.0 Build/load (from the dual worktree)
```bash
cd ~/Git/integdev_gpu_drv/open-iov-dual/kernel && make -j8
# note: modpost needs
# ~/Git/integdev_gpu_drv/open-iov-dual/nvidia-open-gpu-kernel-modules/kernel-open/Module.symvers
# (gitignored; copied into this worktree from the master tree — keep it present
# or the final link fails with "No such file or directory")
```
Load (after a clean/unloaded host; parameters per inventory §5):
```bash
sudo insmod ~/Git/integdev_gpu_drv/open-iov-dual/kernel/vgsp.ko \
gsp_phys_gpu=1 gsp_phys_mdev_owned=1 gsp_phys_guest_mem=1 \
gsp_phys_work_forward=1 gsp_phys_gr_forward=1
```
### 6.1 Setup — two turing mdevs, two QEMUs
```bash
GPU=0000:01:00.0 # substitute the campaign's GPU BDF (10de:…)
T=nvidia-nvidia-vgpu-turing
UUID_A=$(uuidgen); UUID_B=$(uuidgen)
echo "$UUID_A" | sudo tee /sys/bus/pci/devices/$GPU/mdev_supported_types/$T/create
echo "$UUID_B" | sudo tee /sys/bus/pci/devices/$GPU/mdev_supported_types/$T/create
# available_instances must read 2 - 2 = 0 afterwards
```
Launch QEMU A and B, each bound to its own mdev
(`-device vfio-pci,sysfsdev=/sys/bus/mdev/devices/$UUID_A` resp. `$UUID_B`),
each with `memory-backend-file,share=yes` pointing at a **different** file
(e.g. `/dev/shm/vgpu-ram-a`, `/dev/shm/vgpu-ram-b`).
Register each guest's RAM window per mdev (5-token selector form):
```bash
echo "$UUID_A /dev/shm/vgpu-ram-a 0x100000000 0x0 0x40000000" | sudo tee /sys/kernel/debug/vgsp/gsp-phys-guest-ram
echo "$UUID_B /dev/shm/vgpu-ram-b 0x100000000 0x0 0x40000000" | sudo tee /sys/kernel/debug/vgsp/gsp-phys-guest-ram
# guest A may equally be configured by the legacy campaign scripts (4-token form);
# it serves the first open mdev. A second write of a different path to the same
# record is refused -EBUSY until that mdev is removed.
```
In each guest: stock 570.144 driver + `tools/cuda-ab/run/vecadd_loop` (or the
guest-side build from `tools/cuda-ab/`).
### 6.2 Test matrix (inventory §5, T1–T5)
| Test | Action | Pass criteria |
|------|--------|---------------|
| T1 | `nvidia-smi -L` in both guests simultaneously | no host error/stall (`dmesg -T | grep -i vgsp`) |
| T2 | start `./vecadd_loop` in A and B within 1 s | each prints `LOOP launches=100 bad=0`, exit 0 within 600 s |
| T3 | A vecadd, B started 10 s later | both green |
| T4 | `smiley` in A + `vecadd_loop` in B (CE vs GR cross) | both green |
| T5 | stop guest B → its mdev removed → recreate B (+ its RAM record) → rerun A and B | A green across B's cycle (B itself: see risk §5.3) |
Evidence capture: per-guest client logs + host dmesg to
`backups/evidence-*/dual-guest-<run>/`; require ≥200 lines each of
`GUEST GR LAUNCH OK` and zero `fwd_gr_fault`/timeouts in the steady state.
Per-mdev attribution in the log: `guest RAM configured for mdev <uuid>`,
`channel 0x… registered` around each guest's `GR LAUNCH submitted` stream.
Useful live checks:
```bash
dmesg -T | grep -E "sema-svc|vgsp-gr|GUEST GR LAUNCH OK|cuda-bridge" | tail -200
cat /sys/kernel/debug/vgsp/gsp-phys-state | grep -E "guest_ram|work_guest|backend"
sudo cat /sys/kernel/debug/vgsp/gsp-phys-state | grep guest_ram # both mdev records visible
```
Baseline (pre-fix) for the regression control: T2 with the old module should
show the second guest stalling or wrong results (inventory §5).
### 6.3 Rollback
Worktree-local: `git checkout ecfb1278 -- kernel/ && make -j8` in
`open-iov-dual`; the master worktree is untouched.
---
## 7. Artifacts
* Branch `dual` commits `81c11ca0`, `5769d5c4` (local only, not pushed).
* `kernel/vgsp.ko` built at `5769d5c4` (offline; never loaded).
* This report. No sessions/dumps generated (no device access).
|