diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-30 12:54:15 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-30 12:54:15 -0400 |
| commit | 481db042cca8cf61f861a50d40b6c14a0f3c54aa (patch) | |
| tree | 315f0f2cc380b30493e744afd734493cbd7733dc | |
| parent | 7d21a9fa1e7798e1a4d2938ebee307f6da6e4c74 (diff) | |
| download | open-iov-481db042cca8cf61f861a50d40b6c14a0f3c54aa.tar.gz open-iov-481db042cca8cf61f861a50d40b6c14a0f3c54aa.zip | |
guest-diag: stream the client's high-VA UVM sysmem pages as pins
The DtoH staging buffer lives in the client's high-VA /dev/zero|nvidia
mappings (e.g. devptr+0x3c00100), outside the scanner's fixed CE/GR
windows; only the host's pagemap read can see it. The guest scanner
now walks those VMAs and streams their present pages so the module
pins them. run_cycle.sh learns the sema_dbg master switch (auto-on
when any watch knob is set).
| -rw-r--r-- | tools/guest-diag/guest_pin_stream.py | 67 | ||||
| -rwxr-xr-x | tools/guest-diag/run_cycle.sh | 14 |
2 files changed, 79 insertions, 2 deletions
diff --git a/tools/guest-diag/guest_pin_stream.py b/tools/guest-diag/guest_pin_stream.py index 5f71731..3b7d9b1 100644 --- a/tools/guest-diag/guest_pin_stream.py +++ b/tools/guest-diag/guest_pin_stream.py @@ -40,6 +40,17 @@ SPAN = int(os.environ.get("STREAM_SPAN", "0x80000"), 16) LO2 = int(os.environ.get("STREAM_LO2", "0x120000000"), 16) HI2 = int(os.environ.get("STREAM_HI2", "0x121400000"), 16) SPAN2 = int(os.environ.get("STREAM_SPAN2", "0x180000"), 16) +# UVM system-memory AVMAs: the client's sysmem buffers (the DtoH staging +# buffer among them) are /dev/zero mappings at high GPU VAs (e.g. +# 0x79c21c800000). They are not inside the two fixed windows, and only the +# GMMU page tables can translate them, so stream their present pages as +# pins too. Restricted to the high VA range and to nvidia/zero mappings; +# the PAMAX filter rejects device (BAR) PFNs. +VMA_LO = int(os.environ.get("STREAM_VMA_LO", "0x40000000000"), 16) +VMA_EVERY = int(os.environ.get("STREAM_VMA_EVERY", "1")) +VMA_MAX_PAGES = int(os.environ.get("STREAM_VMA_MAX_PAGES", "16384")) +VMA_PATHS = tuple(p for p in os.environ.get( + "STREAM_VMA_PATHS", "zero,nvidia").split(",") if p) TRACE = ("/sys/kernel/tracing/trace" if os.path.exists("/sys/kernel/tracing/trace") else "/sys/kernel/debug/tracing/trace") @@ -156,6 +167,56 @@ def scan_window(fd, lo, hi, span, out): base += REGION +def scan_vmas(fd, pid, out): + """Present pages of the client's high-VA nvidia//dev/zero mappings. + + These are UVM system-memory mappings (sysmem buffers, DtoH staging); + the fast windows do not cover them and the host cannot resolve them + from guest RAM alone. `fd` is the pagemap fd of the client. + """ + n = 0 + try: + maps = open('/proc/%d/maps' % pid) + except OSError: + return + try: + for line in maps: + parts = line.split() + if len(parts) < 6: + continue + path = parts[5] + if not any(p in path for p in VMA_PATHS): + continue + try: + a, b = parts[0].split('-') + lo, hi = int(a, 16), int(b, 16) + except ValueError: + continue + if lo < VMA_LO: + continue + va = (lo + 0xFFF) & ~0xFFF + while va < hi and n < VMA_MAX_PAGES: + cnt = min(256, (hi - va) // 0x1000) + if cnt <= 0: + break + os.lseek(fd, (va // 4096) * 8, 0) + data = os.read(fd, cnt * 8) + got = len(data) // 8 + for i in range(got): + e = int.from_bytes(data[i * 8:i * 8 + 8], 'little') + if not ((e >> 63) & 1): + continue + pfn = e & ((1 << 55) - 1) + pa = pfn << 12 + if not pa or (PAMAX and pa >= PAMAX): + continue + out[va + i * 0x1000] = pa + va += got * 0x1000 + n += got + finally: + maps.close() + + def scan(pid): """Present pages in the first SPAN bytes of every REGION, as {va: pa}. @@ -171,15 +232,21 @@ def scan(pid): """ fd = os.open('/proc/%d/pagemap' % pid, os.O_RDONLY) out = {} + scan.loop += 1 try: scan_window(fd, LO, HI, SPAN, out) if HI2 > LO2: scan_window(fd, LO2, HI2, SPAN2, out) + if VMA_EVERY and scan.loop % VMA_EVERY == 0: + scan_vmas(fd, pid, out) finally: os.close(fd) return out +scan.loop = 0 + + def main(): with open('/tmp/guest_pin_stream.pid', 'w') as f: f.write('%d\n' % os.getpid()) diff --git a/tools/guest-diag/run_cycle.sh b/tools/guest-diag/run_cycle.sh index c3f4ffa..d6ab63f 100755 --- a/tools/guest-diag/run_cycle.sh +++ b/tools/guest-diag/run_cycle.sh @@ -28,13 +28,23 @@ for i in $(seq 1 24); do sleep 5 done -echo "[*] setting module params (SKIP_OPS=${SKIP_OPS:-Y}, OPS_LOG=${OPS_LOG:-8}, COPY_WATCH=${COPY_WATCH:-0}, COPY_PAT=${COPY_PAT:-0}, CE_GRACE=${CE_GRACE:-8000})" +echo "[*] setting module params (SKIP_OPS=${SKIP_OPS:-Y}, OPS_LOG=${OPS_LOG:-8}, COPY_WATCH=${COPY_WATCH:-0}, COPY_PAT=${COPY_PAT:-0}, COPY_DST=${COPY_DST:-0}, COPY_DST2=${COPY_DST2:-0}, CE_GRACE=${CE_GRACE:-8000})" SKIP_OPS="${SKIP_OPS:-Y}" OPS_LOG="${OPS_LOG:-8}" COPY_WATCH="${COPY_WATCH:-0}" COPY_PAT="${COPY_PAT:-0}" +COPY_DST="${COPY_DST:-0}" +COPY_DST2="${COPY_DST2:-0}" CE_GRACE="${CE_GRACE:-8000}" -sudo sh -c "echo Y > /sys/module/open_iov_vfio/parameters/sema_fake_rel; echo $SKIP_OPS > /sys/module/open_iov_vfio/parameters/sema_skip_ops; echo $OPS_LOG > /sys/module/open_iov_vfio/parameters/sema_ops_log; echo $COPY_WATCH > /sys/module/open_iov_vfio/parameters/sema_copy_watch; echo $COPY_PAT > /sys/module/open_iov_vfio/parameters/sema_copy_pat; echo $CE_GRACE > /sys/module/open_iov_vfio/parameters/sema_ce_grace_ms" +# The per-push/per-op tracing is behind the master sema_dbg switch (default +# off, so a normal boot stays quiet). Instrumenting implies enabling it. +DBG="${DBG:-N}" +if [ "$DBG" != "Y" ] && { [ "$COPY_WATCH" != "0" ] || [ "$COPY_PAT" != "0" ] || \ + [ "$COPY_DST" != "0" ] || [ "$COPY_DST2" != "0" ] || [ "$OPS_LOG" -gt 8 ] 2>/dev/null; }; then + DBG=Y +fi +echo " sema_dbg=$DBG" +sudo sh -c "echo $DBG > /sys/module/open_iov_vfio/parameters/sema_dbg; echo Y > /sys/module/open_iov_vfio/parameters/sema_fake_rel; echo $SKIP_OPS > /sys/module/open_iov_vfio/parameters/sema_skip_ops; echo $OPS_LOG > /sys/module/open_iov_vfio/parameters/sema_ops_log; echo $COPY_WATCH > /sys/module/open_iov_vfio/parameters/sema_copy_watch; echo $COPY_PAT > /sys/module/open_iov_vfio/parameters/sema_copy_pat; echo $COPY_DST > /sys/module/open_iov_vfio/parameters/sema_copy_dst; echo $COPY_DST2 > /sys/module/open_iov_vfio/parameters/sema_copy_dst2; echo $CE_GRACE > /sys/module/open_iov_vfio/parameters/sema_ce_grace_ms" echo "[*] pushing guest tools" timeout 300 "$HERE/push_guest_tools.sh" >/dev/null 2>&1 || { echo "push failed"; exit 1; } |
