Проброс видеокарты в виртуальную машину средствами KVM VFIO

Опубликовано 6 Сентября, 2014 под тегами Gentoo, kernel, kvm, qemu, vfio, vga passthrough, Windows, Gentoo, kernel, patch

Давеча я уже писал о пробросе видеокарты средствами Xen. Какое-то время оно работало, но после обновлений как-то перестало. Xen 4.4 в Gentoo вообще выкинул qemu-traditional, а апстрим сломан. А 4.3, который раньше вполне себе работал, почему-то сотрудничать на ядрах с 3.14 со мной отказывается. В связи с этим я решил проверить, как дела у соседей, то бишь у KVM. На моих прошлых тестах он показал себя не то чтобы очень хорошо: периодически подвисал. Но с тех пор многое могло измениться (и изменилось!)

Итак, собственно, в основном техника выполнения не очень разнится с Xen, однако есть некоторые детали. Во-первых, конфиг ядра

CONFIG_GART_IOMMU=y
CONFIG_IOMMU_HELPER=y
CONFIG_VFIO_IOMMU_TYPE1=m
CONFIG_VFIO=m
CONFIG_VFIO_PCI=m
CONFIG_VFIO_PCI_VGA=y
CONFIG_IOMMU_API=y
CONFIG_IOMMU_SUPPORT=y
CONFIG_INTEL_IOMMU=y
CONFIG_INTEL_IOMMU_DEFAULT_ON=y
CONFIG_INTEL_IOMMU_FLOPPY_WA=y
CONFIG_HAVE_KVM=y
CONFIG_HAVE_KVM_IRQCHIP=y
CONFIG_HAVE_KVM_IRQ_ROUTING=y
CONFIG_HAVE_KVM_EVENTFD=y
CONFIG_KVM_APIC_ARCHITECTURE=y
CONFIG_KVM_MMIO=y
CONFIG_KVM_ASYNC_PF=y
CONFIG_HAVE_KVM_MSI=y
CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT=y
CONFIG_KVM_VFIO=y
CONFIG_KVM=m
CONFIG_KVM_INTEL=m
CONFIG_KVM_DEVICE_ASSIGNMENT=y

Это для процессоров Intel. Для AMD соответственно должно быть AMD вместо INTEL, но возможности проверить как-то не представилось. Кое-что у меня представлено модулями, это исключительно преференция. Какие-то опции, возможно, не являются необходимыми, но разбираться в тонкостях мне сейчас немного недосуг.

Теперь о видеокарте. Естественно, что желательно, чтобы карту никто кроме виртуальной машины не трогал. pcistub или аналоги вполне подойдут (хотя если в системе хоста драйверов нет, то и это не необходимо). Чтобы видеокарту пробросить, надо привязать ее к модулю vfio. Делается это примерно следующим образом:

modprobe vfio-pci
echo %video_port% > /sys/bus/pci/devices/%video_port%/driver/unbind
echo %audio_port% > /sys/bus/pci/devices/%video_port%/driver/unbind
echo 0x%vendor% 0x%video_id% > /sys/bus/pci/drivers/vfio-pci/new_id
echo 0x%vendor% 0x%audio_id% > /sys/bus/pci/drivers/vfio-pci/new_id

Здесь %video_port%, %audio_port% – соответствующие порты PCI, на которых расположены собственно сама карта и ее звуковая (для вывода звука через HDMI/DisplayPort). У меня это 0000:01:00.0 и 0000:01:00.1 соответственно (не забывайте, что : надо экранировать обратным слешем или кавычками!). %vendor% и %*_id% это соответствующие идентификаторы устройств. Узнать их можно при помощи lspci -nn (как и порты, кстати). Например:

lspci -nnD | grep ATI
0000:01:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Cape Verde PRO [Radeon HD 7750 / R7 250E] [1002:683f]
0000:01:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Cape Verde/Pitcairn HDMI Audio [Radeon HD 7700/7800 Series] [1002:aab0]

Первая запись в строке – порт. Последняя запись в квадратных скобках %vendor_id%:%device_id%. Сразу оговорю, карты Nvidia пробрасываются аналогично, но проверить пока не довелось.

Итак, привязав таким образом видеокарту, мы можем запускать qemu. Для начала, если kvm собран модулем, надо загрузить модуль:

modprobe kvm_intel

либо

modprobe kvm_amd

Теперь собственно qemu:

qemu-system-x86_64 -enable-kvm -M q35 -cpu host #Здесь особо нечего настраивать. -cpu вместо host можно попробовать указать модель процессора, например, Haswell
    -m 3000 #Количество памяти виртуальной машины, в мегабайтах
    -smp 6,sockets=1,cores=6,threads=1 # Количество процессоров, ядер и потоков виртуальной машины. Изменить по вкусу.
    -bios /usr/share/qemu/bios.bin #Путь к seabios, обычно именно такой
    -vga none #отключает эмуляцию основного дисплея, что необходимо для работы проброшенной видеокарты.
    -device secondary-vga #создает фиктивное вторичное устройство, чтобы если qemu собран с поддержной sdl, spice, vnc или чего-то подобного, работал захват мыши и клавиатуры.
    -device ioh3420,bus=pcie.0,addr=1c.0,multifunction=on,port=1,chassis=1,id=root.1 # создает корневой порт pci-express, к которому мы цепляем видеокарту.
    -device vfio-pci,host=01:00.0,bus=root.1,multifunction=on,x-vga=on #цепляет видеокарту к корневому порту
    -device vfio-pci,host=01:00.1,bus=root.1 #цепляет звуковую карту к корневому порту. Иногда вместо bus=root.1 (при разных проблемах) здесь помогает bus=pcie.0, либо вообще не пробрасывать аудио
    -drive file=Windows-kvm.img,id=disk1,format=raw # Виртуальный жесткий диск, расположенный в файле, в данном случае Windows-kvm.img. Можно указать полный путь
    -device ide-hd,bus=ide.0,drive=disk1 # Устройство, эмулирующее жесткий диск, определенный выше
    -net nic -net bridge,br=xenbr0 # Локальная сеть типа "мост". В моем случае мост уже создан и называется xenbr0. О создании сетевого моста предлагаю ознакомиться с прошлой статьей по теме

Естественно, комментарии и переносы строк надо убрать, иначе bash не разберется. Если нужно подцепить iso-образ установочного диска, то можно добавить такие параметры:

    -drive file=installDVD.iso,id=isocd # диск-образ из файла, в данном случае installDVD.iso, можно задать полный путь
    -device ide-cd,bus=ide.2,drive=isocd # устройство, эмулирующее определенный выше диск
    -boot d # грузиться с компакт-диска в первую очередь

И все бы хорошо. Да вот только у меня, например, при таком пробросе, портится цветопередача на видеокарте хоста. Но с этим можно было бы мириться. Плюс к этому, моя радеоновская карта (Radeon HD 7750) удовлетворительно работает ровно до тех пор, пока на виртуальной машине не установлены драйвера Catalyst. С драйвером я получаю BSOD 0x116.

i916 VGA Arbiter Проблема в целом известная, и связана с особенностями работы встроенных видеокарт от Intel и соответствующими этому особенностями работы драйвера i915. Существует патч, но он не то чтобы очень хорошо применяется на gentoo-sources 3.16.1. И у него есть одна проблема: он фактически отключает DRI на встроенной видеокарте, а как следствие, OpenGL на хосте будет ТОРМОЗИТЬ. Мне лично все равно, а читатель решит для себя сам.

Модифицированный под gentoo-sources-3.16.1 патч приложен. Как его наложить, я думаю, понятно. После наложения надо добавить параметр ядра

i915.enable_hd_vgaarb=1

в опции загрузки, либо, если i915 собран в виде модуля, можно добавить

options i915 enable_hd_vgaarb=1

в /etc/modprobe.d/i915vgaarb.conf. После этого у меня все (кроме OpenGL на хосте) работает прекрасно. OpenGL на хосте просто работает. Но в игры на нем конечно особо поиграть не получится.

VGA Arbiter, PCIe ACS Опять же, это не единственная проблема, которая может вас поджидать. Кроме проблем с видеокартами Intel, есть еще баги других видеокарт (патч), и проблемы реализации PCIe ACS (обеспечивающем изоляцию виртуализованных PCIe-устройств, патч, регулируется опцией загрузки ядра pcie_acs_override=multifunction|downstream|id:nnnn:nnnn, подробнее см. ссылку). Тем не менее, первое, что следует проверить – а не заработает ли оно вообще без патчей.

Итак, резюмируя, если у Вас первичная видеокарта хоста – встроенная интеловская, то делаете по инструкции i916 VGA Arbiter. Если нет, возможно, понадобится патч VGA Arbiter (но не факт). Если QEMU ругается на IOMMU group, возможно стоит попробовать патч PCIe ACS и параметры pcie_acs_override=downstream или pcie_acs_override=multifunction.

Источники: Форум ArchLinux Блог разработчика VFIO Список рассылки Linux Kernel Особенно советую почитать FAQ по VFIO, прежде, чем делать по инструкции с форума Arch.

UPDATE: Патч i916 VGA Arbiter для 3.17.1: intelvgaarb.patch