summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/guest-diag/guest_pin_stream.py67
-rwxr-xr-xtools/guest-diag/run_cycle.sh14
2 files changed, 79 insertions, 2 deletions
diff --git a/tools/guest-diag/guest_pin_stream.py b/tools/guest-diag/guest_pin_stream.py
index 5f71731..3b7d9b1 100644
--- a/tools/guest-diag/guest_pin_stream.py
+++ b/tools/guest-diag/guest_pin_stream.py
@@ -40,6 +40,17 @@ SPAN = int(os.environ.get("STREAM_SPAN", "0x80000"), 16)
LO2 = int(os.environ.get("STREAM_LO2", "0x120000000"), 16)
HI2 = int(os.environ.get("STREAM_HI2", "0x121400000"), 16)
SPAN2 = int(os.environ.get("STREAM_SPAN2", "0x180000"), 16)
+# UVM system-memory AVMAs: the client's sysmem buffers (the DtoH staging
+# buffer among them) are /dev/zero mappings at high GPU VAs (e.g.
+# 0x79c21c800000). They are not inside the two fixed windows, and only the
+# GMMU page tables can translate them, so stream their present pages as
+# pins too. Restricted to the high VA range and to nvidia/zero mappings;
+# the PAMAX filter rejects device (BAR) PFNs.
+VMA_LO = int(os.environ.get("STREAM_VMA_LO", "0x40000000000"), 16)
+VMA_EVERY = int(os.environ.get("STREAM_VMA_EVERY", "1"))
+VMA_MAX_PAGES = int(os.environ.get("STREAM_VMA_MAX_PAGES", "16384"))
+VMA_PATHS = tuple(p for p in os.environ.get(
+ "STREAM_VMA_PATHS", "zero,nvidia").split(",") if p)
TRACE = ("/sys/kernel/tracing/trace"
if os.path.exists("/sys/kernel/tracing/trace")
else "/sys/kernel/debug/tracing/trace")
@@ -156,6 +167,56 @@ def scan_window(fd, lo, hi, span, out):
base += REGION
+def scan_vmas(fd, pid, out):
+ """Present pages of the client's high-VA nvidia//dev/zero mappings.
+
+ These are UVM system-memory mappings (sysmem buffers, DtoH staging);
+ the fast windows do not cover them and the host cannot resolve them
+ from guest RAM alone. `fd` is the pagemap fd of the client.
+ """
+ n = 0
+ try:
+ maps = open('/proc/%d/maps' % pid)
+ except OSError:
+ return
+ try:
+ for line in maps:
+ parts = line.split()
+ if len(parts) < 6:
+ continue
+ path = parts[5]
+ if not any(p in path for p in VMA_PATHS):
+ continue
+ try:
+ a, b = parts[0].split('-')
+ lo, hi = int(a, 16), int(b, 16)
+ except ValueError:
+ continue
+ if lo < VMA_LO:
+ continue
+ va = (lo + 0xFFF) & ~0xFFF
+ while va < hi and n < VMA_MAX_PAGES:
+ cnt = min(256, (hi - va) // 0x1000)
+ if cnt <= 0:
+ break
+ os.lseek(fd, (va // 4096) * 8, 0)
+ data = os.read(fd, cnt * 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
+ n += got
+ finally:
+ maps.close()
+
+
def scan(pid):
"""Present pages in the first SPAN bytes of every REGION, as {va: pa}.
@@ -171,15 +232,21 @@ def scan(pid):
"""
fd = os.open('/proc/%d/pagemap' % pid, os.O_RDONLY)
out = {}
+ scan.loop += 1
try:
scan_window(fd, LO, HI, SPAN, out)
if HI2 > LO2:
scan_window(fd, LO2, HI2, SPAN2, out)
+ if VMA_EVERY and scan.loop % VMA_EVERY == 0:
+ scan_vmas(fd, pid, out)
finally:
os.close(fd)
return out
+scan.loop = 0
+
+
def main():
with open('/tmp/guest_pin_stream.pid', 'w') as f:
f.write('%d\n' % os.getpid())
diff --git a/tools/guest-diag/run_cycle.sh b/tools/guest-diag/run_cycle.sh
index c3f4ffa..d6ab63f 100755
--- a/tools/guest-diag/run_cycle.sh
+++ b/tools/guest-diag/run_cycle.sh
@@ -28,13 +28,23 @@ for i in $(seq 1 24); do
sleep 5
done
-echo "[*] setting module params (SKIP_OPS=${SKIP_OPS:-Y}, OPS_LOG=${OPS_LOG:-8}, COPY_WATCH=${COPY_WATCH:-0}, COPY_PAT=${COPY_PAT:-0}, CE_GRACE=${CE_GRACE:-8000})"
+echo "[*] setting module params (SKIP_OPS=${SKIP_OPS:-Y}, OPS_LOG=${OPS_LOG:-8}, COPY_WATCH=${COPY_WATCH:-0}, COPY_PAT=${COPY_PAT:-0}, COPY_DST=${COPY_DST:-0}, COPY_DST2=${COPY_DST2:-0}, CE_GRACE=${CE_GRACE:-8000})"
SKIP_OPS="${SKIP_OPS:-Y}"
OPS_LOG="${OPS_LOG:-8}"
COPY_WATCH="${COPY_WATCH:-0}"
COPY_PAT="${COPY_PAT:-0}"
+COPY_DST="${COPY_DST:-0}"
+COPY_DST2="${COPY_DST2:-0}"
CE_GRACE="${CE_GRACE:-8000}"
-sudo sh -c "echo Y > /sys/module/open_iov_vfio/parameters/sema_fake_rel; echo $SKIP_OPS > /sys/module/open_iov_vfio/parameters/sema_skip_ops; echo $OPS_LOG > /sys/module/open_iov_vfio/parameters/sema_ops_log; echo $COPY_WATCH > /sys/module/open_iov_vfio/parameters/sema_copy_watch; echo $COPY_PAT > /sys/module/open_iov_vfio/parameters/sema_copy_pat; echo $CE_GRACE > /sys/module/open_iov_vfio/parameters/sema_ce_grace_ms"
+# The per-push/per-op tracing is behind the master sema_dbg switch (default
+# off, so a normal boot stays quiet). Instrumenting implies enabling it.
+DBG="${DBG:-N}"
+if [ "$DBG" != "Y" ] && { [ "$COPY_WATCH" != "0" ] || [ "$COPY_PAT" != "0" ] || \
+ [ "$COPY_DST" != "0" ] || [ "$COPY_DST2" != "0" ] || [ "$OPS_LOG" -gt 8 ] 2>/dev/null; }; then
+ DBG=Y
+fi
+echo " sema_dbg=$DBG"
+sudo sh -c "echo $DBG > /sys/module/open_iov_vfio/parameters/sema_dbg; echo Y > /sys/module/open_iov_vfio/parameters/sema_fake_rel; echo $SKIP_OPS > /sys/module/open_iov_vfio/parameters/sema_skip_ops; echo $OPS_LOG > /sys/module/open_iov_vfio/parameters/sema_ops_log; echo $COPY_WATCH > /sys/module/open_iov_vfio/parameters/sema_copy_watch; echo $COPY_PAT > /sys/module/open_iov_vfio/parameters/sema_copy_pat; echo $COPY_DST > /sys/module/open_iov_vfio/parameters/sema_copy_dst; echo $COPY_DST2 > /sys/module/open_iov_vfio/parameters/sema_copy_dst2; echo $CE_GRACE > /sys/module/open_iov_vfio/parameters/sema_ce_grace_ms"
echo "[*] pushing guest tools"
timeout 300 "$HERE/push_guest_tools.sh" >/dev/null 2>&1 || { echo "push failed"; exit 1; }