summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-29 16:14:12 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-29 16:14:12 -0400
commit6d5131e87a2c9cb5c20437669c1917f7be6252b5 (patch)
tree43934878bc90db539f697bdb2fe069ab0c484442 /tools
parent78b8f909784676c5ec6f02a6c48d51ec2a589f96 (diff)
downloadopen-iov-6d5131e87a2c9cb5c20437669c1917f7be6252b5.tar.gz
open-iov-6d5131e87a2c9cb5c20437669c1917f7be6252b5.zip
sema-svc: persist GR counter slot per channel; stable slot layout; bulk pin queue 4k
- counter_pa autoreg result is written back to the channel table so later passes cannot re-target a different slot when the live GR count changes during teardown; re-registration of the same channel preserves the slot. - counter slot layout uses the max GR count ever seen in the generation instead of the shrinking live count. - sema_map_add queue 64 -> 4096 entries so a full-window pin burst drains without ENOSPC retries. - pin_gr_stream: one batched sudo pass per flush (was one process per run, ~4 s for 400 runs), retrying only queue-rejected runs. - semfix_uvmt: SEMFIX_SKIP_PB=1 skips the blocking push-buffer chain walk when the vsock window stream already supplies those pages.
Diffstat (limited to 'tools')
-rw-r--r--tools/guest-diag/pin_gr_stream.py53
-rwxr-xr-xtools/guest-diag/semfix_uvmt.py7
2 files changed, 52 insertions, 8 deletions
diff --git a/tools/guest-diag/pin_gr_stream.py b/tools/guest-diag/pin_gr_stream.py
index 643145b..b80d30a 100644
--- a/tools/guest-diag/pin_gr_stream.py
+++ b/tools/guest-diag/pin_gr_stream.py
@@ -149,22 +149,59 @@ def main():
def _flush(runs, seen):
- """Inject run lines, return number of pages pinned."""
- pages = 0
+ """Inject run lines (one batched sudo pass), return number of pages pinned.
+
+ The old implementation spawned one sudo process per run; with ~400 runs
+ that cost ~4 s, which landed the GR pins after the client's context-init
+ deadline. The batch writes all runs in a single shell, retrying only the
+ ones the kernel queue rejected (ENOSPC) individually.
+ """
+ todo = []
for va, pa, n in runs:
if seen.get(va) == pa and n == 1:
continue
+ todo.append((va, pa, n))
+ if not todo:
+ return 0
+
+ def mark(va, pa, n):
for i in range(n):
seen[va + i * 0x1000] = pa + i * 0x1000
- if not inject(va, pa, n * 0x1000):
- for i in range(n):
- if inject(va + i * 0x1000, pa + i * 0x1000, 0x1000):
- pages += 1
+
+ script = []
+ for va, pa, n in todo:
+ script.append(
+ "printf '0x%x,0x%x,0x%x,1' > %s/sema_map_add 2>/dev/null || "
+ "echo 'FAIL 0x%x 0x%x 0x%x'"
+ % (va, pa, n * 0x1000, PARAM, va, pa, n * 0x1000))
+ failed = set()
+ try:
+ r = subprocess.run(["sudo", "-n", "sh", "-c", "\n".join(script)],
+ capture_output=True, text=True, timeout=60)
+ for line in r.stdout.splitlines():
+ m = re.match(r"FAIL (0x[0-9a-f]+) (0x[0-9a-f]+) (0x[0-9a-f]+)",
+ line.strip())
+ if m:
+ failed.add((int(m.group(1), 16), int(m.group(2), 16),
+ int(m.group(3), 16) // 0x1000))
+ except Exception as e:
+ log("batch inject failed: %s" % e)
+ failed = set(todo)
+
+ pages = 0
+ for va, pa, n in todo:
+ if (va, pa, n) in failed:
continue
+ mark(va, pa, n)
pages += n
+ for va, pa, n in failed:
+ for i in range(n):
+ if inject(va + i * 0x1000, pa + i * 0x1000, 0x1000):
+ mark(va + i * 0x1000, pa + i * 0x1000, 1)
+ pages += 1
if pages:
- log("pinned %d page(s) in %d run(s)" % (pages, len(runs)))
- for va, pa, n in runs[:3]:
+ log("pinned %d page(s) in %d run(s)" % (pages, len(todo)))
+ for va, pa, n in todo[:3]:
log(" va=0x%x -> pa=0x%x (%d)" % (va, pa, n))
return pages
diff --git a/tools/guest-diag/semfix_uvmt.py b/tools/guest-diag/semfix_uvmt.py
index 02d4f9e..fd58299 100755
--- a/tools/guest-diag/semfix_uvmt.py
+++ b/tools/guest-diag/semfix_uvmt.py
@@ -43,6 +43,11 @@ COUNTER_LO = int(os.environ.get("COUNTER_LO", "0x20400f000"), 16)
COUNTER_HI = int(os.environ.get("COUNTER_HI", "0x204010000"), 16)
SEMSTREAM = os.environ.get("SEM_STREAM_FILE", "/tmp/opencode/sem_stream.txt")
TRACE_EVERY = int(os.environ.get("UVMT_TRACE_EVERY", "5"))
+# The push-buffer resolution walks the channel/pool/manager chain with many
+# QMP reads and can block the poller for tens of seconds. When the GR window
+# stream (pin_gr_stream.py) already supplies the push-buffer pages, skip it so
+# the tracking-sem poller stays live during the client's deadline window.
+SKIP_PB = os.environ.get("SEMFIX_SKIP_PB", "0") == "1"
class Stream:
@@ -309,6 +314,8 @@ def main():
ok = pin(gv, gpa)
log("semfix: sem=0x%x pool va=0x%x -> gpa=0x%x pin=%s"
% (sem, gv, gpa, "ok" if ok else "FAILED"))
+ if SKIP_PB:
+ continue
d = {}
try:
res = resolve_from_sem(q, sem, d)