Skip to content

fix(linux): don't try dma-buf imports on NVIDIA's driver - #2340

Merged
hckrman101 merged 2 commits into
NotRexed:mainfrom
mooki000:fix/linux-nvidia-dmabuf-type
Nov 11, 2026
Merged

hckrman101 merged 2 commits into
NotRexed:mainfrom
mooki000:fix/linux-nvidia-dmabuf-type

Conversation

@mooki000

@mooki000 mooki000 commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

What this is for: a clean start on Linux with NVIDIA's driver. On Linux with NVIDIA's driver, the game asks the driver to import every block of guest memory as a udmabuf dma-buf, the driver refuses every one, and each block ends up a host-pointer import anyway. The attempts leave a kernel line at every start that reads like a driver fault (NVRM: memMap_IMPL: CPU mapping not supported for addressSpace: 0x1). This change stops asking on NVIDIA's driver. Fixes #2339. What is measured: on one machine (RTX 4090, NVIDIA 615.78.08, Kokiri Forest): the refused imports and the kernel line are gone, and the frame rate is unchanged within the spread of identical runs (80.4 and 81.3 fps before, 79.6 and 81.5 after). Not measured: any other NVIDIA card or driver version, any other area, and whether the refusals ever cost a visible hitch in play. So this is a cleanup of the log and the start, not a speed-up.

Problem

  1. On Linux the guest's memory window is a memfd, so that the renderer can hand pieces of it to the GPU as dma-bufs through /dev/udmabuf (perf(linux): hand guest memory to the GPU as udmabuf dma-bufs, not host pointers #448). The readme gives the reason: host-pointer imports can be much slower on AMD with RADV.
  2. For each new pool, import_dma_buf (gpu/vk/vk_memory.cpp) makes a udmabuf and dma_buf_memory asks the driver which memory types it allows for it. NVIDIA's driver answers with two (allowed bits 0x9): type 0 with no flags, and type 3, host visible, coherent and cached. The code takes the first host-visible and coherent one: type 3.
  3. NVIDIA refuses that import with VK_ERROR_OUT_OF_DEVICE_MEMORY (vk: guest ...: its dma-buf not imported (VkResult -2)), for every pool, and a pool made of several pieces stays unbound (its 0 dma-bufs not bound (VkResult -13)).
  4. Each refused pool then goes through the host-pointer import, which works. So on NVIDIA the dma-buf route has never carried a pool: it only adds a failed attempt in front of each one. With one log line per attempt, a 90-second run made 49 to 50 attempts.
  5. The first attempt of a run is slow (2.5 to 3.7 ms in three runs) and the kernel prints the memMap_IMPL line 0.17 to 0.6 ms into it. Later refusals take microseconds and usually print nothing, but 2 of my 18 measured runs of main printed the line three times. One later refusal, of a 64 MB piece during a save's load, took 198 ms on the presentation thread. I saw that once and can't say how often it happens.

What changed

  • gpu/vk/vk_memory.cpp, import_dma_buf: /dev/udmabuf is not opened when dev.driver_id is VK_DRIVER_ID_NVIDIA_PROPRIETARY, so no dma-buf is made or offered and every pool takes the host-pointer import from the start, as it already did after each refusal. The start-up line says why: vk: guest memory as dma-bufs: no (NVIDIA's driver refuses them: host pointers). Other drivers take the same path as before: the driver check is the only new condition.
  • readme.md: the udmabuf paragraph says that NVIDIA's driver refuses these dma-bufs and the game uses host-pointer imports there.
  • tests/import_buffer_binding_test.cpp: the fixture's device gets a driver_id, its open counts calls, and a new nvidia case checks the dma route (no open, no buffer, no allocation, no memfd kept) and the pool route (one host-pointer import).
  • tests/test_import_buffer_binding.py: runs the new case on both routes (37 cases, was 34).

Why skip the attempt instead of picking the other memory type. The driver does accept type 0: with an experiment that forced it, all 60 imports of a run succeeded and no kernel line appeared. But Kokiri Forest ran 2.5 % slower that way (79.17 and 78.53 fps against 80.63 and 81.13 with host pointers, taken in turn; identical runs differed by 0.6 to 0.8 %). I also don't know whether the GPU sees the CPU's writes coherently through an import that isn't host visible. So host pointers stay.

How it was tested

Environment: RTX 4090, NVIDIA 615.78.08 with its open kernel module, i9-13900K, 96 GB, Arch Linux (kernel 7.2.15), headless (HOS_HEADLESS=1, no window), a build from source with clang 23.

The test. tests/test_import_buffer_binding.py passes with the change (g++ and clang++). The new fixture against main's vk_memory.cpp fails: fixture-0.exe dma nvidia aborts on its assert.

All checks. python -I -B tests/run_all.py on the branch: 361 passed, 3 failed, 10 skipped. The three failures are this machine's and don't touch the change: tests/test_updater_asset_digest.py needs the .NET 8 SDK (only .NET 10 is here), tests/test_wayland_libdecor_hint.py trips a clang 23 warning under -Werror, and nx2/test_unsigned_image_types.py failed to compile while the load average was 120 to 370 and passes when run alone. That run was on the commit before a last edit that changed only a code comment; after the edit I reran test_import_buffer_binding.py alone (passes).

In the game. main bdfe09c with this commit against bdfe09c, the two renderers built the same way. The user's save in Kokiri Forest, 13 camera turns of 4 s, uncapped (HOS_FPS_MAX=240), output 2048x1152, Render resolution Auto, no frame generation. Four runs of 90 s in turn (base, fix, base, fix) with nothing else on the card and no build running. The frame times are the game's own present-to-present intervals (HOS_PRESENT_TRACE) over the 30 s from 15 s after the save's load.

The log and the kernel, per run:

q18-base-a: guest memory as dma-bufs: yes, refused imports 7, memMap lines 1, other NVRM 0
q18-base-b: guest memory as dma-bufs: yes, refused imports 7, memMap lines 1, other NVRM 0
q18-fix-a: guest memory as dma-bufs: no (NVIDIA's driver refuses them: host pointers), refused imports 0, memMap lines 0, other NVRM 0
q18-fix-b: guest memory as dma-bufs: no (NVIDIA's driver refuses them: host pointers), refused imports 0, memMap lines 0, other NVRM 0

("refused imports 7" is what the log shows before its own limit of 8 such lines; the count with a line per attempt is the 49 to 50 above.)

The frames, per run:

run build frames mean fps 1% low p99 ms over 16.7 ms over 20 ms over 25 ms longest ms replay memory sync ms load average, start -> end
base a bdfe09c 2437 81.28 46.48 19.4 230 17 3 27.6 0.76 5.07 -> 5.21
fix a bdfe09c + this 2444 81.53 46.84 19.3 216 17 1 28.2 0.65 5.21 -> 4.35
base b bdfe09c 2409 80.36 45.01 19.9 268 24 2 29.3 0.81 4.35 -> 5.89
fix b bdfe09c + this 2388 79.62 46.34 19.4 201 16 3 28.1 0.64 5.89 -> 5.69
  • Fix against base: +0.3 % in the first pair and -0.9 % in the second. The two base runs differ by 1.1 % and the two fix runs by 2.3 %. No difference beyond the spread, in the mean, the 1% low or the slow frames.
  • The replay's memory sync is 0.11 to 0.17 ms a frame shorter with the fix in both pairs. It is too small to show in the frame rate.
  • "fix b" ended while three other programs on this machine were each using a full core. It is the slowest of the four and I kept it in.
  • I looked at three pictures of each run: Kokiri Forest in play, drawn the same with and without the change.

Added 2026-11-11 15:56 UTC: a second driver, run by @CIB (his comment below). RTX 4090, NVIDIA 590.48.01 (open kernel module), Arch Linux, kernel 6.19.8; only the vk_memory.cpp hunk, on main 8c92fe3 in a tree with unrelated local scheduler work. Stock, three starts: each logs the refused imports (7 lines on a first start, 1 on the next two) and adds one memMap_IMPL line to the kernel log. With the hunk, two starts: neither line, and the new vk: guest memory as dma-bufs: no (NVIDIA's driver refuses them: host pointers). Frame times were not measured there.

Risks

  • An NVIDIA driver that would accept the import no longer gets the chance. I know of none: 615.78.08 refuses it, and so does 590.48.01 (CIB's run). Such a driver would run on host pointers, as every NVIDIA run does today.
  • The check is by driver, not by card. VK_DRIVER_ID_NVIDIA_PROPRIETARY matched here with the open kernel module. Mesa's NVK reports a different driver id and keeps trying dma-bufs as before; I have not run it.
  • AMD and Intel take the unchanged path. I did not run them with this branch.
  • RX_VK_NO_DMABUF=1 and HOS_GUEST_MEMFD=0 work as before.

Not tested

Other NVIDIA cards, driver versions other than 615.78.08 (mine) and 590.48.01 (CIB's short starts), NVIDIA's closed kernel module, NVK, AMD, Intel, a run with a window, areas other than Kokiri Forest, the Steam Deck. Windows doesn't reach this code.

Other open pull requests

Twelve open pull requests touch gpu/vk/vk_memory.cpp. None changes these lines. #2042 changes the line two above (getenv("RX_VK_NO_DMABUF") to env_on(...)) and the same two test files; git merges the two without a conflict, and the merged function has both changes.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com

NVIDIA's driver refuses the guest window's udmabuf dma-bufs as host-visible memory, so every pool fell back to a
host-pointer import anyway, and its kernel module logged "NVRM: memMap_IMPL: CPU mapping not supported" at each start.
The attempt is no longer made there; other drivers are unchanged.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CIB

CIB commented Nov 11, 2026

Copy link
Copy Markdown

Independent check on a second NVIDIA driver (590.48.01)

I applied only the gpu/vk/vk_memory.cpp hunk on top of main 8c92fe3 (a local tree with unrelated scheduler work) and ran it on a different machine and driver from yours:

  • RTX 4090, NVIDIA 590.48.01 (open kernel module), Arch Linux, kernel 6.19.8, Ryzen 7 5700X3D, 125 GB RAM, Vulkan 1.4, hookshot_vk.so built from source.

Stock, three starts: every start logged its dma-buf not imported (VkResult -2) (7 lines on a first start, 1 on the next two), and every start added exactly one NVRM: memMap_IMPL: CPU mapping not supported for addressSpace: 0x1 to the kernel log. The pools fell back to host pointers and the game reached its first decoder.

With your hunk, two starts: zero VkResult -2 lines, zero new memMap_IMPL lines, and the new line vk: guest memory as dma-bufs: no (NVIDIA's driver refuses them: host pointers). The renderer started and the game reached the first decoder in both.

So the diagnosis and the gate hold on a driver you did not test, and the kernel line is gone here too. I did not measure frame times (my runs were short boots), so I can't add to your numbers there.

One note: RX_VK_NO_DMABUF=1 also removes the kernel line, but that arm hit an unrelated guest 0xfffff00000000000 sentinel crash at boot, so I would not use it as the validation arm.


🤖 Generated with opencode using DeepSeek V4.1 Flash.

@mooki000

Copy link
Copy Markdown
Contributor Author

Thanks. A second driver was the first thing on my not-tested list, and your run covers it: 590.48.01 refuses the import the same way, and the hunk removes both lines there too. I've added your result to the description.

On RX_VK_NO_DMABUF=1: here that arm ran without the crash you saw. On main bdfe09c it ran 35 s at the title (1,680 presents, no FATAL or CRASH line; one run), and HOS_GUEST_MEMFD=0 ran three minutes into Kokiri Forest at 60 fps. So the 0xfffff00000000000 crash doesn't come from the switch alone, at least on my tree and driver. If you open an issue for it with the log lines around the crash, tag me: that arm is one I can run here.

@ghost ghost left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. The gate is narrow and in the right place: dev.driver_id is filled in vk_main.cpp before any pool is imported, /dev/udmabuf simply isn't opened on VK_DRIVER_ID_NVIDIA_PROPRIETARY, dmabuf_memfd stays -1, and every pool goes straight to the host-pointer import it already ended up on after each refusal. Other drivers (NVK included, which has its own driver id) take exactly the old path, and the new log reason sits after the existing ones so their messages are unchanged. The fixture covers both routes (no open, no buffer/allocation on the dma route; one host-pointer import on the pool route), and the choice to skip rather than force memory type 0 is well argued with the 2.5 % numbers and the coherency doubt.

@CIB's run on 590.48.01 confirms the same refusal and the same fix on a second driver, so the main "not tested" item is covered. His RX_VK_NO_DMABUF=1 crash is separate from this change (you couldn't reproduce it; an issue with logs would be the place for it).

One nit on consistency with #2111 (inline): the startup warning from reserve_guest in host/hos_main.c runs before the Vulkan device exists, so it can't know the driver and will still tell NVIDIA users without access to ask an admin for /dev/udmabuf, a device the renderer now never uses on NVIDIA. Not worth plumbing the driver into that early probe, but the readme could say NVIDIA users can ignore it.

CI: only the title check has run; the build/test workflows are waiting for a maintainer to approve them (action_required), so they need to go green before merge.

Comment thread readme.md
For fast GPU memory imports on Linux, the game user needs read/write access to `/dev/udmabuf` (the kernel's
`udmabuf` module). Without it, Hookshot falls back to host-pointer imports, which can be much slower on AMD/RADV;
`udmabuf` module). Without it, Hookshot falls back to host-pointer imports, which can be much slower on AMD/RADV
(NVIDIA's driver refuses these dma-bufs, so on NVIDIA the game uses host-pointer imports, which are not slow there);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: since #2111 the startup probe in host/hos_main.c still prints the /dev/udmabuf permission denied warning on NVIDIA (it runs before the Vulkan device, so it can't know the driver). Maybe add here that NVIDIA users don't need the access and can ignore that warning, so nobody adds themselves to kvm or writes a udev rule for nothing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 6af3759: the paragraph now says that with an NVIDIA card on NVIDIA's own driver you don't need the access and can ignore that warning, and why it still appears (the check runs at the start, before the game knows which GPU it has). I left the early probe itself alone, as you suggest.

The start-up check for /dev/udmabuf (NotRexed#2111) runs before the game knows which
GPU it has, so it still warns on NVIDIA's driver, where the renderer no longer
opens the device at all. The readme now says that the warning can be ignored
there, so nobody joins a device group or writes a udev rule for nothing.
(Review of NotRexed#2340.)

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux, NVIDIA's driver: every start logs NVRM: memMap_IMPL: CPU mapping not supported, because the driver refuses the game's dma-buf imports

2 participants