diff options
Diffstat (limited to 'tools/guest-diag/guest_pin_stream.py')
| -rw-r--r-- | tools/guest-diag/guest_pin_stream.py | 67 |
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()) |
