summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/guest_pin_stream.py
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-30 12:54:15 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-30 12:54:15 -0400
commitf0d1b77f54a07c249a06dc57280a92d412c01fab (patch)
treeb39df59a9ae00c083a80316a9b73f1dd3c8fba2a /tools/guest-diag/guest_pin_stream.py
parentd3e35752e8d78a368940fc262b0013e1d50deed4 (diff)
downloadopen-iov-f0d1b77f54a07c249a06dc57280a92d412c01fab.tar.gz
open-iov-f0d1b77f54a07c249a06dc57280a92d412c01fab.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).
Diffstat (limited to 'tools/guest-diag/guest_pin_stream.py')
-rw-r--r--tools/guest-diag/guest_pin_stream.py67
1 files changed, 67 insertions, 0 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())