fix(linux): warn when permissions block udmabuf imports #2111
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2111
Loading…
Reference in a new issue
No description provided.
Delete branch "udmabuf-warning"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Linux users can lose much of their frame rate when device permissions prevent Hookshot from using
/dev/udmabuf, without being told how to fix it. On my RX 7900 XT, a matched Kokiri Forest test measured 16.1–16.2 game fps with host-pointer imports versus 36.1 with DMA buffers.What changed
EACCESorEPERM, with the error, slower-import consequence and group/udev access advice. Other open failures keep the existing logging.hookshot_log.txt; writing another line to the redirected stderr alone would not reach the original caller. With unredirected stderr, the warning is logged only once.uaccess, or a dedicated group for service/SSH users. No world-writable device, root game process or automatic permission change.Related: #1000; follows the DMA-import support merged in #448. This does not fix every Linux performance issue or close the tracking issue.
Evidence and tests
Base: upstream main
215ab3dd6536cb21fc67c2e89c31c10a18df555c.Historical hardware evidence: RX 7900 XT / RADV NAVI31, Mesa 26.2.3, Linux 7.2.7, Ryzen 5 7600X. The permission-denied device was
root:kvm 0660. With mesh draws disabled in both cases, the matched DMA run reached 36.1 game fps; reverting toHOS_GUEST_MEMFD=0with the same access grant returned to 16.2. Queue-submit time was 29.10–30.15 ms without DMA and 0.49 ms with it. These are earlier diagnostic runs, not a benchmark of this warning patch or generated-display fps.Regression demonstrated before the change: no warning on stderr.
python -I -B tests/run_all.py udmabuf_warning low_memory_notice: 2 passed. The new test checks EACCES/EPERM, ENOENT/EIO, success, repeated probes, and redirected/unredirected stderr.Compiled and ran the actual warning block plus the production logger against a real mode-000 file: one warning on original stderr and one in the redirected log. No game assets needed.
C fixture: GCC,
-std=c11 -Wall -Wextra -Werror; Ruff andgit diff --check: passed.Full game-free suite:
python -I -B tests/run_all.py --jobs 6 --timeout 1200— 354 passed, 2 failed, 12 skipped, 520 s on Linux.tests/test_finish_output.pyandtests/test_linux_update.pyfail withNameError: name 'a' is not definedin the extractednx2/make_game.pyfinish block; both failures reproduce on untouched upstream at the base SHA above. Skips cover game/GPU/Windows-only checks and unavailable dotnet, PIL and numpy. Earlier runs exposed an unrelatedtests/test_make_game_ngx.pyfailure, reproduced on untouched upstream; upstream fixed it in #2068, and this branch was rebased onto that fix before the earlier passing full run.Whole-file
host/hos_main.cClang syntax/type check: passed, using the project's game-free runtime-header generators.Not tested: the patched full game, Windows runtime/build, other GPUs, or applying either persistent udev policy. No new hardware benchmark was run, so no new tested-configurations row is claimed.
Screenshots
Before/after terminal captures of the game-free EACCES fixture, using the same warning code and real logger (not game screenshots):
Before: no output on stderr. After (once, also in
hookshot_log.txt):Both probes run twice; the new stderr warning appears once.
Note:
tests/test_finish_output.pyandtests/test_linux_update.pyalso fail on untouched currentmain(NameError: name 'a' is not defined); they are unrelated to this change.Thanks, this is a useful one: a silent 2x on AMD is exactly the kind of thing people never find on their own.
I checked it against how the renderer actually gets the device:
import_dma_bufingpu/vk/vk_memory.cppopens/dev/udmabufwith the sameO_RDWR | O_CLOEXEC, and it only gets that far whenhos_guest_memfdis set, which is gated by this exact probe inreserve_guest. So the probe is the right place, and the warning fires for the right reason.No false warnings that I could find:
ENOENTand stays on the existing log line only. Same for a Flatpak-style sandbox without the device node (ENOENT), and macOS/other POSIX builds where the node does not exist.A64_MEM_ZERO) compile all of it out: the declaration, the probe and thefcntldup are all under!defined(_WIN32) && !defined(A64_MEM_ZERO), andfcntl.h/unistd.hare already included on that path.The stderr dup also checks out: the redirect only happens when fd 2 is /dev/null, a socket (the journal when started from the desktop entry) or closed (
GetFileTypeinhost/posix/win32_posix.c), so the extra copy lands in the journal for a desktop launch, is harmless for /dev/null, andfcntljust returns -1 if fd 2 is closed. With a terminal there is no dup and the line appears once. The dup is closed on the first probe, so no fd leaks into the game.Readme advice is safe: group with the
kvmcaveat, a device-scopeduaccessrule (70- sorts before 73-seat-late, as it must), a dedicated group for SSH/service users, and an explicit no to root and world-writable. The rule syntax and theudevadm trigger --action=add /sys/class/misc/udmabufline are right.Test is declared the way
run_all.pywants (RUN_ALL_ARGS = ["--cc", "gcc"], so a PC without gcc skips it;RUN_ALL_SKIPfor Windows) and covers the cases that matter.Nits, none blocking:
HOS_GUEST_MEMFD=0(inline).close(startup_stderr)sits inside the udmabuf probe block. Fine since this is its only user, but a short comment saying the dup exists only for this probe would save the next person a search.udmabufloaded withmodprobedoes not survive a reboot. Mentioning/etc/modules-load.d/udmabuf.confwould save a repeat visit.CI: only the title check has run so far. The source-regression, AppImage and Windows workflows are waiting for a maintainer to approve them (first-time contributor).
@ -163,0 +169,4 @@const char *warning = "WARNING: /dev/udmabuf permission denied; GPU imports may be slower, especially on AMD/RADV. ""Ask your administrator for access via its device group or a device-specific udev rule ""(see README, On Linux). Do not run the game as root.";if (startup_stderr >= 0) dprintf(startup_stderr, "%s (%s)\n", warning, strerror(err));Nit: with
HOS_GUEST_MEMFD=0the user has already chosen anonymous memory, so a warning about slower imports is just noise there (the existing log line below is already skipped wheneis set). Something like&& !(e && !atoi(e))would keep it for the default case and for=1, where it is still true.github-sync referenced this pull request2026-11-12 06:22:54 +00:00