Все об Arch Linux

Arch Linux будет тем, что вы из него сделаете!

NVIDIA в VFIO: как передать GPU в виртуальную машину

Если после запуска Windows в виртуальной машине в диспетчере устройств появился новый видеоадаптер, значит, vfio-pci отработал ровно так, как нужно. Ниже — вся цепочка: что требуется от железа и ядра, как закрепить драйвер за конкретной картой, как подключить её в virt-manager и как проверить результат.

Зачем вообще передавать GPU в виртуальную машину?

Сценариев два.

Первый — Windows как гостевая ОС для игр, VR-шлемов и робототехники: SteamVR, Isaac Sim и экзоскелеты до сих пор рассчитывают на Windows-драйверы и DirectX. Вместо переустановки системы ради такого софта ты оставляешь хост Linux, а Windows живёт в VM с настоящей видеокартой вместо программного vga.

Второй — машинное обучение в изоляции. CUDA-контейнеры крутятся прямо в гостевой VM, связь с хостом даёт virtio-net. Вся вычислительная нагрузка ограничена границей виртуальной машины: если эксперимент уронил систему, откатываешь снапшот. Хост при этом продолжает считать своё.

Обе схемы уживаются с двумя картами в системе: одну отдаёшь гостю, вторую оставляешь хосту под Linux. Как развести две видеокарты, включая связку NVIDIA + AMDGPU в одном хосте, я разбирал в статье про multigpu: NVIDIA и AMDGPU одновременно.

Что нужно железу и ядру, чтобы GPU уехал в VM?

Три вещи: работающий IOMMU, правильная разбивка на IOMMU-группы и модуль vfio-pci.

IOMMU — процессорный блок трансляции адресов: на AMD это AMD-Vi, на Intel — VT-d. Без него vfio-pci не изолирует устройство, и libvirt вернёт ошибку про отсутствие поддержки. Проверь, что IOMMU поднялся:

dmesg | grep -iE 'AMD-Vi|Intel-IOMMU|IOMMU'

В параметры ядра добавляется amd_iommu=on либо intel_iommu=on, плюс iommu=pt — режим passthrough. Для NVIDIA iommu=pt не рекомендация, а условие корректной работы CUDA.

IOMMU-группы — минимальные единицы, между которыми IOMMU умеет изолировать друг от друга. Правило простое: в группу карты не должны попадать устройства, нужные хосту. Список групп смотрится так:

for g in /sys/kernel/iommu_groups/*/devices/*; do
  dev=$(basename "$g")
  printf '%s -> %s\n' "${g%/devices/*}" "$(lspci -nns "$dev")"
done | sort -V

Строки с одинаковым путём группы описывают девайсы одной группы. Если в одной строке с NVIDIA-картой стоит её HDMI-звук, это нормально: отдавай оба устройства в VM. Если рядом оказалась сетевая карта материнской платы — это уже проблема, о ней ниже.

vfio-pci — модуль ядра, который перехватывает PCI-устройства до того, как их увидит nvidia. Список кандидатов показывает lspci -k:

lspci -nnk | grep -A3 -iE 'VGA compatible|3D controller'

Ключевые поля: Kernel driver in use (кем устройство занято сейчас) и Kernel modules (какой модуль может его забрать).

Про isolcpus стоит знать отдельно: на проход GPU он не влияет, гостевой код ядро и так не исполняет в пользовательском пространстве. Если ты изолируешь ядра хостовых задач через isolcpus и nohz_full, эти параметры живут в той же строке GRUB_CMDLINE_LINUX_DEFAULT, что и iommu=pt.

Как закрепить vfio-pci за конкретной картой?

Сначала узнай ID устройства — его нужно указать драйверу по буквам, а не по имени:

lspci -nn | grep -i nvidia
# 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP104 [GeForce GTX 1080] [10de:1b80]
# 01:00.1 Audio device [0403]: NVIDIA Corporation GP104 High Definition Audio [10de:10f9]

Здесь два ID: 10de:1b80 — сама видеокарта, 10de:10f9 — её HDMI-звук. Создай /etc/modprobe.d/vfio.conf:

options vfio-pci ids=10de:1b80,10de:10f9 disable_vga=1
softdep nvidia pre: vfio-pci

Строка options vfio-pci ids=... говорит модулю забрать именно эти устройства. disable_vga=1 выключает legacy VGA framebuffer — с ней карта может не отпустить привязку к консоли. softdep nvidia pre: vfio-pci задаёт порядок загрузки: nvidia не встанет раньше, чем vfio-pci заберёт карту. Без этой строки на быстрых машинах гонка проигрывается непредсказуемо.

Дальше vfio-pci должен оказаться в initramfs, иначе после перезагрузки карта вернётся в хост. Открой /etc/mkinitcpio.conf и допиши модуль:

# в секции MODULES
MODULES=(vfio_pci vfio_iommu_type1)

На ядрах 5.16 и новее отдельный модуль vfio_virqfd больше не нужен, он слит с vfio-pci. Пересобери образ:

mkinitcpio -P

Порядок загрузки в initramfs важен: если модули nvidia собираются через DKMS и попадают туда же, проверь, что vfio идёт первым. Как вообще собрать модуль nvidia — статически или через DKMS — разобрано в статье про DKMS или статическое ядро. Параметры ядра про iommu=pt пропиши в /etc/kernel/cmdline или в /etc/default/grub, затем обнови конфиг GRUB — до перезагрузки ничего не применится.

Как отдать карту виртуальной машине?

В virt-manager открой нужную VM, выключи её, нажми «Показать XML» и найди секцию <devices>. Внутри добавь блок:

<hostdev mode='subsystem' type='pci' managed='yes'>
  <driver name='vfio-pci'/>
  <source>
    <address domain='0x0000' bus='0x01' function='0x0'/>
  </source>
</hostdev>

Значения bus и function бери из вывода lspci для своей карты. Атрибут managed='yes' разрешает libvirt самому привязать устройство к vfio. Секцию <rom bar='on'/> для NVIDIA обычно не добавляют: включение ROM BAR на некоторых картах приводит к зависанию гостя при старте.

Тот же результат даёт графический путь: «Оборудование» → «Добавить новое» → «PCI-устройство», в списке нужная карта, «Готово». Если машина ещё не создана, включи UEFI (OVMF) вместо устаревшего SeaBIOS — Windows 10 и 11 без него не заведутся.

Не правь XML у запущенной машины: после virsh start конфигурация гостя перезаписывается.

Как проверить, что vfio-pci забрал карту?

Главная проверка — одна строка lspci:

lspci -nnk -s 01:00.0
# Kernel driver in use: vfio-pci
# Kernel modules: vfio_pci nvidia

Значение vfio-pci вместо nvidia означает, что карта готова к передаче. В списке Kernel modules nvidia ещё присутствует — это нормально, просто vfio-pci забрал устройство раньше. Если в in use всё ещё nvidia, модуль не загрузился вовремя: проверь mkinitcpio -P и строку softdep.

Дополнительно:

dmesg | grep -iE 'vfio|IOMMU' | tail -20

В логе ожидаются строки о биндинге устройства к группе IOMMU. В virt-manager карта в списке PCI-устройств отображается с драйвером vfio.

В гостевой Windows открой диспетчер устройств, раздел «Видеоадаптеры» — там будет модель карты, а nvidia-smi покажет модель, драйвер и объём видеопамяти. Если nvidia-smi ругается на несовпадение версии драйвера и ядра гостя, обнови драйвер внутри VM. Как выбирать версию драйвера на стороне Arch Linux, я писал в статье про установку драйвера NVIDIA на Arch Linux: в хосте и в госте версии могут различаться, и это нормально.

Что делать, если Windows в VM не видит карту?

Разбирай по симптомам.

Видеоадаптер есть, но работает как «базовый дисплей» — установлен Microsoft Basic Display Adapter вместо NVIDIA. Скачай официальный установщик NVIDIA под Windows, запусти в гостевой VM и дождись перезагрузки.

Ошибка QEMU_ERROR «несовместимо с этим оборудованием», код 43 — гость не доверяет драйверу. Решение: отключить Secure Boot в UEFI VM либо включить тестовый режим подписи (bcdedit /set testsigning on в командной строке с правами администратора).

После выключения VM карта не возвращается хосту — классический reset bug. Встречается на Maxwell и Pascal (GTX 900, GTX 1000/XT, часть GTX 1080), то есть на картах старше Turing: QEMU не умеет корректно сбросить состояние, и следующий запуск падает с BAR 1: can't reserve region. Лечится модулем vfio-vendor-reset и ядром, собранным под него.

Ошибка «Memory: target too small» или сбой маппинга BAR — не хватило адресного пространства. Включи в UEFI хоста Above 4G Decoding, а гостю выдели больше оперативной памяти, чем он запрашивает.

Что делать, если IOMMU-группа захватила лишние устройства?

Ситуация: в группе с картой лежат ещё сетевой адаптер, ACPI-контроллер или устройство SMBus. Пробовать отдать всё это гостю — плохая идея, хост останется без сети.

Что можно сделать по порядку:

  1. Пересоберить IOMMU-группы в UEFI. В BIOS ищи IOMMU Group, ACS Enable или PCI Isolation — в зависимости от платы название отличается, но смысл один: дать процессору дробить таблицу DMAR на мелкие группы.
  2. Включить ACS override через параметр ядра dmar=0xfffff. Он заставляет IOMMU округлить DMAR-таблицу и разрезать группу на отдельные устройства. Скорость IOMMU при этом может заметно просесть, поэтому вариант годится для стенда, а не для рабочей машины.
  3. Отдать в VM всё, что делит группу с картой, включая звук и сетевку. Для игровой Windows-машины это даже полезно: звук и сеть гостя не зависят от хостовых драйверов.

Перед этими операциями сохрани текущую раскладку групп, чтобы вернуться назад перезагрузкой.

Частые вопросы

Нужно ли выключать Secure Boot в хосте?

Нет, если модули nvidia и vfio подписаны. Выключать приходится только ради установки неподписанных модулей — например, vfio-vendor-reset, собранного вручную.

Можно ли отдать в VM две видеокарты?

Технически да, но двум картам NVIDIA для игр нужны разные PCIe-корневые комплексы, а не соседние слоты на одной линии. Смешанная связка AMD + NVIDIA в одной VM проблемнее: требования к драйверам разные, и сбой одного гостя утащит за собой второй.

Как вернуть карту хосту?

Выключи VM, выполни lspci -nnk -s 01:00.0 и убедись, что в in use стоит nvidia. Если остался vfio-pci, помогает перезагрузка. Если карта не отдаётся и после неё, на картах с reset bug нужен полный сброс питания.

Нужен ли IOMMU, если в VM используется только CUDA?

Да. CUDA-передачи и unified memory работают через адресную трансляцию, и без IOMMU ядро гостя получает физические адреса хоста — это и нестабильно, и небезопасно.

Полезные ресурсы

  • ArchWiki: PCI passthrough via OVMF — полная последовательность настройки виртуальной машины с настоящей видеокартой.
  • ArchWiki: VFIO — параметры ядра, загрузка модулей и разбор проблем с IOMMU-группами.

Заключение

Проход NVIDIA в VM сводится к трём точкам: IOMMU включён, IOMMU-группа содержит только нужные устройства, vfio-pci забрал карту раньше nvidia. Проверяется всё одной командой lspci -nnk. Дальше остаётся подключить <hostdev> в XML гостя, поставить в VM драйвер и держать в голове reset bug на старых картах.



Не получилось? Поможем настроить

Задай вопрос в чате — отвечаем быстро, по делу и без воды.

Читайте также

Комментарии

Загрузка…

Откроется GitHub: создайте новый issue с вашим комментарием (кнопка «Submit new issue»). После отправки обновите эту страницу — комментарий появится ниже.

Telegram Max