summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-29 17:08:09 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-29 17:08:09 -0400
commit902ed4ef130084052ff56f7e9f8f57b9928d811a (patch)
tree538fe04d196a1b433c5e40107d73f03acc13b8a3
parentd37825435946a47e1f9b66aee8764aba5362b138 (diff)
downloadopen-iov-902ed4ef130084052ff56f7e9f8f57b9928d811a.tar.gz
open-iov-902ed4ef130084052ff56f7e9f8f57b9928d811a.zip
guest-diag: pin the CE push-buffer window too
The failing wait (uvtw) is uvm_tracker_wait_for_entry for value 3 on a CE channel whose tracking sem stayed at payload 1 for 14 s: its pushes' release values 2..3 were never applied because the CE push-buffer pages (0x120000000 + 0x100000*rank) are outside the GR window and were only found by the content sweep, late and often wrong (false=3065). Scan both windows in the guest so the host gets guest-truth translations for the CE pushes as well.
-rw-r--r--tools/guest-diag/guest_pin_stream.py61
1 files changed, 39 insertions, 22 deletions
diff --git a/tools/guest-diag/guest_pin_stream.py b/tools/guest-diag/guest_pin_stream.py
index bed4728..ac4f72b 100644
--- a/tools/guest-diag/guest_pin_stream.py
+++ b/tools/guest-diag/guest_pin_stream.py
@@ -30,6 +30,12 @@ REGION = 0x200000
# ..0x45000) and the per-channel push allocations follow; 512 KB per 2 MB
# region covers both without scanning holes that hold nothing.
SPAN = int(os.environ.get("STREAM_SPAN", "0x80000"), 16)
+# CE push-buffer window: one 1 MB allocation per CE channel at
+# 0x120000000 + 0x100000*rank; SPAN2 covers the 1 MB boundary inside a
+# 2 MB region.
+LO2 = int(os.environ.get("STREAM_LO2", "0x120000000"), 16)
+HI2 = int(os.environ.get("STREAM_HI2", "0x120600000"), 16)
+SPAN2 = int(os.environ.get("STREAM_SPAN2", "0x180000"), 16)
TRACE = ("/sys/kernel/tracing/trace"
if os.path.exists("/sys/kernel/tracing/trace")
else "/sys/kernel/debug/tracing/trace")
@@ -108,6 +114,31 @@ def cdt_pid():
return None
+def scan_window(fd, lo, hi, span, out):
+ 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
+
+
def scan(pid):
"""Present pages in the first SPAN bytes of every REGION, as {va: pa}.
@@ -115,32 +146,18 @@ def scan(pid):
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.
+
+ Two windows: the GR window (rings/pushbuffers/counter page) and the CE
+ push-buffer window (0x120000000+, one 1 MB allocation per CE channel).
+ The CE window uses a wider span because the per-channel allocations are
+ 1 MB apart inside a 2 MB region.
"""
fd = os.open('/proc/%d/pagemap' % pid, os.O_RDONLY)
out = {}
try:
- 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
+ scan_window(fd, LO, HI, SPAN, out)
+ if HI2 > LO2:
+ scan_window(fd, LO2, HI2, SPAN2, out)
finally:
os.close(fd)
return out