GPU passthrough with libvirt qemu kvm

From Gentoo Wiki
Jump to: navigation, search

GPU passthrough is a technology that allows you to directly present an internal PCI GPU to a virtual machine. The device acts as if it were directly driven by the VM, and the VM detects the PCI device as if it were physically connected. GPU passthrough is also often known as IOMMU, although this is a bit of a misnomer, since the IOMMU is the hardware technology that provides this feature but also provides other features such as some protection from DMA attacks or ability to address 64-bit memory spaces with 32-bit addresses.

As you can imagine, the most common application for GPU passthrough at least gaming, since GPU passthrough allows a VM direct access to your graphics card with the end result of being able to play games with nearly the same performance as if you were running your game directly on your computer.

QEMU (Quick EMUlator) is a generic, open source hardware emulator and virtualization suite.

Note
This article typically uses KVM as the accelerator of choice due to its GPL licensing and availability. Without KVM nearly all commands described here will still work (unless KVM specific).

Installation

BIOS and UEFI firmware

In order to utilize KVM either VT-x or AMD-V must be supported by the processor. VT-x or AMD-V are Intel and AMD's respective technologies for permitting multiple operating systems to concurrently execute operations on the processors.

To inspect hardware for visualization support issue the following command:

user $grep --color -E "vmx|svm" /proc/cpuinfo

For a period manufacturers were shipping with virtualization turned off by default in the system BIOS

Hardware

  • A CPU that supports Intel VT-d or AMD-Vi. Check List of compatible Intel CPUs (Intel VT-x and Intel VT-d)
  • A motherboard that supports the aforementioned technologies. To find this out, check in your motherboard's BIOS configuration for an option to enable IOMMU or something similar. Chances are that your motherboard will support it if it's from 2013 or newer, but make sure to check since this is a niche technology and some manufacturers may save costs by axing it from their motherboards or delivering a defective implementation (such as Gigabyte's 2015-2016 series) simply because NORPs never use it.
  • At least two GPUs: one for your physical OS, another for your VM. (You can in theory run your computer headless through SSH or a serial console, but it might not work and you risk locking yourself away from your computer if you do so).
  • Optional but recommended: Additional monitor, keyboard and mouse

EFI configuration

Go into BIOS (EFI) settings and turn on VT-d and IOMMU support.

Note
VT-d and Virtualization configuration params are same
Note
Some EFI doesn't have IOMMU configuration settings

IOMMU

IOMMU – or input–output memory management unit – is a memory management unit (MMU) that connects a direct-memory-access–capable (DMA-capable) I/O bus to the main memory. The IOMMU maps a device-visible virtual address ( I/O virtual address or IOVA) to a physical memory address. In other words, it translates the IOVA into a real physical address.

In an ideal world, every device has its own IOVA address space and no two devices share the same IOVA. But in practice this is often not the case. Moreover, the PCI-Express (PCIe) specifications allow PCIe devices to communicate with each other directly, called peer-to-peer transactions, thereby escaping the IOMMU.

That is where PCI Access Control Services (ACS) are called to the rescue. ACS is able to tell whether or not these peer-to-peer transactions are possible between any two or more devices, and can disable them. ACS features are implemented within the CPU and the chipset.

Unfortunately the implementation of ACS varies greatly between different CPU or chip-set models.

IOMMU kernel configuration

To enable IOMMU support in kernel:

KERNEL
Device Drivers --->
  [*] IOMMU Hardware Support --->
            Generic IOMMU Pagetable Support ----
      [*]   AMD IOMMU support
      <*>     AMD IOMMU Version 2 driver
      [*]   Support for Intel IOMMU using DMA Remapping Devices
      [*]     Support for Shared Virtual Memory with Intel IOMMU
      [*]     Enable Intel DMA Remapping Devices by default
      [*]   Support for Interrupt Remapping

Re-build the kernel

Turn on IOMMU in GRUB. Edit file /etc/default/grub and add parameters to the kernel

FILE /etc/default/grub
GRUB_CMDLINE_LINUX="... intel_iommu=on pcie_acs_override=downstream,multifunction ..."

And apply changes:

root #grub-mkconfig -o /boot/grub/grub.cfg


Check, that IOMMU turned on and works

user $dmesg | grep 'IOMMU enabled'
[    0.000000] DMAR: IOMMU enabled
Note
For CPU on XEN architecture you may run:
user $lspci -vv | grep -i 'Access Control Services'

IOMMU groups

Passing through PCI or VGA devices requires you to pass through all devices within an IOMMU group. The exception to this rule are PCI root devices that reside in the same IOMMU group with the device(s) we want to pass through. These root devices cannot be passed through as they often perform important tasks for the host. A number of (Intel) CPUs, usually consumer-grade CPUs with integrated graphics (IGD), share a root device in the same IOMMU group as the first PCIe 16x slot.

user $for d in /sys/kernel/iommu_groups/*/devices/*; do n=${d#*/iommu_groups/*}; n=${n%%/*}; printf 'IOMMU Group %s ' "$n"; lspci -nns "${d##*/}"; done;
...

IOMMU Group 13 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP104 [GeForce GTX 1080] [10de:1b80] (rev a1)

IOMMU Group 15 02:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere [Radeon Pro WX 7100] [1002:67c4]

IOMMU Group 16 02:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere [Radeon RX 580] [1002:aaf0]

...

Nvidia in IOMMU Group 13 and AMD Video Card in IOMMU group 15 and 16. Everything looks fine. But if you have buggy IOMMU support and all devices within one IOMMU group, hardware can't guarantee good device isolation. Unfortunately, it is not possible to fix that. The only workaround - use ACS override patch witch ignore IOMMU hardware check. See ACS override patch.

Other devices in my IOMMU group

ACS override patch

root #git clone https://github.com/feniksa/gentoo_ACS_override_patch.git /etc/portage/patches

Next re-emerge the kernel

root #emerge gentoo-sources
 * Applying 4400_alpha-sysctl-uac.patch (-p1) ...                                       [ ok ]
 * Applying 4567_distro-Gentoo-Kconfig.patch (-p1) ...                                  [ ok ]
>>> Source unpacked in /var/tmp/portage/sys-kernel/gentoo-sources-4.14.52/work
>>> Preparing source in /var/tmp/portage/sys-kernel/gentoo-sources-4.14.52/work/linux-4.14.52-gentoo ...
 * Applying override_for_missing_acs_capabilities.patch ...                             [ ok ]
 * User patches applied.

VFIO

Kernel drivers

KERNEL
Device Drivers --->
  <M> VFIO Non-Privileged userpsace driver framework --->
      [*]   VFIO No-IOMMU support ----
      <M>   VFIO support for PCI devices
      [*]     VFIO PCI support for VGA devices
      < >   Mediated device driver framework

Search for your VGA card ids. Run

root #lspci -nn
...
04:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Vega 10 XL/XT [Radeon RX Vega 56/64] [1002:687f] (rev c1)
04:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Device [1002:aaf8]
..


Add VGA PCI ids to VFIO

FILE /etc/modprobe.d/vfio.conf
options vfio-pci ids=1002:687f,1002:aaf8

Libvirt

Windows

Create windows 10 as usual via libvirt manager. Edit virtual image, click Add Hardware, select AMD Ati Vega 64 and AMD Ati device. Click Apply.

Now you can start Windows 10 guest OS.

Amd card have 2 devices on PCIe bus -> one video output and another is HDMI output. Windows drivers works only if KVM will bypass to windows both AMD devices.

Fixed Vega 56/64 reset bug

AMD Vega 56/64 is unable to initialize itself after Guest host shutdown/reboot, because drivers left card in "garbage" state. As workaround of this bug, VFIO should load AMD card ROM at guest startup. To do that:

  1. Install clear Windows 10 somewhere (not in libvirt. We need REAL windows10)
  2. Install all latest windows 10 updates
  3. Install AMD vga drivers
  4. Reboot
  5. Go again to real windows 10
  6. Install GPU-Z
  7. In GPU-Z in main tab, near BIOS version will be small button "Save ROM". Click it and save ROM somewhere. We will need this ROM for our gentoo and libvirt. For example, for my Vega64 I save ROM as Vega64.rom
  8. Boot into gentoo back
  9. mkdir /etc/firmware
  10. Copy to /etc/firmware your ROM file (in my case it is Vega64.rom)
  11. Go to /etc/libvirt/qemu
  12. Edit xml file with description of Win10 guest
  13. Find section with AMD Video Card device (not AMD HDMI. You can always re-check by lspci)

In my case:

FILE /etc/libvirt/qemu/win10.xml
...
    <hostdev mode='subsystem' type='pci' managed='yes'>
      
        <address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
   </hostdev>
...

14. Add path to vga rom

     <rom bar='on' file='/etc/firmware/Vega64.rom'/>

So, it should be:

FILE /etc/libvirt/qemu/win10.xml
...
    <hostdev mode='subsystem' type='pci' managed='yes'>
      
        <address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
     <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
     <rom bar='on' file='/etc/firmware/Vega64.rom'/>
   </hostdev>
...

Sound

create dir /home/qemu copy /home/USER/.config/pulse to /home/qemu chown qemu:qemu -R /home/qemu

change home dir for qemu: usermod -d /home/qemu qemu

Set home dir:

See also

  • QEMU — a generic, open source hardware emulator and virtualization suite.

External resources