diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-29 14:54:34 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-29 14:54:34 -0400 |
| commit | 31d1c3e71e4b80a6b2217881f510e8dd2def4caa (patch) | |
| tree | db102073c1fae28de112d47678131b6f64bc8e66 | |
| parent | c07584101ebd7a8cdabbd463a093ae53756b4c4b (diff) | |
| download | open-iov-31d1c3e71e4b80a6b2217881f510e8dd2def4caa.tar.gz open-iov-31d1c3e71e4b80a6b2217881f510e8dd2def4caa.zip | |
guest-diag: sparse allocation-head scan in the pin stream
The first deltas of a 15k-page window took tens of seconds to arrive while
the client was mapping under load, so the pins still landed after the
client's cuCtxCreate deadline. The scanner now reads only the first 64 KB
of every 2 MB region - the pushes sit at the per-channel allocation heads -
which cuts the per-loop page-table work ~50x, and it logs scan timing so
the latency stays visible.
| -rw-r--r-- | tools/guest-diag/guest_pin_stream.py | 62 |
1 files changed, 43 insertions, 19 deletions
diff --git a/tools/guest-diag/guest_pin_stream.py b/tools/guest-diag/guest_pin_stream.py index 169d048..bb3f99a 100644 --- a/tools/guest-diag/guest_pin_stream.py +++ b/tools/guest-diag/guest_pin_stream.py @@ -22,6 +22,8 @@ HI = int(sys.argv[4], 16) if len(sys.argv) > 4 else 0x206000000 PAMAX = int(sys.argv[5], 16) if len(sys.argv) > 5 else 0x180000000 INTERVAL = float(os.environ.get("STREAM_INTERVAL", "0.25")) BATCH = 4096 +REGION = 0x200000 +SPAN = int(os.environ.get("STREAM_SPAN", "0x10000"), 16) def cdt_pid(): @@ -38,28 +40,38 @@ def cdt_pid(): def scan(pid): - """Present pages of the VA range as {va: pa}.""" + """Present pages in the first SPAN bytes of every REGION, as {va: pa}. + + The pushes sit at the start of each per-channel allocation (2/4 MB + aligned); scanning only the allocation heads keeps the per-loop page + table work small, which is what makes the stream fast while the client + is mapping and the guest is loaded. + """ fd = os.open('/proc/%d/pagemap' % pid, os.O_RDONLY) out = {} try: - va = LO & ~0xFFF - while va < HI: - n = min(BATCH, (HI - va) // 0x1000) - if n <= 0: - break - os.lseek(fd, (va // 4096) * 8, 0) - data = os.read(fd, n * 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 + base = LO & ~(REGION - 1) + while base < HI: + va = max(base, LO) & ~0xFFF + end = min(base + SPAN, HI) + while va < end: + n = min(BATCH, (end - va) // 0x1000) + if n <= 0: + break + os.lseek(fd, (va // 4096) * 8, 0) + data = os.read(fd, n * 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 + base += REGION finally: os.close(fd) return out @@ -71,6 +83,7 @@ def main(): sock = None seen = {} last_pid = None + last_log = 0.0 while True: pid = cdt_pid() if pid is None: @@ -87,13 +100,24 @@ def main(): except OSError: sock.close() sock = None + t0 = time.time() try: cur = scan(pid) except OSError: time.sleep(INTERVAL) continue + dt = time.time() - t0 new = [(va, pa) for va, pa in cur.items() if seen.get(va) != pa] seen = cur + now = time.time() + if now - last_log > 1.0: + last_log = now + try: + with open('/tmp/guest_pin_stream.log', 'a') as f: + f.write('%s pid=%s present=%d new=%d scan=%.3fs\n' + % (time.strftime('%T'), pid, len(cur), len(new), dt)) + except OSError: + pass if not new: time.sleep(INTERVAL) continue |
