Fixing RTX 3090 VFIO Passthrough Crashes on Unraid: QEMU, IOMMU and Physical Address Width
I recently ran into a particularly awkward GPU passthrough problem on my Unraid server. The machine is based on an AMD Threadripper 3960X with 128 GB RAM and an NVIDIA RTX 3090, with the 3090 passed through to a Windows 11 VM.
GPU passthrough initially appeared to work. Windows booted, the NVIDIA driver loaded, the desktop was stable, and even 4K video playback worked normally. However, as soon as the GPU was placed under substantial 3D load, it disappeared from the host and the VM failed.
Symptoms
Running Unigine Superposition at 1080p Medium caused the display to go black almost immediately. The Unraid/QEMU logs then contained errors along these lines:
Afterwards the GPU would often be inaccessible to VFIO, with failed D0/D3 power transitions,
failed resets and PCIe link recovery attempts. A complete host power cycle was sometimes required
before the card could be passed through again.
Eliminating the Obvious Causes
This initially looked like a GPU reset, power-management or PCIe stability problem. I therefore worked through the likely causes one at a time.
- Reduced the VM from 64 GB to 32 GB RAM.
- Removed hugepages from the test VM.
- Removed additional passed-through/virtual disks.
- Created an entirely new Windows 11 VM with fresh OVMF/NVRAM.
- Installed Windows and NVIDIA drivers from scratch.
- Reseated the RTX 3090 and checked its power connections.
- Forced its PCIe slot from Gen4 to Gen3.
None of these fixed the problem.
The particularly useful test was booting Windows 10 directly on the server hardware, without Unraid, KVM or VFIO involved. The same RTX 3090 then completed the same Superposition benchmark at 100% GPU utilisation without difficulty.
That strongly suggested that the GPU, motherboard slot, PSU and basic PCIe signalling were sound, and that the fault was somewhere in the VFIO/QEMU path.
The Important Clue: BAR1
Inspecting QEMU's live PCI map finally exposed the interesting detail. The RTX 3090's 256 MB BAR1 had been assigned this guest address:
That address was particularly significant because it exactly matched the address in the VFIO failure:
The size is significant too: 0x10000000 is 256 MB, exactly the size of BAR1.
The Threadripper 3960X host reports:
In other words, QEMU was placing the GPU's 64-bit MMIO BAR at an extremely high guest address, and VFIO was then failing when it attempted to map that BAR through the host IOMMU.
The Fix
Libvirt/QEMU allows the physical address width exposed to the guest to be constrained. For this machine I added the following line to the VM's CPU definition:
The resulting CPU section looked like this:
Libvirt translated this into the corresponding QEMU CPU options:
What Changed?
After rebooting the VM, the RTX 3090's BAR1 had moved dramatically:
The new address is below the 43-bit physical-address ceiling.
Result
I reran exactly the workload which had previously caused the GPU to disappear almost immediately: Unigine Superposition, DirectX, 1080p Medium.
This time the RTX 3090 reached 100% utilisation, completed the entire benchmark and remained fully operational:
The Unraid kernel log remained clean: no vfio_container_dma_map failure, no
Bad address, no failed D3 power transition and no PCIe link recovery.
I also shut the VM down normally and restarted it. The RTX 3090 reset, detached and was successfully reassigned to the VM again.
Conclusion
In this case the apparent NVIDIA/VFIO reset problem was actually an address-space problem. The D3 errors and PCIe recovery messages were consequences of the failed mapping rather than the original fault.
The giveaway was correlating the failing VFIO DMA-map address with QEMU's live PCI map. The failed address and size matched the RTX 3090's BAR1 exactly.
Constraining the guest physical-address width to the host's 43-bit physical addressing caused QEMU/OVMF to place the GPU BAR inside a usable address range and eliminated the failure.
Important: 43 bits is appropriate to this particular system because that is the physical address width reported by its CPU and proved successful in testing. It should not simply be copied to unrelated hardware. Determine the appropriate CPU/IOMMU address width for the host in question.
Related Reading / Similar Reports
The following QEMU and VFIO discussions were particularly useful. Several show strikingly similar
high-address PCI BAR mappings and vfio_container_dma_map(...)= -14 (Bad address) errors.
- QEMU-devel: OVMF/VFIO physical-address-width issue and host-phys-bits-limit recommendation
- QEMU-devel: VFIO BAR mapping at 0x380000000000 returning -14 (Bad address)
- QEMU-devel: Further discussion of high BAR addresses, D3hot and VFIO DMA mapping
- QEMU-devel: IOMMU_IOAS_MAP / VFIO PCI BAR mapping discussion
- Kata Containers: NVIDIA GPU VFIO passthrough and BAR mapping failure
- Kata Containers: IOMMU_IOAS_MAP Bad address / vfio_container_dma_map -14 with GPUs
Hardware/software involved in this particular case: AMD Ryzen Threadripper 3960X, ASUS ROG Zenith II Extreme Alpha, EVGA GeForce RTX 3090 24 GB, Unraid 7.3.1, Linux 6.18.33-Unraid, QEMU 10.2.2 and libvirt 12.2.0.
No comments:
Post a Comment