Проброс видеокарты в гостевую ОС из гипервизора KVM с помощью технологии VFIO

в 15:06, , рубрики: kvm, linux, passthrough, виртуализация, Железо, метки: , , ,

Вступление

В статье описывается способ проброса физических устройств из гипервизора KVM в гостевую ОС с помощью технологии VFIO, реализованной в linux kernel 3.9.*.
Материал не является руководством к действию (прим. туториалом). Все описанные инструкции вы производите на свой страх и риск.

Эксперимент выполняется под ОС Ubuntu 13.10:

  • kernel: 3.11.0-15-generic
  • qemu: 1.5.0
  • seabios: 1.7.3

Аппаратная часть стенда:

  • мат. плата: AMD990FX
  • процессор: AMD FX-8120
  • опер. память: DDR3 PC3-14900
  • видеокарта: ATI RADEON HD 7750
  • видеокарта (гость): NVIDIA GTX560-TI

Видеокарта для гостевой системы и хоста, может быть, предположительно, любая (NVIDIA, ATI RADEON).
Материнская плата должна иметь блок управления памятью ввода/вывода (IOMMU) — технология AMD-Vi или VT-d.
Если используется процессор Intel, то он также должен поддерживать VT-d.
Независимо от выбранной платформы в биосе материнской платы должны быть реализованы IVRS/DMAR таблицы.

Подготовка

Настоятельно рекомендуется обновить биос мат. платы с сайта производителя перед началом эксперимента.

Проверяем поддержку технологии проброса.

$ dmesg | grep AMD-Vi
[    1.279788] AMD-Vi: Found IOMMU at 0000:00:00.2 cap 0x40
[    1.279790] AMD-Vi: Interrupt remapping enabled
[    1.292879] AMD-Vi: Lazy IO/TLB flushing enabled

Включаем модули в ядре.

$ cat /etc/modules
# /etc/modules: kernel modules to load at boot time.
lp
rtc
pci_stub
vfio
vfio_iommu_type1
vfio_pci
#vfio_pci_vga  (For now, this component is in the stock Ubuntu kernel.)
kvm
kvm_amd

$ sudo update-initramfs -u

Отключаем пробрасываемые устройства (для удобства также проброшены порты usb контроллера).

$ lspci -nn | grep NVIDIA
03:00.0 VGA compatible controller [0300]: NVIDIA Corporation GF114 [GeForce GTX 560 Ti] [10de:1200] (rev a1)
03:00.1 Audio device [0403]: NVIDIA Corporation GF114 HDMI Audio Controller [10de:0e0c] (rev a1)

$ cat /etc/default/grub | grep GRUB_CMDLINE_LINUX_DEFAULT
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash pci-stub.ids=10de:1200,10de:0e0c,1002:4397,1002:4396"

$ sudo update-grub

После перезагрузки должно получиться:

$ dmesg | grep pci-stub
[    3.163062] pci-stub: add 10DE:1200 sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000
[    3.163088] pci-stub 0000:03:00.0: claimed by stub
[    3.163100] pci-stub: add 10DE:0E0C sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000
[    3.163116] pci-stub 0000:03:00.1: claimed by stub
[    3.163124] pci-stub: add 1002:4397 sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000
[    3.163136] pci-stub: add 1002:4396 sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000

Создаем скрипт для подключения устройства к VFIO-PCI.

$ cat /usr/bin/vfio-bind
#!/bin/bash

modprobe vfio-pci

for dev in "$@"; do
        vendor=$(cat /sys/bus/pci/devices/$dev/vendor)
        device=$(cat /sys/bus/pci/devices/$dev/device)
        if [ -e /sys/bus/pci/devices/$dev/driver ]; then
                echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
        fi
        echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id
done

$ sudo chmod 755 /usr/bin/vfio-bind

И добавляем его в автозапуск (ключи берем из вызова lspci — рассматривалось выше).

$ cat /etc/rc.local
#!/bin/sh -e

/usr/bin/vfio-bind 0000:03:00.0 0000:03:00.1 0000:00:12.0 0000:00:12.2
exit 0
Небольшая автоматизация процесса

Внимание: задать ваши параметры!

$ cat pass.sh
#!/bin/sh

#configs
modules="pci_stub;vfio;vfio_iommu_type1;vfio_pci;kvm;kvm_amd"
grub="quiet splash pci_stub.ids=10de:1200,10de:0e0c,1002:4397,1002:4396"
autorun="/usr/bin/vfio-bind 0000:03:00.0 0000:03:00.1 0000:00:12.0 0000:00:12.2"

#functions
split() {
	#$1 - file, $2 - param
	arr=$(echo $2 | tr ";" "n")

	for x in $arr
	do
	    echo $x >> $1
	done
}

replaceParam() {
	#$1 - file, $2 - param, $3 - value
	cur_val=`cat $1 | grep $2`
	sed "s/$cur_val/$2="$3"/g" -i $1
}

autorunAdd() {
	#$1 - param
	str='$i '"$1"'n'
	sed -i -e "$str" /etc/rc.local
}

pause() {
	read -p "$1" -n1 -s
	echo
}

#info virtualization
dmesg | grep AMD-Vi
pause 'Press any key to continue or CTRL-C to exit...'

#modules load
split '/etc/modules' "$modules"
update-initramfs -u

#grub config
replaceParam '/etc/default/grub' 'GRUB_CMDLINE_LINUX_DEFAULT' "$grub"
update-grub

autorunAdd "$autorun"

$ cat vfio-bind
#!/bin/bash

modprobe vfio-pci

for dev in "$@"; do
        vendor=$(cat /sys/bus/pci/devices/$dev/vendor)
        device=$(cat /sys/bus/pci/devices/$dev/device)
        if [ -e /sys/bus/pci/devices/$dev/driver ]; then
                echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
        fi
        echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id
done

$ sudo cp vfio-bind /usr/bin/
$ sudo chmod 755 /usr/bin/vfio-bind
$ sudo chmod 755 pass.sh
$ sudo bash pass.sh

Запуск

В качестве гостевой ОС используется WINDOWS 7 с паравиртуальными драйверами VIRTIO. В соответствии с этим выбрано оборудование виртуальной машины.

$ cat win7_vfio_test.sh
#! /bin/sh
IS_TAP0_EXISTS=`ifconfig | grep -c tap0`
if [ $IS_TAP0_EXISTS = 0 ] ; then
    sudo tunctl -t tap0 && sudo ifconfig tap0 0.0.0.0 promisc up && sudo brctl addif br0 tap0 && echo 'Success: tap0 interface created.' || echo 'Error: tap0 interface is not created.'
else
    echo 'Success: tap0 interface already exists.'
fi

#init kvm-qemu
sudo qemu-system-x86_64 
-boot menu=on 
-enable-kvm 
-M q35 
-m 1024 
-cpu host 
-rtc base=localtime 
-cpu Opteron_G4,+perfctr_nb,+perfctr_core,+topoext,+nodeid_msr,+lwp,+wdt,+skinit,+ibs,+osvw,+cr8legacy,+extapic,+cmp_legacy,+fxsr_opt,+mmxext,+osxsave,+monitor,+ht,+vme 
-smp 1,sockets=1,cores=1,threads=1 
-bios /usr/share/qemu/bios.bin 
-acpitable file=/usr/share/seabios/q35-acpi-dsdt.aml 
-device ioh3420,bus=pcie.0,addr=1c.0,multifunction=on,port=1,chassis=1,id=root 
-device ahci,bus=pcie.0,id=ahci 
-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x6 
-drive file='/dev/sdc6',if=none,id=drive-virtio-disk0,format=raw -device virtio-blk-pci,scsi=off,bus=pcie.0,addr=0x5,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=2 
-drive file='windows7.iso',if=none,id=drive-ide0-1-0,readonly=on,format=raw -device ide-cd,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0,bootindex=1 
-drive file='virtio-win-0.1-74.iso',if=none,id=drive-ide0-2-0,readonly=on,format=raw -device ide-cd,bus=ide.2,unit=0,drive=drive-ide0-2-0,id=ide0-2-0 
-netdev tap,ifname=tap0,id=hostnet0,script=no,downscript=no -device virtio-net-pci,netdev=hostnet0,mac=52:54:00:26:7F:96,id=net0 
-device vfio-pci,host=00:12.0,bus=pcie.0 
-device vfio-pci,host=00:12.2,bus=pcie.0 
-device vfio-pci,host=03:00.0,bus=root,addr=00.0,multifunction=on,x-vga=on 
-device vfio-pci,host=03:00.1,bus=root,addr=00.1 
-vga none 
-nographic

#-vnc 127.0.0.1:0

В ВМ проброшен раздел жесткого диска -drive file=/dev/sdc6.
Начальная установка выполнялась без проброса физических устройств (можно использовать -vnc 127.0.0.1:0 или стандартную консоль).
Аргументы -vga none -nographic следует добавить при пробросе компонентов видеокарты (-device vfio-pci).

Автор: DeathSAAD

Источник

Поделиться

* - обязательные к заполнению поля