diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-24 01:58:15 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-24 01:58:15 -0400 |
| commit | 0b799c9487477fd31a56e0c386e6ba3896c4c569 (patch) | |
| tree | 3a4ae80aa78433d31a3f098c68b3b47b4c157e58 /tools | |
| parent | 2a59dcb768541b759ada9cd6f24986e2c234bd37 (diff) | |
| download | open-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-x | tools/guest-diag/cycle_cdt.sh | 46 | ||||
| -rw-r--r-- | tools/guest-diag/guest_pmap.py | 37 | ||||
| -rwxr-xr-x | tools/guest-diag/inject_tracker.sh | 30 | ||||
| -rwxr-xr-x | tools/guest-diag/pmap_va.sh | 21 | ||||
| -rwxr-xr-x | tools/guest-diag/run_traced.sh | 53 | ||||
| -rwxr-xr-x | tools/guest-diag/vmrestart.sh | 31 |
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 |
