Using a PCI graphics card in KVM/QEMU on Debian Stretch
Update 2020: follow-up article in regard to PulseAudio can be found here: „Fixing PulseAudio sound problems in QEMU/KVM VMs by building QEMU 4.2 on Debian Buster“.
Why? Some software needs to use a dedicated graphics card, like GPU computation or playing video games. But not everything is available in Debian, so we can use virtual machines to use other operating systems as well, without having to use dual boot.
My setup is as follows: Intel Core i5 processor with integrated graphics, VT-d and IOMMU, and a NVIDIA GeForce GTX dedicated graphics card. These two are necessary for what we are going to do. In a normal scenario you would use your graphics card as the main display output device. You plug it into your PCIe slot, set the card as default in your BIOS and plug in your monitors into the HDMI/DVI outputs of your card. Everything that is done graphically - playing, watching movies, GPU computing - is then done by the card.
In the new setup I want my card to be passed into a virtual machine exclusively. That means my host system (the operating system which boots when I power on my computer, Debian Stretch) cannot make use of it and must use the internal graphics processor (IGP). This setting can be found in your BIOS/UEFI settings menu at start time. You can use two cables from both outputs into one monitor and switch by pressing your source button. Passing in the PCI device has one big advantage over using the emulated graphics device inside a virtual machine: there is no abstraction layer in between, resulting in the best performance.
I did this whole procedure some years ago already (with Wheezy…), but the documentation and software support for passthrough has improved enormously. You should only continue if you know what you are doing. Backup your data and probably have another computer in standby. If you are unsure about the procedure, stop now and ask someone who you think can help you. I'm in no way responsible for any damages to your hardware or software.
Let's begin with the configuration. For the details I advise you to read the extensive article by Heiko Sieger.
- Check if your hardware supports IOMMU and VT-d
- Check that you are using the correct output (the integrated graphics processor)
- Blacklist modprobe modules which could claim the dedicated PCI card (
nouveau
,nvidia
) and add thevfio-pci
module with options (ids=
) - Add the vfio modules to your initramfs and use
update-initramfs -u
to create the new initramfs - Reboot
You should now have bound the card to the vfio-pci driver. You can use dmesg | grep vfio
to check for existing vfio_pci: add
strings which should be the IDs of your video card.
Update 2018-06-24: if you are switching from a NVIDIA PCI graphics card to Intel i915 integrated graphics, your Xorg might need a config in /etc/X11/xorg.conf
. My X11 on Debian segfaults after removing all NVIDIA driver related packages (and blacklisting the nouveau kernel module) because it still wanted to use card0 and card1, but card1 is the vfio-bound PCI card (you can check this by comparing the PCI slot identifiers). Login as root and run X -configure
. A new autogenerated xorg.conf.new
appears in your directory. Remove all sections that might cause trouble (Section "Device"
with Identifier "Card1"
) and restart your X server.
libvirt
In the following I am using libvirt to manage my virtual machines. Disable all display output in your configuration, so that no Spice or VNC are left.
For networking I decided to stay with the default NAT adapter. I know, I know, very bad. In an advanced setup you can create a bridge and connect it to your VM, but I have not experienced any network limits inside the VM yet, so I did not test another configuration.
What you want to do however is using the virtio driver whereever possible. This should be doable with networking and storage. If your guest will be a Windows machine, you need to load the virtio drivers as a removable disk when setting things up. Else your virtual hardware will not show up.
You should use the OVMF BIOS option when creating the machine, because the support for this setup is dependant on UEFI. It helps to create a writable directory where the OVMF_VARS.fd
file can be stored, so your VM UEFI settings are persistently saved (e.g. the boot order).
So far so good. For reference, here are the most important snippets from my VM config:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
If you have problems with the GTX drivers in a Windows guest, make sure you have both KVM hiding switches enabled: <kvm><hidden state='on'/></kvm>
and -cpu host,kvm=off
. Otherwise it could happen that the Windows driver shuts down the hardware device because the device detected the virtual environment and raises errors.
Using PulseAudio for sound
As you can see in my configuration snippet above, I am using an existing PulseAudio session I attach the VM sound device to. This needs some additional configuration: starting the machine via libvirt uses the default system user libvirt-qemu
. This can be tricky in combination with the running PulseAudio session which is most likely started by your local user, with permissions only granted to this account.
After my initial setup, I tried using the existing session at /run/user/1000/pulse/native
, but the following error appeared in /var/log/libvirt/<vmname>.log
during boot:
1 2 3 |
|
This error means libvirt-qemu
tried to use the referenced session socket but was refused because of permissions. I found two options to solve this problems: either you run QEMU as your session user or you configure PulseAudio to create a UNIX socket with anonymous authentication. Some forum posts suggest to copy the PulseAudio cookie
file, but this did not work for me.
For the first option, start your VM as another user by editing /etc/libvirt/qemu.conf
:
1 2 3 4 |
|
The second option is probably the better solution, because it does not mix the libvirt-qemu
user with your local user account. To create a second socket, copy /etc/pulse/default.pa
to ~/.pulse/default.pa
(or edit the file in your home directory if it already exists) and add the following line at the bottom.
1 |
|
Restart your PulseAudio daemon with pulseaudio -k
and pulseaudio --start
. Your volume controls should work as before and PulseAudio should have created both sockets, the one in /run/user/1000/pulse/native
and /tmp/pa.socket
. The next time you boot your VM it should appear in the host's PulseAudio control panel as a client playing audio. Sound playing from inside the VM should work as expected.
One thing I could not solve yet is a 24 second latency when using the PulseAudio session as a recording device inside the VM. If you run e.g. Audacity and set your microphone as the recording input for the VM recording device in PulseAudio, Audacity records with a 24 second delay. This is due to a wrong handling of the buffer PulseAudio uses. The VM seems to not empty the buffer before fetching the content. A patch exists, but after looking into the Debian qemu package source the patch was not applied. There are some hints of discussion about the problem in bug trackers and mailing lists, and it seems to be fixed, but the problem has persisted until now in Debian Stretch.
Resources
- libvirt QEMU driver docs
- libvirt Domain XML docs
- Tutorial for PCI passthrough by Heiko Sieger
- Red Hat VirtIO drivers
- Reddit /r/VFIO: Arch Linux + pulseaudio on Windows 10 guest
- Superuser: kvm/qemu - Audio from windows guest to host without spice
- Reddit /r/linux_gaming: fixing PulseAudio pa_context_connect()
- Arch Forums: QEMU microphone input has 25 seconds delay
- QEMU patch for PulseAudio recording latency by Volker Rümelin
Open graph icon made by Freepik from www.flaticon.com, licensed as CC 3.0 BY.