summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-24 01:58:15 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-24 01:58:15 -0400
commit0b799c9487477fd31a56e0c386e6ba3896c4c569 (patch)
tree3a4ae80aa78433d31a3f098c68b3b47b4c157e58 /tools
parent2a59dcb768541b759ada9cd6f24986e2c234bd37 (diff)
downloadopen-iov-0b799c9487477fd31a56e0c386e6ba3896c4c569.tar.gz
open-iov-0b799c9487477fd31a56e0c386e6ba3896c4c569.zip
kernel: validate learned semaphore maps and post guest GPU events
- admit {GPU VA -> PA} pairs only when the preceding bytes form a valid client memory descriptor header (type 0/1, CPU+GPU mapped bits), prefer validated pairs over headerless candidates under first-wins dedup, and invalidate the translation cache on every rebuild; fixes stale identity entries shadowing live pool pages and lets the guest UVM channel bring-up complete on its own - capture OS event allocations (class 0x79) and post event records (function 0x1003) into the guest status queue when a channel's busy notification is consumed, with a broadcast fallback, serialized with replies via a queue lock - add BAR0 shadow introspection (dump_bar0/scan_bar0) and guest-side diagnostic scripts for the VM cycle
Diffstat (limited to 'tools')
-rwxr-xr-xtools/guest-diag/cycle_cdt.sh46
-rw-r--r--tools/guest-diag/guest_pmap.py37
-rwxr-xr-xtools/guest-diag/inject_tracker.sh30
-rwxr-xr-xtools/guest-diag/pmap_va.sh21
-rwxr-xr-xtools/guest-diag/run_traced.sh53
-rwxr-xr-xtools/guest-diag/vmrestart.sh31
6 files changed, 218 insertions, 0 deletions
diff --git a/tools/guest-diag/cycle_cdt.sh b/tools/guest-diag/cycle_cdt.sh
new file mode 100755
index 0000000..009bdfc
--- /dev/null
+++ b/tools/guest-diag/cycle_cdt.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# cycle_cdt.sh - full test cycle: push tools, start cdt, inject the libcuda
+# tracker page mapping when the tracker wait is reached, poke UVM's tracker
+# when it stalls, and report where cdt ends up.
+set -u
+cd "$(dirname "$(readlink -f "$0")")/../.."
+
+echo "[*] waiting for guest"
+for i in $(seq 1 30); do
+ timeout 20 ./scripts/vgpu_vm.py exec --timeout 12 'uptime >/dev/null 2>&1 && echo READY' 2>/dev/null | grep -q READY && break
+ sleep 5
+done
+
+./tools/guest-diag/push_guest_tools.sh >/tmp/opencode/push.log 2>&1 || { echo "PUSH FAILED"; tail -3 /tmp/opencode/push.log; exit 1; }
+echo "[*] tools pushed"
+
+timeout 40 ./scripts/vgpu_vm.py exec --timeout 35 'sudo nvidia-modprobe -c 0; sudo nvidia-modprobe -u; sudo modprobe nvidia-uvm; sudo sh -c "echo 1 > /sys/module/nvidia_uvm/parameters/uvm_debug_prints"; sudo rm -f /tmp/cdt.out; sudo setsid /tmp/cdt > /tmp/cdt.out 2>&1 & sleep 2; tail -1 /tmp/cdt.out' 2>/dev/null | tail -1
+echo "[*] cdt started"
+
+# wait for the libcuda tracker wait and inject the page mapping
+SEEN_CTX=0
+for i in $(seq 1 24); do
+ sleep 10
+ STATE=$(timeout 20 ./scripts/vgpu_vm.py exec --timeout 15 'tail -1 /tmp/cdt.out' 2>/dev/null | tail -1)
+ echo " [$((i*10))s] $STATE"
+ if echo "$STATE" | grep -q "cuCtxCreate"; then
+ SEEN_CTX=1
+ if [ ! -f /tmp/opencode/injected_tracker ]; then
+ if /tmp/opencode/inject_tracker.sh >/tmp/opencode/inject.log 2>&1; then
+ touch /tmp/opencode/injected_tracker
+ echo " [*] tracker mapping injected: $(tail -1 /tmp/opencode/inject.log)"
+ fi
+ fi
+ fi
+ if echo "$STATE" | grep -qE "FAILED|ALL OK|cuMemAlloc"; then
+ echo "[*] cuCtxCreate finished: $STATE"
+ break
+ fi
+done
+
+# if UVM's tracking semaphore lags, poke it forward
+SEMAPA=$(sudo sh -c 'echo 0x121000010,1 > /sys/module/open_iov_vfio/parameters/sema_probe' 2>/dev/null; sleep 2; sudo dmesg -T | grep -A2 "sema-probe: va=0x121000010" | grep -oE "pa=0x[0-9a-f]+" | head -1 | cut -d= -f2)
+if [ -n "$SEMAPA" ]; then
+ CUR=$(sudo dmesg -T | grep -A3 "sema-probe: va=0x121000010" | grep -oE "\[0\] 0x[0-9a-f]+" | tail -1 | awk '{print $2}')
+ echo "[*] UVM tracking sema pa=$SEMAPA value=$CUR"
+fi
diff --git a/tools/guest-diag/guest_pmap.py b/tools/guest-diag/guest_pmap.py
new file mode 100644
index 0000000..96b7ddc
--- /dev/null
+++ b/tools/guest-diag/guest_pmap.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+# guest-side helper: translate GPU/user VAs of a process through its pagemap.
+# Usage: sudo python3 guest_pmap.py <pid> <hex-va> [hex-va ...]
+import os
+import sys
+
+pid = int(sys.argv[1])
+vas = [int(x, 16) for x in sys.argv[2:]]
+
+maps = []
+try:
+ with open('/proc/%d/maps' % pid) as f:
+ for line in f:
+ parts = line.split()
+ a, b = parts[0].split('-')
+ maps.append((int(a, 16), int(b, 16), line.strip()))
+except OSError as e:
+ print('MAPS_ERROR: %s' % e)
+
+def in_maps(va):
+ for a, b, line in maps:
+ if a <= va < b:
+ return line
+ return None
+
+fd = os.open('/proc/%d/pagemap' % pid, os.O_RDONLY)
+try:
+ for va in vas:
+ os.lseek(fd, (va // 4096) * 8, 0)
+ e = int.from_bytes(os.read(fd, 8), 'little')
+ present = (e >> 63) & 1
+ pfn = e & ((1 << 55) - 1)
+ pa = (pfn << 12) | (va & 0xfff)
+ line = in_maps(va)
+ print('VA=0x%x present=%d PA=0x%x map=%s' % (va, present, pa, line if line else '-'))
+finally:
+ os.close(fd)
diff --git a/tools/guest-diag/inject_tracker.sh b/tools/guest-diag/inject_tracker.sh
new file mode 100755
index 0000000..a563b0d
--- /dev/null
+++ b/tools/guest-diag/inject_tracker.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+# inject_tracker.sh - find the libcuda tracker slot the stuck cdt is polling,
+# translate its page through the guest pagemap, pin the mapping in the service.
+set -u
+cd "$(dirname "$(readlink -f "$0")")/../.."
+
+OUT=$(timeout 100 ./scripts/vgpu_vm.py exec --timeout 90 'PID=$(pgrep -x cdt | head -1); B=$(sudo grep libcuda.so.570.144 /proc/$PID/maps | head -1 | cut -d- -f1); sudo timeout -s KILL 70 gdb -p $PID -batch -ex "set pagination off" -ex "set confirm off" -ex "break *(long)(0x$B+0x3c7970)" -ex "continue" -ex "set \$p2 = *(unsigned long*)(\$rdi+0x20)" -ex "set \$vp = *(unsigned long*)(\$p2+0x10)" -ex "printf \"VALPTR=0x%lx EXPECTED=%lu PID=%d\n\", \$vp, \$rsi, $PID" -ex "detach" 2>&1; sudo kill -CONT $PID' 2>/dev/null)
+LINE=$(echo "$OUT" | grep "VALPTR=" | head -1)
+VALPTR_HEX=$(echo "$LINE" | sed -n 's/.*VALPTR=0x\([0-9a-f]*\).*/\1/p')
+PID=$(echo "$LINE" | sed -n 's/.*PID=\([0-9]*\).*/\1/p')
+[ -n "$VALPTR_HEX" ] || { echo "NO VALPTR FOUND"; echo "$OUT" | tail -3; exit 1; }
+echo "valptr=0x$VALPTR_HEX pid=$PID"
+
+PA=$(timeout 40 ./scripts/vgpu_vm.py exec --timeout 35 "sudo python3 -c \"
+import os
+pid=$PID
+va=0x$VALPTR_HEX
+fd=os.open('/proc/%d/pagemap'%pid, os.O_RDONLY)
+os.lseek(fd, (va//4096)*8, 0)
+e=int.from_bytes(os.read(fd,8),'little')
+os.close(fd)
+pfn=e & ((1<<55)-1)
+print('PA=0x%x'%((pfn<<12)|(va&0xfff)))
+\"" 2>/dev/null | grep -o 'PA=0x[0-9a-f]*' | head -1 | sed 's/^PA=//')
+[ -n "$PA" ] || { echo "NO PA FOUND"; exit 1; }
+echo "target pa=$PA"
+
+sudo sh -c "echo 0x$VALPTR_HEX,$PA > /sys/module/open_iov_vfio/parameters/sema_map_add" || exit 1
+sleep 1
+sudo dmesg -T | grep "injected mapping" | tail -1
diff --git a/tools/guest-diag/pmap_va.sh b/tools/guest-diag/pmap_va.sh
new file mode 100755
index 0000000..e5bcef4
--- /dev/null
+++ b/tools/guest-diag/pmap_va.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+# pmap_va.sh <pid|name> <hex-va> [hex-va ...]
+# Push guest_pmap.py to the running VM and translate the VAs of the named
+# process through the guest's own pagemap (root in the guest).
+set -u
+cd "$(dirname "$0")/../.."
+
+TARGET="$1"; shift
+VAS="$*"
+
+B64=$(base64 -w0 tools/guest-diag/guest_pmap.py)
+timeout 30 ./scripts/vgpu_vm.py exec --timeout 25 \
+ "echo '$B64' | base64 -d > /tmp/pmap.py" >/dev/null 2>&1
+
+if ! [[ "$TARGET" =~ ^[0-9]+$ ]]; then
+ PID=$(timeout 20 ./scripts/vgpu_vm.py exec --timeout 15 "pgrep -x $TARGET | head -1" 2>/dev/null | tail -1)
+ [ -n "$PID" ] && TARGET="$PID"
+fi
+[ -n "$TARGET" ] || { echo "no pid found"; exit 1; }
+
+timeout 60 ./scripts/vgpu_vm.py exec --timeout 55 "sudo python3 /tmp/pmap.py $TARGET $VAS" 2>/dev/null | grep -E '^VA='
diff --git a/tools/guest-diag/run_traced.sh b/tools/guest-diag/run_traced.sh
new file mode 100755
index 0000000..e8d3a09
--- /dev/null
+++ b/tools/guest-diag/run_traced.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+# run_traced.sh - run cdt under the ioctl tracer, keep the libcuda tracker page
+# mapped (pagemap + sema_map_add) and poke UVM's tracking semaphore forward as
+# the sweep completes pushes, so the run reaches the rc=999 rollback where the
+# tracer records the failing driver call.
+set -u
+cd "$(dirname "$(readlink -f "$0")")/../.."
+
+EXEC() { timeout 30 ./scripts/vgpu_vm.py exec --timeout 25 "$1" 2>/dev/null; }
+
+EXEC 'sudo pkill -9 -x cdt; sudo pkill -9 -x ptio; sleep 1; sudo rm -f /tmp/cdt.out /tmp/tr.log; sudo setsid /tmp/ptio /tmp/cdt > /tmp/cdt.out 2> /tmp/tr.log & sleep 3; pgrep -x cdt | head -1'
+
+for i in $(seq 1 40); do
+ sleep 10
+ PID=$(EXEC 'pgrep -x cdt | head -1' | tail -1)
+ [ -n "$PID" ] || { echo "[*] cdt gone at $((i*10))s"; break; }
+
+ # pin the tracker page when the guest has it mapped
+ PA=$(EXEC "sudo python3 -c \"
+import os
+fd=os.open('/proc/$PID/pagemap', os.O_RDONLY)
+os.lseek(fd, (0x20480f000//4096)*8, 0)
+e=int.from_bytes(os.read(fd,8),'little')
+os.close(fd)
+pfn=e & ((1<<55)-1)
+print('PA=0x%x'%((pfn<<12)|0xeb0))
+\"" | grep -o 'PA=0x[0-9a-f]*' | head -1 | sed 's/PA=//')
+ if [ -n "$PA" ]; then
+ sudo sh -c "echo 0x20480feb0,$PA > /sys/module/open_iov_vfio/parameters/sema_map_add" 2>/dev/null
+ fi
+
+ # nudge UVM's tracking semaphore forward when the sweep has stalled
+ sudo sh -c 'echo 0x121000010,1 > /sys/module/open_iov_vfio/parameters/sema_probe' 2>/dev/null
+ sleep 1
+ VALS=$(sudo dmesg -T | grep -A4 "sema-probe: va=0x121000010" | tail -4)
+ SPA=$(echo "$VALS" | grep -oE "pa=0x[0-9a-f]+" | head -1 | cut -d= -f2)
+ CUR=$(echo "$VALS" | grep -oE "\[0\] 0x[0-9a-f]+" | head -1 | awk '{print $2}')
+ if [ -n "$SPA" ] && [ -n "$CUR" ]; then
+ NEXT=$(printf '0x%x' $(( CUR + 1 )))
+ sudo sh -c "echo 0x$SPA,$NEXT > /sys/module/open_iov_vfio/parameters/poke_guest" 2>/dev/null
+ echo " [$((i*10))s] cdt=$PID sema $CUR -> $NEXT"
+ else
+ echo " [$((i*10))s] cdt=$PID (sema unresolved)"
+ fi
+
+ STATE=$(EXEC 'tail -1 /tmp/cdt.out' | tail -1)
+ case "$STATE" in
+ *FAILED*|*"ALL OK"*|*cuMemAlloc*) echo "[*] finished: $STATE"; break ;;
+ esac
+done
+
+echo "=== trace tail ==="
+EXEC 'tail -25 /tmp/tr.log'
diff --git a/tools/guest-diag/vmrestart.sh b/tools/guest-diag/vmrestart.sh
new file mode 100755
index 0000000..dbe60ec
--- /dev/null
+++ b/tools/guest-diag/vmrestart.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+# vmrestart.sh - stop QEMU, reload open-iov-vfio.ko from the tree, fresh mdev,
+# relaunch the vGPU VM. Everything bounded so a stuck step cannot hang the
+# caller.
+set -u
+cd "$(dirname "$(readlink -f "$0")")/../.."
+
+sudo pkill -f qemu-system-x86_64 2>/dev/null
+sleep 4
+sudo rm -f /tmp/qemu-guest-agent.sock /tmp/qemu-qmp.sock
+
+for m in /sys/bus/mdev/devices/*; do
+ [ -d "$m" ] || continue
+ timeout 20 sudo sh -c "echo 1 > $m/remove" || echo "WARN: mdev remove timeout"
+done
+
+timeout 30 sudo rmmod open_iov_vfio || { echo "ERROR: rmmod failed"; exit 1; }
+sudo insmod kernel/open-iov-vfio.ko || { echo "ERROR: insmod failed"; exit 1; }
+
+U=$(uuidgen)
+echo "$U" | sudo tee /sys/class/mdev_bus/0000:01:00.0/mdev_supported_types/nvidia-nvidia-vgpu-turing/create >/dev/null
+sed -i "s|/sys/bus/mdev/devices/[0-9a-f-]*|/sys/bus/mdev/devices/$U|" tools/guest-diag/launch_qemu_rootport.run.sh
+mkdir -p /tmp/sockets/e726ba0b-204f-4ec9-91da-983ed7f41d06
+
+(setsid nohup tools/guest-diag/launch_qemu_rootport.run.sh > tools/guest-diag/run/qemu.log 2>&1 &)
+sleep 6
+if pgrep -f qemu-system-x86_64 >/dev/null; then
+ echo "VM up (mdev $U)"
+else
+ echo "VM FAILED"; tail -5 tools/guest-diag/run/qemu.log; exit 1
+fi