From 32b551d43781dc7b8ac4587ca2ba1554b0cbd5e5 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Sat, 3 Oct 2026 14:05:38 -0400 Subject: vgsp: host CUDA bridge + sema release recovery; greeneye simulator wave - kernel: gsp_cuda_bridge (QMD launch capture, debugfs job/out/done, param+output resolution cached from inline writes), sema drain fixes (never hold/starve a channel), GR ring autopin + counter autoreg, per-page push reads, counter-derive gate; module builds clean - tools/cuda-bridge: CUDA driver-API daemon executing mail/vadd kernels on the physical GPU, CUBB2/CBD2 v2 protocol, layout check + self-tests - tools/guest-diag + tools/cuda-ab: scanner/pin fixes, victory push-only mode - debug/risc-v-gsp-rm-vm: Phase S simulator wave (GSP device model, boot ladder, triage/re-demo harnesses, docs 30-41), GPGPU-sim scaffolding - STATE-CURRENT/docs updates --- tools/cuda-bridge/client_test.py | 233 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 tools/cuda-bridge/client_test.py (limited to 'tools/cuda-bridge/client_test.py') diff --git a/tools/cuda-bridge/client_test.py b/tools/cuda-bridge/client_test.py new file mode 100644 index 0000000..da0dd4d --- /dev/null +++ b/tools/cuda-bridge/client_test.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# +# client_test.py - external producer/consumer check for the CUBB v1 transport. +# +# Simulates what vgsp.ko will do: write a CUBB job record into the job file, +# wait for the daemon to update the done file with the matching seq, then +# verify the fields. Uses a fake guest-RAM file (never /dev/shm/vgpu-guest-ram). +# +# python3 client_test.py + +import array +import os +import struct +import subprocess +import sys +import time + +HERE = os.path.dirname(os.path.abspath(__file__)) +BIN = os.path.join(HERE, "cuda_bridge") +DIR = os.environ.get("CUBB_TEST_DIR", "/tmp/opencode/cuda-bridge-test") +RAM = os.path.join(DIR, "transport-ram.bin") +JOB = os.path.join(DIR, "transport-job.bin") +DONE = os.path.join(DIR, "transport-done.bin") +LOG = os.path.join(DIR, "transport-daemon.log") + +N = 1 << 20 +MASK = 0xFFFFFFFF + +JOB_FMT = "<4sIQ10I9Q" +DONE_FMT = "<4sIQIIQQII64sQII" +assert struct.calcsize(JOB_FMT) == 128 +assert struct.calcsize(DONE_FMT) == 128 + + +def job_record(seq, opcode, n, mail_out_gpa=0, a_gpa=0, b_gpa=0, c_gpa=0): + buffer_bytes = n * 4 + mail_bytes = n if opcode == 1 else 0 + return struct.pack( + JOB_FMT, + b"CUBB", + 1, + seq, + opcode, + (n + 255) // 256 if opcode != 1 else 1, + 1, + 1, + 256, + 1, + 1, + 0, + n, + 0, + mail_out_gpa, + mail_bytes, + a_gpa, + buffer_bytes, + b_gpa, + buffer_bytes, + c_gpa, + buffer_bytes, + 0, + ) + + +def parse_done(data): + ( + magic, + version, + seq, + status, + bad_count, + checksum_bits, + kernel_ns, + name_len, + opcode, + name, + host_ns, + err_code, + _reserved, + ) = struct.unpack(DONE_FMT, data) + checksum = struct.unpack(" 0 + ) + if not ok: + fails += 1 + print( + "TRANSPORT seq=%d op=%d status=%d bad=%d kernel_ns=%d " + "checksum=%.6f device=\"%s\" file_bad=%d %s" + % ( + d["seq"], + d["opcode"], + d["status"], + d["bad_count"], + d["kernel_ns"], + d["checksum"], + d["device"], + bad, + "PASS" if ok else "FAIL", + ) + ) + + write_job(job_record(1001, 1, len(b"the little gpu man says you've got mail"), + mail_out_gpa=0x40000000)) + data = wait_done(1001) + want = b"the little gpu man says you've got mail" + if data is None: + print("TRANSPORT seq=1001 mail: no done record") + fails += 1 + else: + d = parse_done(data) + with open(RAM, "rb") as fh: + fh.seek(0x40000000) + got = fh.read(len(want)) + ok = d["status"] == 0 and d["bad_count"] == 0 and got == want + if not ok: + fails += 1 + print( + "TRANSPORT seq=%d op=%d status=%d bad=%d mail=\"%s\" %s" + % ( + d["seq"], + d["opcode"], + d["status"], + d["bad_count"], + got.decode(), + "PASS" if ok else "FAIL", + ) + ) + finally: + daemon.terminate() + try: + daemon.wait(timeout=10) + except subprocess.TimeoutExpired: + daemon.kill() + daemon.wait() + + print("TRANSPORT %s" % ("PASS" if fails == 0 else "FAIL")) + return 0 if fails == 0 else 1 + + +if __name__ == "__main__": + sys.exit(main()) -- cgit v1.2.3