summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/fenceword_assist.py
blob: 9e207e2a005a07a19fc06de1c0068bf83f10a2ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-3-Clause
# fenceword_assist.py - keep the Vulkan fence wait word of a live vk_frame120
# client released (session 125).
#
# SUPERSEDED (SESSION 126, commit 95e7f57e): the service now authors these
# releases itself. The `sema_gr_ahead` fence fold sweeps the channel's
# pre-built ring continuation beyond GP_PUT through the ring's validated
# GPFIFO translation, decodes the INC-5 RELEASE groups it finds (addr 0x5c
# count>=5) and queues them in the standard pending ledger; r717 evidence:
# `sema-ahead: h=0xbeec0100 put=11 swept=10 rels=4` followed 30 ms later by
# `sema-svc: deferred release va=0x45ba090 payload=1 (was cur=0)`, with
# VKFRAME seq/DONE and NO assist running (see
# backups/evidence-20261002-victory/r717-fencefold/). Keep this tool only as
# the A/B fallback for boots that predate the fold (or with
# sema_gr_ahead=0).
#
# The client's fence wait polls a word in a 16 KB /dev/nvidiactl BO mapped
# read-write into the client: the init wait word lives at +0x80 (the t122
# "BW" chaser writes it once) and the per-submit fence word at +0x90 of the
# SAME BO (GPU VA <base>+0x90, delivered by the submit push's INC-5 semaphore
# group). While the emulation does not apply that group, this host assist
# writes 1 to the +0x90 slot whenever it reads 0 - the direct predecessor of
# the service-side fold (see Handoff SESSION 125/126).
#
# Usage (host, while a t122-driven client runs):
#   tools/guest-diag/fenceword_assist.py [seconds] [guest-pid]
import os
import subprocess
import sys
import time

VMEXEC = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                       "..", "..", "scripts", "vgpu_vm.py"))
PY = r'''
import struct, sys, time
pids = [p for p in sys.argv[1:] if p]
maps_of = {}
def vma(pid, addr):
    for line in open("/proc/%d/maps" % pid):
        rng, perms = line.split(None, 2)[:2]
        a, b = [int(x, 16) for x in rng.split("-")]
        if a <= addr < b:
            return line.strip()
    return None
found = {}
for pid in pids:
    try:
        maps = [l for l in open("/proc/%d/maps" % pid) if "nvidia" in l and "rw-s" in l]
    except OSError:
        continue
    for l in maps:
        rng, perms, off = l.split(None, 3)[:3]
        a, b = [int(x, 16) for x in rng.split("-")]
        if b - a != 0x4000:
            continue
        try:
            mem = open("/proc/%d/mem" % pid, "r+b", 0)
            mem.seek(a + 0x80); init = mem.read(4)
            mem.seek(a + 0x90); fw = mem.read(4)
        except Exception:
            continue
        if init == b"\x01\x00\x00\x00" and fw == b"\x00\x00\x00\x00":
            found[pid] = (a + 0x90, mem)
            print("found pid=%s word=%#x" % (pid, a + 0x90), flush=True)
end = time.time() + float(sys.argv[1])
while time.time() < end:
    for pid, (w, mem) in list(found.items()):
        try:
            mem.seek(w)
            if mem.read(4) == b"\x00\x00\x00\x00":
                mem.seek(w); mem.write(struct.pack("<I", 1)); mem.flush()
                print("released pid=%s word=%#x" % (pid, w), flush=True)
        except Exception:
            del found[pid]
    time.sleep(0.5)
print("assist done", flush=True)
'''


def main():
    dur = sys.argv[1] if len(sys.argv) > 1 else "300"
    pid = sys.argv[2] if len(sys.argv) > 2 else ""
    b64 = __import__("base64").b64encode(PY.encode()).decode()
    subprocess.run([VMEXEC, "exec", "--timeout", str(int(dur) + 60),
                    "echo %s | base64 -d > /tmp/fwa.py; sudo python3 /tmp/fwa.py %s %s"
                    % (b64, dur, pid)])


if __name__ == "__main__":
    main()