Перейти к содержанию

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

Введение

GPU passthrough позволяет напрямую передавать видеокарту внутрь виртуальной машины. Это может понадобиться по разным причинам - например, для запуска нейросетей или реализации рабочего места.

В дальнейшей заметке я опишу свой опыт проброса видеокарты Nvidia GTX 1660 Super. По хорошему, данная заметка должна работать и на других видеокартах

Настройки на гипервизоре

Настройка BIOS

  1. Включаем поддержку виртуализации, если еще не сделали это - VT-x для Intel и SVM для AMD. Да и если не сделали - как у вас Proxmox вообще нормально работает?
  2. Отключаем CSM (Compatibility Support Module) - без него остальные вещи (IOMMU и прочее), как правило, не получится включить.
  3. Включаем VT-d (если Intel) или AMD-Vi/IOMMU (если AMD) - это аппаратные технологии ввода-вывода, которые предназначены для виртуализации устройств. Как раз именно эта опция позволит напрямую использовать нашу видеокарту в виртуалке. Скажу сразу, эти опции могут быть запрятаны глубоко - например в моем BIOS они были аж в "Advanced > AMD CBS > NBIO Common Options > IOMMU".
  4. Включаем Above 4G Decoding - позволяет устройствам использовать адреса памяти выше 4х ГБ. Без этой опции в логах ядра мы сможем увидеть vfio-pci: BAR 0 region memory size exceeds supported 32-bit limit.
  5. Если в процессоре есть встроенное видеоядро - ищем опцию "Init Display Output" - это нужно для того, чтобы минимизировать шанс ядра на захват видеокарты буфером вывода.

Получение vendor id и device id

Запускаемся, заходим на хост, теперь нам нужно получить vendor id и device id которые будут использоваться в дальнейшем.

Для того, чтобы узнать эти ID, нам нужно найти все устройства NVIDIA, для этого выполняем:

lspci -nn | grep -i nvidia

Получим такой вывод:

07:00.0 VGA compatible controller [0300]: NVIDIA Corporation TU116 [GeForce GTX 1660 SUPER] [10de:21c4] (rev a1)
07:00.1 Audio device [0403]: NVIDIA Corporation TU116 High Definition Audio Controller [10de:1aeb] (rev a1)

где 10de:21c4 и 10de:1aeb - это и есть наши пары vendorId:deviceId. Запоминаем их, они понадобятся нам много где позже.

Настройка запуска ядра

Заходим в /etc/default/grub и редактируем строку:

GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt pci=realloc vfio-pci.ids=10de:21c4,10de:1aeb"

Разберемся с опциями:

  • amd_iommu=on - включает поддержку IOMMU для AMD. Если intel - вставляем intel_iommu=on
  • iommu=pt - включает тегирование iommu только для тех устройств, которые настроены на проброс. Сильно ускоряет производительность.
  • pci=realloc перераспределение BAR-ов
  • vfio-pci.ids=10de:21c4,10de:1aeb - вместо 10de:21c4,10de:1aeb нужно вставить vendorId:deviceId устройств, которые мы получили в предыдущем шаге. Опция не всегда обязательна, но некоторые видеокарты без неё не захватываются при помощи vfio.

Сохраняем файл, прописываем update-grub, перезагружаемся.

Проверка IOMMU

Заходим в систему после перезагрузки и проверяем следующим скриптом, появились ли IOMMU-группы:

for g in $(find /sys/kernel/iommu_groups/* -maxdepth 0 -type d | sort -V); do
    echo "IOMMU Group ${g##*/}:"
    for d in $g/devices/*; do
        echo -e "\t$(lspci -nns ${d##*/})"
    done;
done;

Если есть вывод с группами - всё окей, можно продолжать. Если нет - значит IOMMU не включен.

Отключение драйверов видеокарты

Перед включением vfio, нам нужно выключить драйвера нашей видеокарты, чтобы они не мешали захвату vfio. Для этого добавьте в /etc/modprobe.d/blacklist.conf следующие строки:

  1. Для карт NVIDIA

blacklist nouveau
blacklist nvidia*
2. Для карт AMD

blacklist amdgpu
blacklist radeon
3. Для карт Intel

blacklist i915

Настройка vfio

Добавляем модули vfio

Добавляем следующие модули в /etc/modules:

vfio
vfio_iommu_type1
vfio_pci

Если ядро меньше 6.2, нужно добавить vfio_virqfd. В 6.2 и старше он включен по умолчанию.

Настраиваем vfio

В файле /etc/modprobe.d/vfio.conf нужно добавить следующую строку:

options vfio-pci ids=10de:1b81,10de:10f0 disable_vga=1

Очевидно, что вместо 10de:1b81,10de:10f0 мы вставляем наши пары vendorId:deviceId которые получили ранее.

Обновляем initramfs

На этом настройка на гипервизоре закончена. Нам остается обновить initramfs и перезагрузиться:

update-initramfs -u -k all
reboot now

Проверка корректности захвата vfio нашей видеокарты:

В dmesg | grep vfio должны получить примерный вывод:

[   40.004872] vfio-pci 0000:07:00.0: vgaarb: deactivate vga console
[   40.004876] vfio-pci 0000:07:00.0: vgaarb: VGA decodes changed: olddecodes=io+mem,decodes=io+mem:owns=none
[   40.004992] vfio_pci: add [10de:21c4[ffffffff:ffffffff]] class 0x000000/00000000
[   40.054256] vfio_pci: add [10de:1aeb[ffffffff:ffffffff]] class 0x000000/00000000
[  235.344905] vfio-pci 0000:07:00.1: enabling device (0000 -> 0002)

Если получили - значит всё ок, vfio захватил видеокарту.

Вводим lspci -nnk -d 10de:21c4 и если видим - Kernel driver in use: vfio-pci - всё ок.

Дальнейшая настройка

Дальше в Proxmox через GUI в Hardware -> PCI Device -> Raw Device добавляем наше устройство. На самой виртуалке ставим драйвер видеокарты - ну а как иначе?

Решение проблем

probe of 0000:07:00.0 failed with error -22

Если видим ошибку в dmesg по типу:

[   46.901149] vfio-pci 0000:07:00.0: vgaarb: deactivate vga console
[   46.901154] vfio-pci 0000:07:00.0: vgaarb: VGA decodes changed: olddecodes=io+mem,decodes=io+mem:owns=io+mem
[   46.901171] vfio-pci: probe of 0000:07:00.0 failed with error -22
[   46.901177] vfio_pci: add [10de:21c4[ffffffff:ffffffff]] class 0x000000/00000000
[   46.901193] vfio-pci 0000:07:00.0: vgaarb: deactivate vga console
[   46.901195] vfio-pci 0000:07:00.0: vgaarb: VGA decodes changed: olddecodes=io+mem,decodes=io+mem:owns=io+mem
[   46.901201] vfio-pci: probe of 0000:07:00.0 failed with error -22
[   46.901253] vfio-pci: probe of 0000:07:00.1 failed with error -22
[   46.901257] vfio_pci: add [10de:1aeb[ffffffff:ffffffff]] class 0x000000/00000000

Значит vfio не смогу получить доступ к BAR-у карты.

Отключение фреимбуффера

Заранее скажу, что это очень редкая ситуация, но отключение фреимбуффера и драйверов дает эксклюзивный доступ vfio к видеокарте.

В файле /etc/default/grub в GRUB_CMDLINE_LINUX_DEFAULT добавляем следующие опции:

  • video=efifb:off и nomodeset отключаем фреимбуффер.
  • initcall_blacklist=simplefb_init - ещё один способ выключить фреимбуффер.

Еще помогает отключить модули для fb, для этого создаем файл /etc/modprobe.d/blacklist-fb.conf и добавляем:

blacklist simplefb
blacklist efifb
blacklist vesafb

Обновляем initramfs и перезагружаемся:

update-initramfs -u -k all
reboot now

Внимание! При отключении фреимбуффера, мы не сможем видеть при подключии HDMI-кабеля видеть изображение!

vfio-pci не смог захватить устройство

Получаем наше устройство: lspci -nnk -d 10de:21c4

Получим вывод в виде такого:

07:00.0 VGA compatible controller [0300]: NVIDIA Corporation TU116 [GeForce GTX 1660 SUPER] [10de:21c4] (rev a1)
        Subsystem: Gigabyte Technology Co., Ltd TU116 [GeForce GTX 1660 SUPER] [1458:4014]
        Kernel driver in use: vfio-pci
        Kernel modules: nvidiafb, nouveau

Если в Kernel driver in use у вас что-то другое кроме vfio-pci - vfio не смог захватить видеокарту и его захватил другой драйвер. Для решения, вернитесь на пункт с отключением драйверов видеокарты.

Если вместо сервера используется ноутбук

Есть смысл в GRUB опцию pcie_aspm=off - этот параметр управляет потреблением шин PCI-E. Если он включен, ответ устройства увеличивается из-за времени, которое затрачивается на переключение режимов шины.

Можно ли использовать 1 видеокарту сразу на нескольких устройствах?

Нет, это вам нужно смотреть в сторону vGPU. Но никто не мешает создать несколько ВМ и включать их по мере необходимости.

Ссылки

  • Есть похожая статья на Habr.
  • PCI Passthrough в вики проксмокса.
  • Хороший gist с некоторыми дополнительными пояснениями.