Якщо ви колись пробували PCI passthrough на Proxmox і зіштовхувалися з повідомленням «device is in use», ви зустріли ядро Linux, що виконує свою роботу: захищає пристрій, який досі належить комусь іншому. Проблема в тому, що «хтось інший» часто невидим — ранній framebuffer під час завантаження, ввічлива аудіофункція на GPU, драйвер сховища, правило udev або сам Proxmox, який намагається допомогти.
У продакшні: ви не хочете «просто перезавантажити і пощастить». Вам потрібен детермінований контроль над тим, хто володіє PCI-функцією, коли її заявляють та як довести, що вона від’єднана перед тим, як передати її в VM. Ось як це зробити без забобонів.
Практична модель: що насправді означає «device is in use»
Коли Proxmox (QEMU) каже, що PCI-пристрій «in use», це не метафора. Це повідомлення про те, що щось на хості наразі тримає пристрій відкритим, має прив’язку драйвера або іншим способом перешкоджає QEMU безпечно взяти керування через VFIO.
В Linux PCI-функція переважно «належить» через driver binding. Якщо до неї прив’язані драйвери як nouveau, amdgpu, nvidia, xhci_hcd, ixgbe, megaraid_sas тощо, ядро правильно вважає, що пристрій — частина хоста. VFIO потребує, щоб пристрій був прив’язаний до vfio-pci (іноді разом з пристосуваннями для скидання), щоб QEMU міг відобразити його в гостя.
Але прив’язка — лише початок. Ви можете відв’язати пристрій і все одно мати процес, який його чіпає через sysfs, консольний framebuffer, або аудіофункцію, яку використовує PulseAudio і про яку ви забули. І Proxmox додає ще один поворот: він — гіпервізор, що прагне допомогти. Якщо хост вважає GPU доступним, щось може його захопити під час завантаження (framebuffer, DRM), і тоді ви ведете переговори з рухомою ціллю.
Отже, мета проста і жорстка:
- Пристрій (і будь-які функції, що мають йти разом з ним) знаходяться в ізольованій IOMMU-групі або ви приймаєте компроміс безпеки з ACS override.
- Хост ніколи не прив’язує до нього драйвер, відмінний від VFIO, під час завантаження.
- Пристрій має належну підтримку скидання, щоб пережити перезапуски VM і зупинки.
- QEMU отримує виключний доступ через VFIO щоразу, без ручної «рулетки unbind».
Холодна правда: VFIO passthrough — це менше «функція під ключ» і більше «контракт, який ви забезпечуєте».
Швидкий алгоритм діагностики (перший/другий/третій)
Це порядок дій, який швидко виявляє вузьке місце в продакшні, коли немає часу вишукувати журнали ядра.
Перший: доведіть, хто зараз володіє пристроєм
- Знайдіть PCI-адресу (bus:slot.func) і подивіться, який драйвер прив’язаний.
- Підтвердіть, чи завантажені модулі VFIO.
- Перевірте, чи інша функція в тому самому multi-function пристрої не прив’язана до драйвера хоста.
Другий: перевірте IOMMU-групування і чи не конфліктуєте ви з платформою
- Якщо пристрій ділить IOMMU-групу з чимось, що потрібно хосту (наприклад, SATA-контролер з root-файловою системою), зупиніться і переробіть архітектуру.
- Якщо ви використовували ACS override, ставте це як виключення безпеки, а не як доказ успіху.
Третій: пошукайте ранніх претендентів і «невидимих» користувачів
- Framebuffer/DRM (поширено для GPU).
- udev, який автозавантажує драйвери за modalias.
- systemd-сервіси (display manager, persistence daemons).
- Залишкові модулі ядра в initramfs.
Якщо робити ці три кроки послідовно, «device is in use» перетворюється з атмосфери на розв’язне питання.
Цікаві факти та історичний контекст (чому VFIO дивний)
- VFIO не був першою спробою. До того, як VFIO став стандартом, часто використовували
pci-stubдля «паркування» пристроїв від драйверів хоста. - IOMMU — це не лише для віртуалізації. Він існує для ізоляції DMA, захищаючи пам’ять від пристроїв, які можуть bus-master — корисно для безпеки та надійності навіть без VM.
- PCIe-пристрої можуть виконувати DMA без дозволу. Ось чому пристрій у невірній IOMMU-групі — це не лише незручність; це проблема меж.
- ACS (Access Control Services) — це апаратна функція, а не настрій ядра. Коли платформи не експонують ACS коректно, Linux не завжди може чітко розбити групи.
- Multi-function пристрої — класична пастка. GPU часто з’являються як VGA + HDMI audio (і інколи USB-C контролер). Передавати лише одну функцію — означає чекати конфліктів.
- Підтримка скидання нерівномірна. Деякі споживчі GPU відомі тим, що не скидаються чисто між перезавантаженнями гостя без додаткових хитрощів.
- Ранні графічні функції під час завантаження — річ. Linux framebuffer і DRM KMS стартують рано. Якщо вони захопили GPU, ви вже запізнилися, якщо не зробили прив’язку до VFIO в initramfs.
- «Device is in use» часто — блокування на рівні ядра, а не файлове блокування. Інструменти на кшталт
lsofможуть бути марними, бо «користувач» — це сам драйвер ядра. - Віртуалізаційні стеки еволюціонували під потреби QEMU. Дизайн VFIO відповідає моделі QEMU/KVM: простір користувача отримує безпечний, контрольований доступ, а ядро забезпечує DMA-ізоляцію.
Цитата, що прижилася серед SRE: Hope is not a strategy
— загальний принцип операцій. Ставте його як SRE-принцип, а не як декор. (перефразований вислів)
Практичні завдання: команди, виводи та рішення (12+)
Ось кроки, які реально відповідають на питання «хто його використовує?» і «що робити далі?» Кожне завдання включає: команду, що означає вивід, і рішення.
Завдання 1: ідентифікуйте пристрій і поточну прив’язку драйвера
cr0x@server:~$ lspci -nnk -d 10de:2684
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:2684] (rev a1)
Subsystem: ASUSTeK Computer Inc. Device [1043:88aa]
Kernel driver in use: nouveau
Kernel modules: nouveau, nvidia_drm, nvidia
Значення: GPU прив’язаний до nouveau. VFIO не зможе взяти його, поки графічний драйвер володіє ним.
Рішення: Потрібно заборонити хосту прив’язувати nouveau/nvidia і прив’язати до vfio-pci натомість (ідеально — в initramfs).
Завдання 2: підтвердьте, що модулі VFIO завантажені
cr0x@server:~$ lsmod | egrep 'vfio|kvm'
vfio_pci 16384 0
vfio_pci_core 73728 1 vfio_pci
vffo_iommu_type1 40960 0
vfio 45056 2 vfio_pci_core,vfio_iommu_type1
kvm_intel 397312 0
kvm 1036288 1 kvm_intel
Значення: VFIO і KVM присутні. Це необхідно, але не достатньо.
Рішення: Якщо модулі VFIO відсутні, виправте це спочатку. Інакше переходьте до питань прив’язки/IOMMU.
Завдання 3: перевірте, чи IOMMU увімкнено на рівні ядра
cr0x@server:~$ dmesg | egrep -i 'iommu|dmari|remapping' | head -n 8
[ 0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-6.8.12-4-pve root=ZFS=rpool/ROOT/pve-1 ro quiet intel_iommu=on iommu=pt
[ 0.412345] DMAR: IOMMU enabled
[ 0.412901] DMAR: Intel(R) Virtualization Technology for Directed I/O
[ 0.413112] DMAR: Interrupt remapping enabled
[ 0.419876] pci 0000:00:00.0: DMAR: Skip IOMMU disabling for graphics
Значення: IOMMU активований і включено перенаправлення переривань (добре для стабільності та безпеки).
Рішення: Якщо ви не бачите IOMMU увімкненим, зупиніться і виправте налаштування BIOS та параметри ядра перед тим, як щось змінювати.
Завдання 4: перевірте членство в IOMMU-групі
cr0x@server:~$ for g in /sys/kernel/iommu_groups/*; do echo "Group $(basename "$g")"; ls -1 "$g/devices"; done | sed -n '1,24p'
Group 0
0000:00:00.0
0000:00:01.0
Group 10
0000:01:00.0
0000:01:00.1
Group 11
0000:02:00.0
Значення: GPU (01:00.0) і його аудіофункція (01:00.1) ділять групу. Це нормально; зазвичай передають обидві.
Рішення: Якщо ваш цільовий пристрій ділить групу з критичною для хоста частиною (наприклад, NVMe, на якому лежить пул), не продовжуйте passthrough на такій платформі.
Завдання 5: подивіться, що прив’язано через sysfs (авторитетно)
cr0x@server:~$ readlink -f /sys/bus/pci/devices/0000:01:00.0/driver
/sys/bus/pci/drivers/nouveau
Значення: Прив’язка ядра — nouveau, незалежно від того, що ви думаєте про свою конфігурацію.
Рішення: Потрібно заборонити хосту прив’язувати його під час завантаження (blacklist + initramfs + vfio-pci ids).
Завдання 6: дізнайтеся, чи пристрій «зайнятий» через консоль/DRM (GPU)
cr0x@server:~$ dmesg | egrep -i 'drm|framebuffer|fb0|efi fb|simpledrm' | head -n 12
[ 0.623456] simpledrm: initialized
[ 1.912345] nouveau 0000:01:00.0: DRM: VRAM: 8192 MiB
[ 1.934567] fb0: switching to nouveaufb from simple
Значення: GPU став framebuffer-пристроєм (fb0). Це класичний «якір» для статусу «in use».
Рішення: Забезпечте, щоб хост ніколи не використовував його для консольної графіки: прив’яжіть до vfio-pci в initramfs; часто вимикають framebuffer handoff там, де це доречно.
Завдання 7: перевірте, чи хост-сервіс утримує графічні драйвери
cr0x@server:~$ systemctl status nvidia-persistenced --no-pager
● nvidia-persistenced.service - NVIDIA Persistence Daemon
Loaded: loaded (/lib/systemd/system/nvidia-persistenced.service; enabled)
Active: active (running) since Fri 2025-12-26 08:11:12 UTC; 3h 14min ago
Main PID: 2211 (nvidia-persiste)
Tasks: 1
Memory: 1.8M
CPU: 2.013s
Значення: Хост активно керує стеком NVIDIA, що буде протистояти вашому passthrough.
Рішення: Вимкніть його (і розвантажте драйвери), якщо GPU призначений для VM.
Завдання 8: помітити модулі ядра, які знову захоплять пристрій після unbind
cr0x@server:~$ modprobe --showconfig | egrep -n 'blacklist (nouveau|amdgpu|nvidia|radeon)' | head
412:blacklist nouveau
Значення: Існує blacklist для nouveau, але це не гарантує, що він є в initramfs або що альтернативи не прив’яжуться.
Рішення: Переконайтеся, що blacklist застосовано й потім перебудуйте initramfs, щоб раннє завантаження його врахувало.
Завдання 9: перевірте вміст initramfs на предмет «неправильних» драйверів
cr0x@server:~$ lsinitramfs /boot/initrd.img-6.8.12-4-pve | egrep 'nouveau|amdgpu|nvidia' | head
usr/lib/modules/6.8.12-4-pve/kernel/drivers/gpu/drm/nouveau/nouveau.ko
usr/lib/modules/6.8.12-4-pve/kernel/drivers/gpu/drm/drm.ko
Значення: Модуль знаходиться всередині initramfs, тож він може завантажитись ще до застосування конфігів з root-файлової системи.
Рішення: Видаліть його з initramfs через належну конфігурацію (blacklist в контексті initramfs) і згенеруйте initramfs заново.
Завдання 10: прив’яжіть пристрій до vfio-pci за допомогою driver_override (точково, у runtime)
cr0x@server:~$ echo vfio-pci | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver_override
vfio-pci
cr0x@server:~$ echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/nouveau/unbind
0000:01:00.0
cr0x@server:~$ echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/vfio-pci/bind
0000:01:00.0
Значення: Ви примусово перев’язали пристрій без перезавантаження. Це корисно для тестування та аварійного відновлення.
Рішення: Якщо це «працює», не зупиняйтеся на цьому — зробіть це постійним при завантаженні. Runtime bindings — чудовий спосіб забути, що ви змінили.
Завдання 11: підтвердьте, що прив’язка тепер VFIO
cr0x@server:~$ lspci -nnk -s 01:00.0
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:2684] (rev a1)
Subsystem: ASUSTeK Computer Inc. Device [1043:88aa]
Kernel driver in use: vfio-pci
Kernel modules: nouveau, nvidia_drm, nvidia
Значення: vfio-pci тепер володіє пристроєм. Перелічені Kernel modules — «доступні», а не обов’язково завантажені.
Рішення: Переконайтесь, що інші функції (01:00.1 тощо) теж прив’язані до VFIO, а потім запускайте VM.
Завдання 12: перевірте аудіофункцію (бо GPU підступні)
cr0x@server:~$ lspci -nnk -s 01:00.1
01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:22ba] (rev a1)
Subsystem: ASUSTeK Computer Inc. Device [1043:88aa]
Kernel driver in use: snd_hda_intel
Kernel modules: snd_hda_intel
Значення: Аудіофункція GPU все ще прив’язана до хоста. QEMU може зазнати проблем, і ви отримаєте «in use» або нестабільність гостя.
Рішення: Прив’яжіть 01:00.1 до VFIO також, або передайте її явно, якщо конфіг VM цього очікує. Розглядайте функції GPU як набір.
Завдання 13: знайти «утримувачів» з перспективи драйвера (хто залежить від нього)
cr0x@server:~$ sudo lsof /dev/nvidia0 2>/dev/null | head
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Xorg 1880 root 15u CHR 195,0 0t0 331 /dev/nvidia0
Значення: Якщо ви використовуєте пропрієтарний стек NVIDIA, процеси можуть тримати вузли пристрою відкритими.
Рішення: Зупиніть сервіс (display manager, Xorg, persistence daemon) перед відв’язкою. Якщо ви без монітора, не встановлюйте десктопні стеки на хості, призначеному для passthrough, якщо немає реальної потреби.
Завдання 14: простежте, що каже QEMU/Proxmox при невдачі запуску
cr0x@server:~$ journalctl -u pvedaemon -u pveproxy -u pvestatd -u pve-firewall -u pve-ha-lrm -u pve-ha-crm --since "10 minutes ago" --no-pager | tail -n 12
Dec 26 11:20:41 server pvedaemon[1552]: start VM 120: UPID:server:00003A1B:0001D2A1:676D...:qmstart:120:root@pam:
Dec 26 11:20:42 server pvedaemon[1552]: VM 120 qmp command failed - unable to open /dev/vfio/10: Device or resource busy
Dec 26 11:20:42 server pvedaemon[1552]: start failed: QEMU exited with code 1
Значення: Це вказує на те, що вузол IOMMU-групи (/dev/vfio/10) зайнятий. Хтось інший тримає цю групу відкритою.
Рішення: Пошукайте іншу VM або процес, що використовує ту саму IOMMU-групу; підтвердіть, що немає застарілого процесу QEMU; перевірте, що всі функції групи передані послідовно.
Жарт №1: VFIO як система спільних столів в офісі: якщо хтось лишив свою чашку на місці, технічно ви не можете сісти.
Від’єднання пристроїв від хоста: правильно, а не пощастить
Крок 0: вирішіть, чи хост взагалі повинен використовувати пристрій
Існують два легітимні режими роботи:
- Присвячений passthrough-пристрій: хост ніколи не повинен його використовувати. Прив’яжіть до VFIO під час завантаження. Це здоровий режим для GPU, USB-контролерів та NIC, призначених для гостей.
- Пристрій для випадкового passthrough: хост іноді його використовує, іноді передає. Це крихке і провокує «device is in use», бо ви робите runtime unbind/rebind. Використовуйте лише коли обладнання обмежене і ви любите ризик.
Крок 1: підтвердіть, що платформа підтримує чисту ізоляцію
IOMMU-групова ізоляція — не «приємна опція». Це межа, яка запобігає DMA-записам пристроїв у пам’ять інших пристроїв. У суворому середовищі ви передаєте цілі групи. У практичному — принаймні розумієте, що віддаєте взамін.
Якщо ваш пристрій ділить IOMMU-групу з критичним для хоста пристроєм, варіанти:
- Перемістіть картку в інший слот (інший root port, інше групування).
- Змініть налаштування платформи (іноді «Above 4G decoding», «Resizable BAR» впливають на групування опосередковано; іноді ні).
- Використайте іншу материнську плату/генерацію CPU, яка коректно експонує ACS.
- Використайте ACS override patch/параметр модуля (останній засіб; застереження щодо безпеки і стабільності).
Крок 2: зробіть прив’язку VFIO постійною (те, що люди роблять напівправильно)
У Proxmox чистий підхід: встановити параметри ядра для IOMMU, завантажити модулі VFIO і налаштувати vfio-pci, щоб він захоплював ID пристроїв рано.
2a) Переконайтесь, що є параметри ядра для IOMMU (приклад для Intel):
cr0x@server:~$ cat /etc/default/grub | egrep 'GRUB_CMDLINE_LINUX_DEFAULT'
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"
Значення: Параметри ядра включають IOMMU і режим pass-through для продуктивності (iommu=pt) при збереженні можливості ізоляції VFIO.
Рішення: Якщо відсутні, додайте потрібні параметри, потім запустіть update-grub і перезавантажтеся.
cr0x@server:~$ sudo update-grub
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.8.12-4-pve
Found initrd image: /boot/initrd.img-6.8.12-4-pve
done
2b) Завантажуйте модулі VFIO під час старту:
cr0x@server:~$ cat /etc/modules | egrep '^vfio'
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
Значення: Ядрові модулі VFIO будуть доступні рано.
Рішення: Якщо їх немає, додайте і перебудуйте initramfs, щоб раннє завантаження їх бачив.
2c) Прив’язка за vendor:device ID через vfio-pci
Створіть конфіг modprobe, який інструктує vfio-pci захоплювати ID пристроїв. Приклад для GPU і його аудіофункції:
cr0x@server:~$ cat /etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:2684,10de:22ba disable_vga=1
Значення: При завантаженні vfio-pci прив’яже ці ID. disable_vga=1 допомагає уникнути VGA-арбітражних конфліктів на деяких системах.
Рішення: Якщо у вас кілька ідентичних GPU, розгляньте використання driver_override по PCI-адресі замість цього, щоб не захопити невірну картку.
2d) Заблокуйте конфліктні драйвери хоста (включно для initramfs)
cr0x@server:~$ cat /etc/modprobe.d/blacklist-gpu.conf
blacklist nouveau
blacklist nvidia
blacklist nvidiafb
blacklist rivafb
Значення: Ці модулі не повинні автоматично завантажуватись.
Рішення: Якщо після перезавантаження вони все ще в lsmod, вони підтягуються через initramfs або залежності; виправте initramfs наступним кроком.
2e) Перебудова initramfs (де більшість історій «я забанив його» помирає)
cr0x@server:~$ sudo update-initramfs -u -k all
update-initramfs: Generating /boot/initrd.img-6.8.12-4-pve
Значення: Новий initramfs міститиме вашу конфігурацію VFIO і blacklist.
Рішення: Перезавантажтеся і перевірте прив’язки. Якщо усе ще неправильно, перевірте інші модулі на кшталт simpledrm і налаштування консолі.
Крок 3: чисте від’єднання в runtime (якщо потрібно)
Іноді ви вже в інциденті і перезавантаження дорогі. Runtime detach можливий, але це контрольована процедура, а не випадкове ехо в sysfs поки система перестане верещати.
Порядок операцій для runtime detach:
- Зупиніть процеси/сервіси, які можуть торкатися пристрою (display manager, persistence daemons, сервіси сховища).
- Відв’яжіть усі PCI-функції, які будуть передані.
- Встановіть
driver_overrideвvfio-pci, щоб уникнути негайного повторного прив’язання до старого драйвера. - Прив’яжіть до
vfio-pci. - Запустіть VM.
Приклад для VGA-функції GPU + аудіо:
cr0x@server:~$ sudo systemctl stop nvidia-persistenced
cr0x@server:~$ echo vfio-pci | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver_override
vfio-pci
cr0x@server:~$ echo vfio-pci | sudo tee /sys/bus/pci/devices/0000:01:00.1/driver_override
vfio-pci
cr0x@server:~$ echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/nouveau/unbind
0000:01:00.0
cr0x@server:~$ echo 0000:01:00.1 | sudo tee /sys/bus/pci/drivers/snd_hda_intel/unbind
0000:01:00.1
cr0x@server:~$ echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/vfio-pci/bind
0000:01:00.0
cr0x@server:~$ echo 0000:01:00.1 | sudo tee /sys/bus/pci/drivers/vfio-pci/bind
0000:01:00.1
Що може піти не так: старий драйвер миттєво перев’язується; framebuffer тримає доступ; пристрій не скидається; або ви від’єднуєте NIC, що тримає ваш SSH-сеанс (таке трапляється).
Жарт №2: Відв’язування управляючого NIC по SSH — чудовий спосіб дізнатися, наскільки ви довіряєте вашому OOB-консолю.
Крок 4: підтвердьте, що Proxmox передає всю історію, а не половину
У конфігах VM Proxmox зазвичай використовують записи hostpciX. Кількість проблем, спричинених передачею лише VGA-функції і забуттям аудіо, USB-C або bridge-функції, вражає печально.
Перевірте конфіг VM:
cr0x@server:~$ sudo cat /etc/pve/qemu-server/120.conf
agent: 1
bios: ovmf
machine: q35
memory: 16384
name: win11-gpu
ostype: win11
scsihw: virtio-scsi-single
hostpci0: 0000:01:00.0,pcie=1,x-vga=1
hostpci1: 0000:01:00.1,pcie=1
Значення: Передані обидві функції; OVMF + Q35 зазвичай — правильна база для сучасного GPU passthrough.
Рішення: Якщо ви передаєте лише 01:00.0, виправте це. Якщо IOMMU-група включає додаткові функції (наприклад, 01:00.2), передайте їх також або перегляньте придатність пристрою.
Найпоширеніші причини відмов при passthrough GPU
1) Консоль хоста використовує GPU (DRM/KMS)
Це найпоширеніша коренева причина «in use». Якщо GPU подає консольний вивід, DRM-драйвер ядра не відпускає його ввічливо. Іноді можна відв’язати, але іноді отримаєте чорну консоль, завислий драйвер або напівскинутий пристрій.
Порада для продакшну: використовуйте дешевий другий GPU (або вбудовану графіку) для хоста та присвятіть passthrough-GPU VFIO з першої мілісекунди завантаження.
2) Ви передали VGA-функцію, але не аудіофункцію
Багато GPU — це два пристрої, з’єднані разом: VGA і аудіо. Вони часто знаходяться в тій же IOMMU-групі, бо ділять ресурси. Якщо ви передаєте одну і не передаєте іншу, хост залишає частину, а VM отримує іншу. Це не спільний доступ; це суперечка про опіку.
3) Проблеми зі скиданням: GPU не повертається після зупинки/перезапуску VM
Деякі пристрої не реалізують належне Function Level Reset (FLR), або платформа не маршрутизовує скидання коректно. Симптоми:
- Перший запуск VM проходить, другий запуск не вдається з «device is in use» або гість бачить Code 43 / помилку пристрою.
- Журнали хоста показують застряглий пристрій або VFIO не може його ініціалізувати повторно.
Що робити:
- Переконайтесь, що передаєте всі функції.
- Перевірте, чи пристрій підтримує скидання; деякі вимагають vendor-специфічних порад.
- У найгіршому випадку лише перезавантаження хоста реально скидає пристрій. Плануйте вікна змін і резерви відповідно.
4) Неправильний GPU був захоплений vfio-pci
Прив’язка за device ID (ids=) — зручна. Вона також груба. Якщо у вас два однакові GPU, хост може прив’язати обидва до VFIO і ви втратите консоль, або ви прив’яжете не ту картку і дивуєтеся, чому VM не бачить ту, що встановили «вчора».
Переважний підхід у системі з кількома GPU: прив’язуйте за PCI-адресою використовуючи driver_override в ранніх скриптах завантаження, або тримайте пристрої хоста іншого класу, щоб можна було точно прив’язувати по ID без побічних ефектів.
HBAs, USB-контролери, NIC: різні класи пристроїв, різні пастки
Передача HBA (SAS/SATA контролер)
HBA зазвичай відмінно підходять для passthrough, бо поводяться як «чесні» PCI-пристрої і не потребують графічної нісенітниці. Пастка простіша: ви випадково намагаєтесь передати контролер, що забезпечує сховище хоста. Linux заперечить, і якщо ви примусите, хост перестане бути хостом.
Завдання: підтвердіть, які блок-пристрої на контролері, який ви збираєтесь витягти.
cr0x@server:~$ lsblk -o NAME,MODEL,SERIAL,HCTL,TYPE,SIZE | head -n 15
NAME MODEL SERIAL HCTL TYPE SIZE
sda ST12000NM0007 ZHZ0AAAA 1:0:0:0 disk 10.9T
sdb ST12000NM0007 ZHZ0BBBB 1:0:1:0 disk 10.9T
nvme0n1 Samsung SSD S6E... - disk 1.8T
zd0 - - - disk 50G
Значення: HCTL вказує на диски на SCSI host (ймовірно HBA). Якщо цей HBA — ваш ZFS пул, це не кандидат для passthrough, якщо хост і далі буде керувати цим пулом.
Рішення: Передавайте лише HBA, що не містять критичного сховища Proxmox, або приймайте архітектуру storage-VM і проектуйте відповідно.
Передача USB-контролера
USB-контролери популярні, бо дають «реальну» USB-поведінку (донгли, VR-гарнітури, UPS). Режим відмов — IOMMU-групування: ваш USB-контролер ділить групу з іншими чипсет-пристроями, що вам потрібні. Або ви віддаєте контролер і втрачаєте клавіатуру, яку треба було для виправлення. Комедія необов’язкова.
Завдання: ідентифікуйте контролер і групу.
cr0x@server:~$ lspci -nn | grep -i usb
00:14.0 USB controller [0c03]: Intel Corporation Device [8086:7ae0] (rev 11)
cr0x@server:~$ readlink /sys/bus/pci/devices/0000:00:14.0/iommu_group
../../../../kernel/iommu_groups/2
Значення: Група 2 містить USB-контролер. Тепер потрібно перевірити, що ще в групі 2.
Рішення: Якщо група 2 включає інші елементи чипсету, необхідні хосту, не передавайте її. Додайте окрему USB-контролер-картку замість цього.
Передача NIC
NIC passthrough чудово підходить для спеціальних навантажень (firewall, DPDK, низька затримка). Це також спосіб відсікати власну гілку, якщо ви передаєте інтерфейс управління. Завжди майте OOB-доступ (IPMI/iKVM) перед експериментами з передачою NIC.
Завдання: зіставити PCI-адресу NIC з іменем інтерфейсу в Linux і переконатися, що це не ваш шлях управління.
cr0x@server:~$ sudo lshw -class network -businfo | head -n 12
Bus info Device Class Description
pci@0000:03:00.0 eno1 network Ethernet controller
pci@0000:04:00.0 enp4s0 network Ethernet controller
Значення: Тепер ви знаєте, яка PCI-функція відповідає якому інтерфейсу.
Рішення: Не передавайте інтерфейс, через який йде ваш поточний SSH-сеанс, якщо у вас немає протестованого OOB-шляху і плану відкату.
Три корпоративні міні-історії (як це ламається в реальному житті)
Інцидент: неправильне уявлення про «blacklisting»
Середня компанія мігрувала кілька важких обчислювальних навантажень у кластер Proxmox. На одному вузлі була запасна GPU для Windows VM, що працювала з апаратним ключем ліцензії і деяким рендерингом. Інженер зробив те, що роблять всі спочатку: заблокував nouveau, встановив vfio-pci IDs, перезавантажився і відсвяткував, коли lspci показав VFIO.
Через два тижні після оновлення ядра VM перестала запускатися. Proxmox повідомляв, що GPU «in use». Інженер припустив, що конфіг «мабуть відкотився» і перевстановив його. Нічого не допомогло. Вони відкотили ядро. Працювало. Вони звинуватили нове ядро і відкрили тікет.
Справжня причина була банальною: оновлений initramfs знову містив GPU-драйвер і завантажував його рано. Файл blacklist існував, але він не застосовувався в контексті initramfs через спосіб його генерації. GPU був захоплений до того, як Proxmox отримав шанс. Перезавантаження ховалося проблему до зміни таймінгу завантаження від оновлення.
Виправлення не було героїчним. Вони стандартизували: перевіряти наявність модулів у initramfs, регенерувати initramfs після змін і перевіряти прив’язки після кожного оновлення ядра. Більше не було «вчора працювало» суперечок. VM знову стала нудною, а це правильно для інфраструктури.
Оптимізація, що обернулася проти: runtime rebind щоб уникнути перезавантажень
Інша організація мала одну GPU в вузлі, яка іноді обслуговувала локальну панель моніторингу, а іноді передавалась у VM для коротких ML-експериментів. Вони не хотіли перезавантажень, бо вузол також хостив інші VM. Тому вони написали скрипт: зупинити display manager, unbind GPU, bind до VFIO, запустити VM; потім назад.
В тестах воно працювало. Потім у реальному використанні: VM падала, перезапускалась швидко, і GPU не скидався чисто. Скрипт змушував «переприв’язати» пристрій, але апарат залишався в поганому стані. Іноді хост зависав при перезавантаженні DRM-драйвера. Іноді VM стартувала, але бачила зламану GPU. Іноді Proxmox відмовляв з «device is in use», бо застарілий процес QEMU тримав вузол групи відкритим на кілька секунд довше, ніж очікувалось.
Постмортем був болісним і передбачуваним: runtime rebinding — податок на надійність. Вони купили дешеву низькопотужну GPU для консолі хоста і присвятили кращу GPU лише для passthrough. Раптово не було скриптів, танців і таємниць. «Оптимізація» уникання витрат на обладнання коштувала часу інженерів і бюджету інцидентів.
Нудна, але правильна практика, що врятувала день: валідація на рівні груп перед змінами
Фінансова команда експлуатувала Proxmox з суворим контролем змін. У них був пункт у чеклисті, що дратував усіх: перед додаванням або зміною будь-якого hostpci запису вони фіксували повне членство IOMMU-групи і перевіряли, чи нічого не змінилося після оновлень прошивки.
Якось кварталом оновлення BIOS змінило поведінку PCIe bifurcation на частині вузлів. IOMMU-група GPU почала включати upstream bridge і USB-контролер, які раніше були окремо. На вузлах, що оновились першими, passthrough почав ламатися з «device is in use» і, гірше, з періодичними проблемами USB на хості.
Оскільки команда мала знімки груп «до/після» як частину тікету змін, діагностика зайняла хвилини. Вони не витрачали години на відв’язку драйверів і звинувачення Proxmox. Вони відкотили BIOS на уражених вузлах, запланували зміну розташування карток і запобігли масштабнішому інциденту. Усі ненавиділи чекліст, поки він не виправдав себе — так працює надійна експлуатація.
Поширені помилки: симптом → корінь → виправлення
1) Симптом: «device is in use» одразу при старті VM, завжди
Корінь: Драйвер хоста прив’язаний (GPU драйвер, USB драйвер, NIC драйвер) замість vfio-pci.
Виправлення: Перевірте за допомогою lspci -nnk і readlink /sys/bus/pci/devices/.../driver. Налаштуйте vfio-pci ids=, заблокуйте конфліктні драйвери, перебудуйте initramfs, перезавантажтеся.
2) Симптом: працює після ручного unbind, але після перезавантаження знову ламається
Корінь: Зміни не застосовуються достатньо рано; initramfs завантажує драйвер до того, як конфіг rootfs стане активним.
Виправлення: Перевірте lsinitramfs на наявність проблемного модуля, оновіть initramfs, підтвердіть, що модулі VFIO включені рано.
3) Симптом: GPU передано, але гість не має виводу або драйвер повідомляє про помилки
Корінь: Відсутні супутні функції (audio, USB-C), неправильне firmware (SeaBIOS vs OVMF) або невідповідність x-vga.
Виправлення: Передайте всі функції в IOMMU-групі; використовуйте OVMF + Q35 для сучасних GPU; додайте x-vga=1, коли потрібно.
4) Симптом: перший старт VM працює, другий старт не вдається без перезавантаження хоста
Корінь: Проблеми зі скиданням пристрою (немає FLR, баг у маршрутизації скидання платформи) або застаріле утримання групи залишковим процесом QEMU.
Виправлення: Переконайтеся, що немає залишкових процесів QEMU; перевірте журнали; розгляньте повне перезавантаження хоста як єдиний надійний спосіб скидання. Для критичних задач обирайте серверні GPU або пристрої з відомою підтримкою reset.
5) Симптом: «/dev/vfio/X: Device or resource busy»
Корінь: Інший процес/VM тримає вузол IOMMU-групи відкритим, часто тому, що ви розділили функції групи між VM.
Виправлення: Переконайтесь, що не розділяєте групу між VM; зупиніть іншу VM; перевірте членство групи.
6) Симптом: відключення мережі відразу після прив’язки NIC до VFIO
Корінь: Ви передали управляючий NIC, або systemd-networkd/ifupdown втратили інтерфейс.
Виправлення: Використайте OOB-доступ, поверніть прив’язку, переробіть архітектуру з виділеним passthrough NIC і окремим управляючим NIC або bond-ом.
7) Симптом: хост зависає, коли ви відв’язуєте GPU-драйвер
Корінь: GPU все ще використовується як активний консольний framebuffer або художником/композитором.
Виправлення: Не робіть runtime GPU detach на хості, що використовує цей GPU для консолі. Використайте інший GPU для хост-графіки або працюйте без голої консолі.
Контрольні списки / покроковий план
Чекліст A: Присвячений passthrough-пристрій (рекомендовано)
- Виберіть пристрій і перелічіть всі функції (
lspci -nnдля 01:00.0, 01:00.1 тощо). - Перевірте IOMMU-групи і підтвердьте, що ізоляція групи прийнятна.
- Увімкніть IOMMU у BIOS і параметрах ядра (
intel_iommu=onабоamd_iommu=on). - Завантажте модулі VFIO рано через
/etc/modules. - Прив’яжіть ID пристрою до vfio-pci використовуючи
/etc/modprobe.d/vfio.conf. - Заблокуйте конфліктні драйвери хоста, які можуть захопити пристрій.
- Перебудуйте initramfs і перезавантажтеся.
- Після завантаження перевірте прив’язки за допомогою
lspci -nnkі шляху драйвера в sysfs. - Налаштуйте VM так, щоб передати всі необхідні функції; для GPU використовуйте OVMF/Q35.
- Протестуйте цикли зупинки/запуску (принаймні 5 циклів), щоб виявити проблеми зі скиданням раніше, ніж користувачі.
Чекліст B: Runtime detach (використовуйте рідко)
- Підтвердіть, що маєте консольний доступ на випадок втрати пристрою (особливо для NIC).
- Зупиніть сервіси, які можуть торкатися пристрою (display manager, persistence daemons, сервіси сховища).
- Відв’яжіть усі функції, які будуть передані.
- Встановіть
driver_overrideвvfio-pci, щоб уникнути негайного повторного прив’язання. - Прив’яжіть до
vfio-pci. - Запустіть VM і підтвердіть, що власність
/dev/vfio/*коректна. - Після завершення обережно відновіть: зупиніть VM, відв’яжіть від VFIO, очистіть
driver_override, прив’яжіть назад до драйвера хоста, перезапустіть сервіси.
План відкату (запишіть його перед початком)
- Якщо хост втрачає мережу: використайте OOB-консоль, поверніть VFIO-прив’язку, перезавантажтеся при необхідності.
- Якщо GPU залишається зафіксованим: перезавантаження хоста — фізичний скидання; плануйте відповідно.
- Якщо IOMMU-групи змінились після оновлення: відкотіть прошивку/ядро або перемістіть картки; не боріться з фізикою.
Питання й відповіді
1) Чому Proxmox каже «device is in use», навіть коли жодна VM не працює?
Тому що драйвер ядра хоста його використовує. «In use» зазвичай означає, що драйвер прив’язаний або вузол IOMMU-групи тримається відкритим. Перевірте lspci -nnk і /sys/bus/pci/devices/.../driver.
2) Чи достатньо заблокувати модулі (blacklist)?
Не завжди. Якщо драйвер є в initramfs, він може завантажитись до того, як ваш blacklist застосується з root-файлової системи. Перебудуйте initramfs і перевірте його вміст за допомогою lsinitramfs.
3) Чи потрібно передавати аудіофункцію GPU?
Зазвичай так. Вона часто в тій же IOMMU-групі і ділить фізичний пристрій. Залишивши її прив’язаною до snd_hda_intel на хості, ви часто отримаєте «in use» і дивну поведінку драйверів гостя.
4) Що конкретно означає «/dev/vfio/10 busy»?
Це означає, що IOMMU-група 10 вже відкрита якимось процесом — часто іншим інстансом QEMU, іноді застряглим. Також може статись, якщо ви розділили функції групи між VM. Виправте, передаючи одну групу лише одній VM (або жодній).
5) Чи можна безпечно хот-видаляти PCI-пристрій з хоста?
Ви можете відв’язувати і повторно прив’язувати драйвери, але «безпечність» залежить від класу пристрою і того, чи платформа підтримує чисте скидання. GPU найменш поступливі; HBAs і NIC зазвичай поводяться краще.
6) Чи варто використовувати ACS override?
Тільки якщо ви розумієте компроміс: ви можете послабити гарантії ізоляції. В домашніх лабораторіях це поширено; в регульованому середовищі це зазвичай виняток безпеки, що потребує офіційного дозволу.
7) Чому це ламається після оновлення ядра або BIOS?
Оновлення змінюють поведінку драйверів, вміст initramfs, порядок PCIe-енумерації та IOMMU-групування. Розглядайте хости з passthrough як системи, де оновлення вимагають валідації після змін: прив’язки, групи та цикли старт/стоп VM.
8) Як уникнути прив’язки невірного GPU, коли є два однакові картки?
Не покладайтеся виключно на vfio-pci ids=. Використовуйте PCI-адреси з логікою driver_override під час завантаження, або забезпечте, щоб хост використовував інший клас GPU (вбудований або дешевий адаптер), щоб прив’язка за ID не зачіпала потрібний пристрій.
9) У чому різниця між «Kernel modules» і «Kernel driver in use» в lspci?
«Kernel driver in use» — це активна прив’язка. «Kernel modules» — це драйвери, які могли б прив’язатися за modalias. Вам важлива саме частина «in use».
10) Чи потрібен OVMF для passthrough GPU?
Для більшості сучасних GPU і сучасних гостей — так. OVMF (UEFI) з Q35 — найпередбачуваніша база. SeaBIOS може працювати в окремих налаштуваннях, але додає зайвої складності.
Висновок: наступні кроки, щоб уникнути сюрпризів о 2 ранку
Помилка «device is in use» не випадкова. Це система, що повідомляє: хост все ще володіє апаратурою. Ваше завдання — зробити володіння однозначним: коректна IOMMU-ізоляція, постійна VFIO-прив’язка в initramfs і конфіги VM, що передають усі необхідні функції разом.
Практичні наступні кроки:
- Виберіть один пристрій для passthrough і повністю задокументуйте його функції та членство IOMMU-групи.
- Зробіть VFIO-прив’язку постійною (IDs або адреси), перебудуйте initramfs і перевірте після перезавантаження.
- Запустіть повторні цикли стоп/старт VM, щоб виявити проблеми зі скиданням ще до користувачів.
- Напишіть план відкату, який припускає, що колись ви помилково від’єднаєте не те — бо це рано чи пізно станеться.
Якщо ви зробите passthrough нудним, ви все зробили правильно. Нудно = масштабовано.