summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-18 00:56:45 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-18 00:56:45 -0400
commit23e38b1235071c784c453ed947a615fd2cc1bfbd (patch)
tree610e95e89af6fcc438bd55c139bd4fc6e78e8dbe /tools/guest-diag
parent5de33b14586b8669ebba9150b54c7ea182859214 (diff)
downloadopen-iov-23e38b1235071c784c453ed947a615fd2cc1bfbd.tar.gz
open-iov-23e38b1235071c784c453ed947a615fd2cc1bfbd.zip
tools: prompt re-assert after recv chunks, heartbeat inside long QMP loops, novel-only activity accounting (tools-fix a4840952)
Diffstat (limited to 'tools/guest-diag')
-rw-r--r--tools/guest-diag/pin_gr_stream.py22
-rwxr-xr-xtools/guest-diag/semfix_uvmt.py10
2 files changed, 29 insertions, 3 deletions
diff --git a/tools/guest-diag/pin_gr_stream.py b/tools/guest-diag/pin_gr_stream.py
index 2f78f2a..6ba51ee 100644
--- a/tools/guest-diag/pin_gr_stream.py
+++ b/tools/guest-diag/pin_gr_stream.py
@@ -328,10 +328,11 @@ def main():
chunk = conn.recv(1 << 20)
except socket.timeout:
if runs:
+ novel = _novel_pages(runs, seen)
pages = _flush(runs, seen)
pin_total += pages
pins_since_client += pages
- if pages:
+ if novel:
_activity()
runs = []
_maybe_reassert_request(seen)
@@ -433,12 +434,18 @@ def main():
int(m.group(2), 16),
int(m.group(3)) if m.group(3) else 1))
if runs:
+ novel = _novel_pages(runs, seen)
pages = _flush(runs, seen)
pin_total += pages
pins_since_client += pages
- if pages:
+ if novel:
_activity()
runs = []
+ # Poll the semfix re-assert request on every recv cycle, not
+ # only on recv-timeouts: sustained scanner traffic starves the
+ # timeout branch, so the request fired post-mortem in r203
+ # (119 s late, `seen` already cleared by #nopid).
+ _maybe_reassert_request(seen)
except Exception as e:
# A transient error on one connection must not kill the process:
# log it, drop the connection and go back to accept().
@@ -482,6 +489,17 @@ def _coalesce_seen(seen):
return runs
+def _novel_pages(runs, seen):
+ """Count pages in runs whose translation is new or changed.
+
+ Progress accounting for the stall watchdog: the guest re-streams the CE
+ window and UVM pool every couple of seconds even when nothing moves, so a
+ pure repeat of an already-known pair must not count as pin activity
+ (r203: those repeats masked the watchdog through every hang)."""
+ return sum(1 for va, pa, n in runs for i in range(n)
+ if seen.get(va + i * 0x1000) != pa + i * 0x1000)
+
+
def _flush(runs, seen, full=False):
"""Inject run lines (one batched sudo pass), return number of pages pinned.
diff --git a/tools/guest-diag/semfix_uvmt.py b/tools/guest-diag/semfix_uvmt.py
index f28efd3..11c191c 100755
--- a/tools/guest-diag/semfix_uvmt.py
+++ b/tools/guest-diag/semfix_uvmt.py
@@ -671,7 +671,12 @@ def main():
for sem in sorted(sems - known_sems):
known_sems.add(sem)
- for sem in sorted(known_sems):
+ for hb_i, sem in enumerate(sorted(known_sems)):
+ if hb_i % 16 == 0:
+ # A full pass can run 60-90 s (thousands of QMP reads); keep
+ # the heartbeat fresh inside it (r203: 79 s gap -> supervisor
+ # restarted the tool mid-pass).
+ _hb()
now = time.time()
if now < skip_until.get(sem, 0):
continue
@@ -793,6 +798,7 @@ def main():
qmp_next_try = now + 5.0
for ch in sorted(chans - seen_ch):
+ _hb() # a resolve can block for tens of seconds
d = {}
try:
res = resolve_from_channel(q, ch, d)
@@ -807,6 +813,7 @@ def main():
else:
log("semfix: ch=0x%x pushbuf miss [%s]" % (ch, diag_str(d)))
for pb in sorted(pbs - seen_pb):
+ _hb() # a resolve can block for tens of seconds
d = {}
try:
res = resolve_from_pushbuffer(q, pb, d)
@@ -833,6 +840,7 @@ def main():
if loop % 8 == 0:
for st in list(pinned_ranges.values()):
if not st["done"] and st["tries"] > 0:
+ _hb() # pin_range_once can walk thousands of QMP reads
pin_range_once(*st["args"])
# Dead-pid detection and the pre-capture stall watchdog. The guest is