diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-28 07:34:34 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-28 07:34:34 -0400 |
| commit | 17e1f522e4e9e04d13c4ca9495a2f4f1bdc58c36 (patch) | |
| tree | 350dc2707e5ddc471ab93686cd32a4d7ea92f40e /tools | |
| parent | d8f1069384dbfb1d8e453e71614a9baaf3438f5c (diff) | |
| download | open-iov-17e1f522e4e9e04d13c4ca9495a2f4f1bdc58c36.tar.gz open-iov-17e1f522e4e9e04d13c4ca9495a2f4f1bdc58c36.zip | |
sema-svc: apply releases promptly and decode compute-push releases
The content sweep now:
- decodes a release whose method stream continues with any INC_METHOD,
not only the CE LAUNCH_DMA group, so the releases at the head of a
compute/GR channel's first push are applied too;
- remembers releases whose target VA had no translation (deferred list)
and retries them every service pass, so a VA->PA injection lands them
immediately instead of after the next full sweep;
- re-arms the fast sliced sweep for a few seconds after an injection;
- treats a channel whose USERD/ring cannot be resolved as stalled, so the
sweep keeps looking for the releases its unreadable pushes carry.
tools: add per-cdt client-pin helper (sem pages + /dev/nvidia0 windows),
sem-page scanner and memprobe fixes (short-read guard, --brief).
Live-verified: injecting a client sem page applied 12 deferred releases
within a second; the service kept applying (89 -> 202) while cdt ran.
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/guest-diag/memprobe.py | 11 | ||||
| -rwxr-xr-x | tools/guest-diag/pin_client_sems.sh | 66 | ||||
| -rwxr-xr-x | tools/guest-diag/sempages.py | 37 |
3 files changed, 111 insertions, 3 deletions
diff --git a/tools/guest-diag/memprobe.py b/tools/guest-diag/memprobe.py index 26ed0af..96dd40b 100644 --- a/tools/guest-diag/memprobe.py +++ b/tools/guest-diag/memprobe.py @@ -126,8 +126,10 @@ def dump_tracker(pid, fd, p): if sob: print(" sem_obj[0:0x40]=%s" % sob.hex()) mem = r64(sob, 0x10) - mvb = rd(fd, mem & ~0xfff, 0x40) - print(" sem_mem=0x%x val@+0x%x=0x%x" % (mem, mem & 0xfff, r32(mvb, mem & 0xfff) if mvb else -1)) + mvb = rd(fd, mem & ~0xfff, 0x1000) + off = mem & 0xfff + val = r32(mvb, off) if mvb and off + 4 <= len(mvb) else -1 + print(" sem_mem=0x%x val@+0x%x=0x%x" % (mem, off, val)) q = r64(rd(fd, p + 0x48, 8) or b"\0" * 8) print(" prog_obj=0x%x next=0x%x cblist=0x%x idx95bc=0x%x p34=0x%x" % (q, r64(rd(fd, p + 0x9470, 8) or b"\0" * 8), @@ -154,15 +156,18 @@ def main(): if mode == "scan": lo, hi = 0x204800000, 0x204c0000 limit = 8 + brief = False for i, a in enumerate(sys.argv[3:]): if a == "--min": lo = int(sys.argv[4 + i], 16) if a == "--max": hi = int(sys.argv[4 + i], 16) if a == "--limit": limit = int(sys.argv[4 + i]) + if a == "--brief": brief = True res = find_trackers(pid, fd, lo, hi, limit) for p, sub, last, cur, sem, mem in res: print("P=0x%x sub=0x%x last=0x%x cur=0x%x sem_obj=0x%x sem_mem=0x%x" % (p, sub, last, cur, sem, mem or 0)) - dump_tracker(pid, fd, p) + if not brief: + dump_tracker(pid, fd, p) elif mode == "dump": dump_tracker(pid, fd, int(sys.argv[3], 16)) elif mode == "hex": diff --git a/tools/guest-diag/pin_client_sems.sh b/tools/guest-diag/pin_client_sems.sh new file mode 100755 index 0000000..82d2789 --- /dev/null +++ b/tools/guest-diag/pin_client_sems.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# pin_client_sems.sh - bridge the CUDA client's device mappings into the +# emulation for the live cdt process. +# +# Two things are pinned, both per-process (a new cdt maps new physical pages +# at the same VAs, so re-run this for every cdt run): +# 1. the /dev/nvidia0 window: the driver's channel rings/USERDs (the GR +# channels' GPFIFO VAs live here), so the pump can run their pushes; +# 2. the semaphore pages libcuda's trackers poll (found by scanning the +# client's memory for sem references, then translated through the guest +# pagemap). +# +# Usage: tools/guest-diag/pin_client_sems.sh [iterations] [sleep-secs] +set -u +cd "$(dirname "$(readlink -f "$0")")/../.." +VMEXEC=./scripts/vgpu_vm.py +ITER=${1:-1} +SLEEP=${2:-10} + +PID=$(timeout 20 $VMEXEC exec --timeout 15 'pgrep -x cdt | head -1' 2>/dev/null | tail -1) +[ -n "$PID" ] || { echo "no cdt pid"; exit 1; } +echo "cdt pid=$PID" + +B64=$(gzip -9 < tools/guest-diag/sempages.py | base64 -w0) +timeout 30 $VMEXEC exec --timeout 25 "echo '$B64' | base64 -d | gunzip > /tmp/sempages.py" >/dev/null 2>&1 +B64=$(gzip -9 < tools/guest-diag/memprobe.py | base64 -w0) +timeout 30 $VMEXEC exec --timeout 25 "echo '$B64' | base64 -d | gunzip > /tmp/memprobe.py" >/dev/null 2>&1 +B64=$(base64 -w0 tools/guest-diag/guest_pmap_all.py) +timeout 30 $VMEXEC exec --timeout 25 "echo '$B64' | base64 -d > /tmp/pmapall.py" >/dev/null 2>&1 + +pin_range() { # pin_range <lo-dec> <hi-dec> <label> + local cur=$1 hi=$2 label=$3 total=0 + while [ "$cur" -lt "$hi" ]; do + mapfile -t lines < <(timeout 60 $VMEXEC exec --timeout 50 "sudo python3 /tmp/pmapall.py $PID $(printf '0x%x' "$cur") 32" 2>/dev/null | grep -aE '^0x[0-9a-f]+,0x[0-9a-f]+$') + [ "${#lines[@]}" -gt 0 ] || break + for l in "${lines[@]}"; do + echo "$l,0x1000,1" | sudo tee /sys/module/open_iov_vfio/parameters/sema_map_add >/dev/null + done + total=$((total + ${#lines[@]})) + local last=${lines[-1]%%,*} + cur=$(( 16#${last#0x} + 0x1000 )) + done + echo " $label: pinned $total page(s)" +} + +for it in $(seq 1 "$ITER"); do + # 1. /dev/nvidia0 windows (channel rings/USERDs). + mapfile -t n0 < <(timeout 30 $VMEXEC exec --timeout 25 "awk '/\\/dev\\/nvidia0/ {print \$1}' /proc/$PID/maps" 2>/dev/null | grep -E '^[0-9a-f]+-[0-9a-f]+$') + for r in "${n0[@]}"; do + lo=$(( 16#${r%-*} )) + hi=$(( 16#${r#*-} )) + pin_range "$lo" "$hi" "nvidia0 $r" + done + + # 2. client semaphore pages. + lines=$(timeout 150 $VMEXEC exec --timeout 140 "sudo timeout 120 python3 /tmp/sempages.py $PID" 2>/dev/null | grep -E '^0x[0-9a-f]+$') + n=0 + for va in $lines; do + line=$(timeout 30 $VMEXEC exec --timeout 25 "sudo python3 /tmp/pmapall.py $PID $va 1" 2>/dev/null | grep -E '^0x[0-9a-f]+,0x[0-9a-f]+$' | head -1) + [ -n "$line" ] || continue + echo "$line,0x1000,1" | sudo tee /sys/module/open_iov_vfio/parameters/sema_map_add >/dev/null || continue + n=$((n + 1)) + done + echo "iter $it: pinned $n sem page(s) (pid $PID)" + [ "$it" -lt "$ITER" ] && sleep "$SLEEP" +done diff --git a/tools/guest-diag/sempages.py b/tools/guest-diag/sempages.py new file mode 100755 index 0000000..ad4dae6 --- /dev/null +++ b/tools/guest-diag/sempages.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# Print the distinct pages of the semaphore VAs a process references +# (reuses memprobe.py's scanner; run in the guest as root). The VA window is +# derived from the process's /dev/nvidia* mappings unless lo/hi are given. +# usage: sudo python3 sempages.py <pid> [lo] [hi] +import os +import sys + +sys.path.insert(0, '/tmp') +import memprobe as m + +pid = int(sys.argv[1]) +if len(sys.argv) > 3: + lo = int(sys.argv[2], 16) + hi = int(sys.argv[3], 16) +else: + lo = 1 << 64 + hi = 0 + with open('/proc/%d/maps' % pid) as f: + for line in f: + parts = line.split() + if len(parts) < 6 or not parts[5].startswith('/dev/nvidia'): + continue + a, b = parts[0].split('-') + lo = min(lo, int(a, 16)) + hi = max(hi, int(b, 16)) + if hi == 0: + print('no /dev/nvidia mappings') + raise SystemExit(1) + +fd = os.open('/proc/%d/mem' % pid, os.O_RDONLY) +sems = m.sem_objs(pid, fd, lo, hi) +os.close(fd) +pages = sorted({va & ~0xfff for va in sems}) +print('sem VAs: %d pages: %d (window 0x%x..0x%x)' % (len(sems), len(pages), lo, hi)) +for pg in pages: + print('0x%x' % pg) |
