Angry robot logo

Mumbling about computers

GPU Passthrough in Debian

This is a recompilation of various sources to get gpu passthrough on Debian.

General notes

  • Update your kernel. Seriously, 90% of my issues were solved by being on 4.14.7 AND I got better performance with the NPT patch.
  • Passthrough a usb controller. I had slightly jittery pointer movements when doing small mouse movements (that were supposed to be precise)
  • Get a USB audio card. I spent 3 days fighting pulseaudio as I had ~100ms delay on audio that went directly through my motherboard's audio output. It was solved instantly by a $3 usb audio card.
  • Move processes away from your VM cores.
    • I had jittery performance when I had a lot of processes running, as they were taking time from the VM. I got 60FPS in games either way, but now it's a lot smoother.
    • I couldn't get KVM to run in cores that were isolated by the kernel. I don't know why, but it just didn't use the isolated cores.
  • If you build qemu-patched to test pulse audio routing, make sure you build with --enable-libusb

Update your kernel

For amd you need at least 4.14.*, or the npt patch. After that update your initramfs again. (Is that necessary?)

BIOS/UEFI

Enable IOMMU and Virtualization

Modules

disable modules

File: /etc/modprobe.d/passthrough-blacklist.conf

blacklist radeon
blacklist amdgpu
blacklist snd_hda_intel

Load modules

File: /etc/modules

vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd

set vfio-pci options

File: /etc/modprobe.d/vfio.conf

options vfio-pci ids=1002:67ef,1002:aae0,13f6:8788 disable_idle_d3=1

set grub parameters

File: /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt cgroup_enable=memory rootdelay=2 swapaccount=1 text"

run update-grub2

edit initramfs

File: /etc/initramfs-tools/modules

vfio
vfio_iommu_type1
vfio_pci ids=1002:67ef,1002:aae0,13f6:8788 disable_idle_d3=1
vfio_virqfd
vhost-net

run update-initramfs -u

KVM

My script looks like this

taskset -c 12-15 ./qemu-patched \
        -enable-kvm -m 8192 \
        -cpu host -smp 4,sockets=1,cores=4,threads=1 \
        -bios /usr/share/ovmf/OVMF.fd -vga none -nographic \
        -serial none -parallel none \
        -netdev type=tap,id=net0,ifname=vmtap0,vhost=on \
        -device virtio-net-pci,netdev=net0,mac=00:16:3e:00:02:02 \
        -device vfio-pci,host=0b:00.0,multifunction=on,romfile=$USR_HOME/scripts/rx560.rom \
        -device vfio-pci,host=0b:00.1 \
        -device vfio-pci,host=0d:00.3 \
        -drive id=disk0,if=virtio,cache=none,file=$DISK,format=qcow2 \
        -rtc clock=host,base=utc;

GPU Bios

If you get a black screen when you boot your VM and get a core stuck to 100% you might have a 'bad' gpu bios. You need to extract your BIOS from your own card, if you use linux you need to have 2 GPUs or otherwise you get a corrupt image. On windows this works fine.

Bugs

Device or resource busy

I got spammed to death with qemu-system-x86_64: vfio_region_write(0000:0a:00.0:region0+0x11d9e4, 0x3a4c4c,4) failed: Device or resource busy which was fixed by running

echo 0 > /sys/class/vtconsole/vtcon0/bind
echo 0 > /sys/class/vtconsole/vtcon1/bind
echo efi-framebuffer.0 > /sys/bus/platform/drivers/efi-framebuffer/unbind

Useful testing scripts

This was all taken from the arch wiki and formatted / made a bit nicer

Test your IOMMU groups

##!/bin/bash
shopt -s nullglob
for d in /sys/kernel/iommu_groups/*/devices/*; do 
    n=${d#*/iommu_groups/*}; n=${n%%/*}
    printf 'IOMMU Group %s ' "$n"
    lspci -nns "${d##*/}"
done | sort -n -k3

You'll most likely have success passing devices that are on their own on a given IOMMU group.
I couldn't get any device which wasn't on his own passed to KVM, maybe you could try the acs override patch.

Test your USB hubs

for usb_ctrl in $(find /sys/bus/usb/devices/usb* -maxdepth 0 -type l); do
        pci_path="$(dirname "$(realpath "${usb_ctrl}")")";
        echo "Bus $(cat "${usb_ctrl}/busnum") --> $(basename $pci_path) (IOMMU group $(basename $(realpath $pci_path/iommu_group)))";
        lsusb -s "$(cat "${usb_ctrl}/busnum"):";
        echo;
done

This was useful for me to test in which hub I had my devices, as one of my root hubs was OK to be passed to the VM.