From 1147d4f81da01397b52fd1087bf4453de056a0ee Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Tue, 29 Sep 2026 17:08:09 -0400 Subject: 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. --- tools/guest-diag/guest_pin_stream.py | 61 +++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 22 deletions(-) (limited to 'tools') 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 -- cgit v1.2.3