summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-30 05:28:13 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-30 05:28:13 -0400
commit14f195400a14034612fd9b1f69fb6296419eae6d (patch)
tree91cf8c127a5eb90c2b9037581c826a97891f6cf1 /tools/guest-diag
parente5d1b3d6ab61b85b945400fcbd3637052feb2ace (diff)
downloadopen-iov-14f195400a14034612fd9b1f69fb6296419eae6d.tar.gz
open-iov-14f195400a14034612fd9b1f69fb6296419eae6d.zip
guest-diag: pushbuffer pin survives PTI holes (full-range scan + bounded retries until complete)
Diffstat (limited to 'tools/guest-diag')
-rwxr-xr-xtools/guest-diag/semfix_uvmt.py31
-rw-r--r--tools/guest-diag/semfix_watch.py15
2 files changed, 37 insertions, 9 deletions
diff --git a/tools/guest-diag/semfix_uvmt.py b/tools/guest-diag/semfix_uvmt.py
index fba465d..b9f3ac9 100755
--- a/tools/guest-diag/semfix_uvmt.py
+++ b/tools/guest-diag/semfix_uvmt.py
@@ -32,7 +32,7 @@ from semfix_watch import ( # noqa: E402
Qmp, QMP_SOCK, is_kern, is_gpu, diag_str, guest_trace,
resolve_sem_pool, pin, pin_bulk, resolve_from_channel,
resolve_from_pushbuffer, resolve_from_sem, resolve_pushbuffer_wide,
- pin_pushbuffer,
+ pin_pushbuffer, PUSHBUF_MAX_PAGES,
)
TIMELINE = os.environ.get("TIMELINE", "/tmp/opencode/uvmt_timeline.log")
@@ -250,7 +250,7 @@ def main():
seen_sem_pin = set()
seen_pb = set()
seen_ch = set()
- pinned_ranges = set()
+ pinned_ranges = {}
last_state = {}
last_seen_t = {}
dseen = set()
@@ -262,10 +262,22 @@ def main():
def pin_range_once(gpu_vas, cpu, gpa, size=0):
key = tuple(gpu_vas), cpu, size
- if key in pinned_ranges:
+ st = pinned_ranges.get(key)
+ if st is None:
+ st = pinned_ranges[key] = {"tries": 0, "done": False,
+ "args": (gpu_vas, cpu, gpa, size)}
+ if st["done"]:
return
- pinned_ranges.add(key)
- pin_pushbuffer(q, gpu_vas, cpu, gpa, size)
+ st["tries"] += 1
+ pinned = pin_pushbuffer(q, gpu_vas, cpu, gpa, size)
+ expected = min(max(size >> 12, 1) if size else PUSHBUF_MAX_PAGES,
+ PUSHBUF_MAX_PAGES)
+ if pinned >= expected:
+ st["done"] = True
+ elif st["tries"] >= 12:
+ st["done"] = True
+ log("semfix: pushbuf pin incomplete after %d tries: %d/%d pages"
+ % (st["tries"], pinned, expected))
while True:
loop += 1
@@ -388,6 +400,15 @@ def main():
if DLOG:
dmesg_releases(dseen)
+ # Retry incomplete push-buffer pins: gva2gpa of the kernel vmalloc
+ # alias fails while the sampled vCPU runs in user mode (PTI), so a
+ # pass can miss a run of pages that resolves a moment later. Only
+ # every 8th loop - a full pass costs thousands of QMP reads.
+ if loop % 8 == 0:
+ for st in list(pinned_ranges.values()):
+ if not st["done"] and st["tries"] > 0:
+ pin_range_once(*st["args"])
+
# Pin the completion-counter page of a newly started client, before its
# first GR pushes decode their releases. The pid comes from the guest
# scanner; the guest-agent fallback is only a safety net. The exec can
diff --git a/tools/guest-diag/semfix_watch.py b/tools/guest-diag/semfix_watch.py
index 1c33de6..fa435ea 100644
--- a/tools/guest-diag/semfix_watch.py
+++ b/tools/guest-diag/semfix_watch.py
@@ -392,7 +392,14 @@ def resolve_sem_pool(q, sem):
def pin_pushbuffer(q, gpu_vas, cpu, gpa, size=0):
"""Pin the pushbuffer pages (the UVM allocation is 16 x 1 MB chunks =
16 MB = 4096 pages; the page layout is only piecewise contiguous, so
- group ascending-contiguous runs into one bulk injection each)."""
+ group ascending-contiguous runs into one bulk injection each).
+
+ gva2gpa of a kernel vmalloc VA intermittently fails while the sampled
+ vCPU executes in user mode (page-table isolation hides the kernel half),
+ so a pass can miss a run of pages. Do not stop at the first hole: scan
+ the whole range (bailing only when essentially everything misses) and
+ return the pinned count, so the caller can retry the remainder later.
+ """
pages = min(max(size >> 12, 1) if size else PUSHBUF_MAX_PAGES,
PUSHBUF_MAX_PAGES)
pinned = 0
@@ -403,9 +410,9 @@ def pin_pushbuffer(q, gpu_vas, cpu, gpa, size=0):
g = q.gva2gpa(cpu + i * 0x1000)
if not g:
miss += 1
- if miss >= 64:
- print("semfix: pushbuf va=0x%x: %d unmapped pages at %d, stop (%d pinned)"
- % (gv, miss, i, pinned), flush=True)
+ if miss >= pages:
+ print("semfix: pushbuf va=0x%x: no page resolved (%d "
+ "tried), wrong cpu base" % (gv, pages), flush=True)
return pinned
i += 1
continue