Si vous avez déjà tenté le passthrough PCI sur Proxmox et rencontré « device is in use », vous avez fait l’expérience du noyau Linux qui fait son travail : protéger un périphérique encore possédé par autre chose. Le problème, c’est que ce « quelque chose » est souvent invisible — un framebuffer démarré tôt, une petite fonction audio du GPU, un pilote de stockage, une règle udev, ou Proxmox lui‑même qui essaie d’aider.
Réalité en production : vous ne voulez pas « juste redémarrer en espérant que ça passe ». Vous voulez un contrôle déterministe sur qui possède la fonction PCI, quand elle est revendiquée, et comment prouver qu’elle est détachée avant de la remettre à une VM. Voici comment faire cela sans superstition.
Un modèle mental opérationnel : ce que « device is in use » signifie vraiment
Quand Proxmox (QEMU) vous indique qu’un périphérique PCI est « in use », ce n’est pas poétique. Il rapporte que quelque chose sur l’hôte détient actuellement le périphérique ouvert, possède le binding du pilote, ou empêche autrement QEMU de prendre le contrôle en toute sécurité via VFIO.
Sur Linux, une fonction PCI est essentiellement « possédée » via le binding du pilote. Si nouveau, amdgpu, nvidia, xhci_hcd, ixgbe, megaraid_sas, etc. sont liés, le noyau considère, à juste titre, que le périphérique fait partie de l’hôte. VFIO a besoin que ce périphérique soit lié à vfio-pci (ou parfois à vfio-pci avec des quirks de reset spécifiques) pour que QEMU puisse le mapper dans l’invité.
Mais le binding n’est que le début. Vous pouvez détacher un périphérique et avoir malgré tout un processus qui le sollicite via sysfs, une console framebuffer attachée, ou une fonction audio utilisée par PulseAudio que vous aviez oubliée. Et Proxmox ajoute une nuance supplémentaire : c’est un hyperviseur qui essaie d’être utile. Si l’hôte pense qu’un GPU est disponible, quelque chose peut le saisir au démarrage (framebuffer, DRM), et vous négociez alors avec une cible en mouvement.
L’objectif est donc simple et strict :
- Le périphérique (et toutes les fonctions qui doivent le suivre) se trouvent dans un groupe IOMMU isolé ou vous acceptez le compromis de sécurité avec l’override ACS.
- L’hôte ne lie jamais un pilote non‑VFIO pendant le démarrage.
- Le périphérique supporte suffisamment le reset pour survivre aux redémarrages et arrêts de la VM.
- QEMU obtient un accès exclusif via VFIO, à chaque fois, sans « roulette d’unbind » manuelle.
Vérité sèche : le passthrough VFIO est moins une « fonctionnalité prête à l’emploi » et plus un « contrat que vous faites respecter ».
Playbook de diagnostic rapide (premier/deuxième/troisième)
Ceci est l’ordre qui trouve rapidement le goulot d’étranglement en production, quand vous n’avez pas le temps d’admirer les logs du noyau.
Premier : prouver qui possède le périphérique maintenant
- Trouvez l’adresse PCI (bus:slot.func) et voyez quel pilote est lié.
- Confirmez si les modules VFIO sont chargés.
- Vérifiez si une autre fonction du même périphérique multi‑fonction est encore liée à un pilote hôte.
Deuxième : vérifier le groupement IOMMU et si vous vous battez contre la plateforme
- Si le périphérique partage un groupe IOMMU avec quelque chose dont l’hôte a besoin (comme le contrôleur SATA qui gère votre système racine), arrêtez‑vous et repensez l’architecture.
- Si vous avez utilisé l’override ACS, considérez‑le comme une exception de sécurité, pas comme une victoire.
Troisième : chercher les revendications au démarrage et les « utilisateurs invisibles »
- Framebuffer/DRM (fréquent avec les GPU).
- udev qui charge automatiquement des pilotes selon le modalias.
- services systemd (gestionnaire d’affichage, démons de persistance).
- modules noyau restants dans initramfs.
Si vous faites ces trois étapes dans l’ordre, « device is in use » devient un ticket résoluble au lieu d’une ambiance.
Faits intéressants et contexte historique (pourquoi VFIO est étrange)
- VFIO n’a pas été la première tentative. Avant que VFIO ne devienne la norme,
pci-stubétait souvent utilisé pour « garer » des périphériques à l’écart des pilotes hôtes. - L’IOMMU n’est pas seulement pour la virtualisation. Elle existe pour l’isolation DMA, protégeant la mémoire contre les périphériques qui peuvent être bus‑master — utile pour la sécurité et la fiabilité même sans VM.
- Les périphériques PCIe peuvent faire du DMA sans demander. C’est pourquoi un périphérique dans un mauvais groupe IOMMU n’est pas seulement gênant ; c’est un problème de frontière.
- ACS (Access Control Services) est une caractéristique matérielle, pas un caprice du noyau. Quand les plateformes n’exposent pas correctement ACS, Linux ne peut pas toujours scinder proprement les groupes.
- Les périphériques multi‑fonction sont un piège classique. Les GPU apparaissent souvent comme VGA + audio HDMI (et parfois contrôleur USB‑C). Passer une seule fonction mène vite aux conflits.
- Le support du reset est inégal. Certains GPU grand public sont réputés pour ne pas se réinitialiser proprement entre les redémarrages d’invité sans astuces supplémentaires.
- Les graphiques au tout début du démarrage existent. Le framebuffer Linux et DRM KMS démarrent tôt. S’ils saisissent le GPU, vous êtes déjà en retard à moins de pré‑lier à VFIO dans initramfs.
- « Device is in use » est souvent un verrou au niveau noyau, pas un verrou de fichier. Des outils comme
lsofpeuvent être inutiles parce que « l’utilisateur » est le pilote noyau lui‑même. - Les piles virtuelles ont évolué autour des besoins de QEMU. Le design de VFIO s’aligne sur le modèle QEMU/KVM : l’espace utilisateur obtient un accès sûr et médié tandis que le noyau impose l’isolation DMA.
Une maxime qui circule dans les cercles SRE : L’espoir n’est pas une stratégie
— principe général des opérations. Traitez‑la comme un principe SRE, pas comme de la déco. (idée paraphrasée)
Tâches pratiques : commandes, sorties et décisions (12+)
Voici les actions qui répondent vraiment à « qui l’utilise ? » et « que faire ensuite ? » Chaque tâche inclut : commande, ce que la sortie signifie, et la décision à prendre.
Tâche 1 : Identifier le périphérique et le binding du pilote actuel
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
Signification : Le GPU est lié à nouveau. VFIO ne peut pas le prendre pendant qu’un pilote graphique le possède.
Décision : Vous devez empêcher l’hôte de lier nouveau/nvidia et lier vfio-pci à la place (idéalement dans initramfs).
Tâche 2 : Confirmer que les modules VFIO sont chargés
cr0x@server:~$ lsmod | egrep 'vfio|kvm'
vfio_pci 16384 0
vfio_pci_core 73728 1 vfio_pci
vfio_iommu_type1 40960 0
vfio 45056 2 vfio_pci_core,vfio_iommu_type1
kvm_intel 397312 0
kvm 1036288 1 kvm_intel
Signification : VFIO et KVM sont présents. C’est nécessaire mais pas suffisant.
Décision : Si les modules VFIO manquent, corrigez cela d’abord. Sinon, passez aux problèmes de binding/IOMMU.
Tâche 3 : Vérifier que l’IOMMU est activé au niveau noyau
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
Signification : L’IOMMU est active et le remappage d’interruptions est activé (bon pour la stabilité et la sécurité).
Décision : Si vous ne voyez pas l’IOMMU activé, arrêtez‑vous et corrigez le BIOS et les paramètres du noyau avant de toucher quoi que ce soit d’autre.
Tâche 4 : Vérifier l’appartenance au groupe 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
Signification : Le GPU (01:00.0) et sa fonction audio (01:00.1) partagent un groupe. C’est acceptable ; vous passez typiquement les deux.
Décision : Si votre périphérique cible partage un groupe avec quelque chose de critique (comme le NVMe de démarrage), ne continuez pas le passthrough sur cette configuration.
Tâche 5 : Voir ce qui est lié via sysfs (autoritatif)
cr0x@server:~$ readlink -f /sys/bus/pci/devices/0000:01:00.0/driver
/sys/bus/pci/drivers/nouveau
Signification : Le binding noyau est nouveau, indépendamment de ce que vous pensez que votre configuration indique.
Décision : Vous devez empêcher l’hôte de le lier au démarrage (blacklist + initramfs + ids vfio-pci).
Tâche 6 : Trouver si un périphérique est « occupé » parce qu’il fait partie de la pile console/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
Signification : Le GPU est devenu un périphérique framebuffer (fb0). C’est une ancre classique de « en cours d’utilisation ».
Décision : Assurez‑vous que l’hôte ne l’utilise jamais pour la console graphique : lier à vfio-pci dans initramfs ; souvent désactiver le handoff du framebuffer si approprié.
Tâche 7 : Vérifier si un service hôte maintient les pilotes GPU chargés
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
Signification : L’hôte gère activement la pile NVIDIA, qui s’opposera à votre passthrough.
Décision : Désactivez‑le (et déchargez les pilotes) si ce GPU est destiné à une VM.
Tâche 8 : Repérer les modules noyau qui réclameront le périphérique après un unbind
cr0x@server:~$ modprobe --showconfig | egrep -n 'blacklist (nouveau|amdgpu|nvidia|radeon)' | head
412:blacklist nouveau
Signification : Une blackliste existe pour nouveau, mais cela ne garantit pas qu’elle est dans votre initramfs ou que des alternatives ne lieront pas.
Décision : Assurez‑vous que la blackliste est présente puis régénérez l’initramfs pour que le démarrage précoce la respecte.
Tâche 9 : Vérifier le contenu d’initramfs pour les mauvais pilotes (le mystère du « il se charge encore ! »)
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
Signification : Le module est dans l’initramfs, donc il peut se charger avant que vos configurations du rootfs soient appliquées.
Décision : Supprimez‑le de l’initramfs via la configuration appropriée (blacklist dans le contexte initramfs) et régénérez l’initramfs.
Tâche 10 : Lier un périphérique à vfio-pci en utilisant driver_override (chirurgical, 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
Signification : Vous avez re‑lié de force le périphérique sans redémarrer. Utile pour les tests et la récupération d’urgence.
Décision : Si cela « fonctionne », ne vous arrêtez pas là — rendez‑le persistant au démarrage. Les binds runtime sont un excellent moyen d’oublier ce que vous avez changé.
Tâche 11 : Valider que le binding est maintenant 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
Signification : vfio-pci le possède. Les modules listés sont « disponibles », pas nécessairement chargés.
Décision : Assurez‑vous que le reste des fonctions (01:00.1, etc.) est aussi lié à VFIO, puis démarrez la VM.
Tâche 12 : Vérifier la fonction audio (parce que les GPU sont sournois)
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
Signification : La fonction audio du GPU est encore liée à l’hôte. QEMU peut s’étouffer sur cela, et vous obtiendrez « in use » ou une instabilité invitée.
Décision : Liez 01:00.1 à VFIO aussi, ou passez‑la explicitement si votre configuration VM l’attend. Traitez les fonctions GPU comme un ensemble.
Tâche 13 : Identifier les « détenteurs » du point de vue du pilote (qui en dépend)
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
Signification : Si vous utilisez la pile propriétaire NVIDIA, des processus peuvent garder les nœuds de périphérique ouverts.
Décision : Arrêtez le service (gestionnaire d’affichage, Xorg, démon de persistance) avant de détacher. Si vous êtes sans tête, n’installez pas de piles de bureau sur un hôte passthrough à moins d’en avoir besoin.
Tâche 14 : Surveiller ce que QEMU/Proxmox reproche quand le démarrage échoue
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
Signification : Cela pointe vers un nœud de groupe IOMMU (/dev/vfio/10) occupé. Quelque chose a ce groupe ouvert.
Décision : Cherchez une autre VM ou un autre processus utilisant le même groupe IOMMU ; confirmez qu’il n’existe pas de processus QEMU obsolète ; vérifiez que toutes les fonctions du groupe sont passées de façon cohérente.
Blague #1 : VFIO, c’est comme le bureau partagé : si quelqu’un a laissé sa tasse sur la place, vous ne pouvez techniquement pas vous asseoir là.
Détacher les périphériques de l’hôte : la bonne méthode, pas la méthode chanceuse
Étape 0 : Décider si l’hôte doit jamais utiliser le périphérique
Il existe deux modes d’exploitation légitimes :
- Périphérique dédié en passthrough : L’hôte ne doit jamais l’utiliser. Lier à VFIO au démarrage. C’est le mode sensé pour les GPU, contrôleurs USB et NICs destinés aux invités.
- Périphérique en passthrough occasionnel : L’hôte l’utilise parfois, le passe parfois. C’est fragile et invite le « device is in use » car vous faites des unbind/rebind en runtime. N’utilisez‑le que si le matériel est rare et que vous aimez vivre dangereusement.
Étape 1 : Confirmer que votre plateforme supporte l’isolation propre
L’isolation par groupe IOMMU n’est pas un « agréable à avoir ». C’est la frontière qui empêche une VM d’écrire en DMA dans la mémoire d’autres périphériques. Dans un environnement strict, vous passez des groupes entiers. Dans un environnement pragmatique, vous comprenez au moins ce que vous sacrifiez.
Si votre périphérique partage un groupe IOMMU avec un périphérique critique de l’hôte, vos options sont :
- Déplacer la carte vers un autre slot (port root différent, groupement différent).
- Changer les réglages de la plateforme (parfois « Above 4G decoding », « Resizable BAR » modifient indirectement le groupement ; parfois non).
- Utiliser une carte mère/génération CPU différente qui expose ACS correctement.
- Utiliser l’override ACS patch/paramètre module (dernier recours ; mises en garde sur la sécurité et la stabilité).
Étape 2 : Rendre le binding VFIO persistant (la partie que les gens font à moitié)
Sur Proxmox, l’approche propre est : définir les paramètres du noyau pour l’IOMMU, charger les modules VFIO, et configurer vfio-pci pour réclamer les IDs de périphérique tôt.
2a) S’assurer que les paramètres noyau IOMMU existent (exemple Intel) :
cr0x@server:~$ cat /etc/default/grub | egrep 'GRUB_CMDLINE_LINUX_DEFAULT'
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"
Signification : La ligne de commande du noyau inclut l’activation de l’IOMMU et le mode pass‑through pour la performance (iommu=pt) tout en permettant l’isolation VFIO.
Décision : Si absent, ajoutez les bons paramètres, puis exécutez update-grub et redémarrez.
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) Charger les modules VFIO au démarrage :
cr0x@server:~$ cat /etc/modules | egrep '^vfio'
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
Signification : Les modules VFIO de base seront disponibles tôt.
Décision : Si absent, ajoutez‑les et reconstruisez l’initramfs pour que le démarrage précoce les ait.
2c) Lier par vendor:device ID avec vfio-pci
Créez une config modprobe qui indique à vfio-pci de réclamer les IDs. Exemple pour un GPU et sa fonction audio :
cr0x@server:~$ cat /etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:2684,10de:22ba disable_vga=1
Signification : Au chargement, vfio-pci se liera à ces IDs. disable_vga=1 aide à éviter des conflits d’arbitrage VGA sur certains setups.
Décision : Si vous avez plusieurs GPU identiques, envisagez d’utiliser driver_override par adresse PCI à la place, pour éviter de capturer la mauvaise carte.
2d) Blacklister les pilotes hôtes conflictuels (et le faire pour initramfs)
cr0x@server:~$ cat /etc/modprobe.d/blacklist-gpu.conf
blacklist nouveau
blacklist nvidia
blacklist nvidiafb
blacklist rivafb
Signification : Ces modules ne devraient pas se charger automatiquement.
Décision : Si vous les voyez encore dans lsmod après reboot, ils sont tirés via initramfs ou dépendances ; corrigez l’initramfs ensuite.
2e) Rebuild initramfs (là où la plupart des histoires « j’ai blacklistré mais ça ne marche pas » vont mourir)
cr0x@server:~$ sudo update-initramfs -u -k all
update-initramfs: Generating /boot/initrd.img-6.8.12-4-pve
Signification : Le nouvel initramfs inclut votre configuration VFIO et blacklist.
Décision : Redémarrez et revérifiez les bindings. Si c’est encore incorrect, vérifiez d’autres modules comme simpledrm et les réglages console.
Étape 3 : Détacher proprement en runtime (si nécessaire)
Parfois vous êtes déjà en plein incident et un reboot coûte cher. Le détachement en runtime est possible, mais c’est une procédure contrôlée, pas un bruitage d’échos dans sysfs jusqu’à ce qu’il se taise.
Ordre des opérations pour un détachement runtime :
- Arrêtez les processus/services utilisant le périphérique (gestionnaire d’affichage, daemons de persistance, services de stockage).
- Détachez toutes les fonctions PCI qui seront passées.
- Définissez
driver_overridesurvfio-pcipour éviter une ré‑liaison immédiate au pilote ancien. - Liez à
vfio-pci. - Démarrez la VM.
Exemple pour la fonction VGA du GPU + la fonction audio :
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
Ce qui peut mal tourner : l’ancien pilote se relie immédiatement ; un framebuffer reste attaché ; le périphérique ne se réinitialise pas ; ou vous détachez une NIC qui transporte votre session SSH (oui, ça arrive).
Blague #2 : Détacher la NIC de gestion via SSH est une excellente façon de découvrir à quel point vous faites confiance à votre console hors bande.
Étape 4 : Confirmer que Proxmox transmet toute l’histoire, pas la moitié
Dans les configs VM Proxmox, vous utilisez typiquement des entrées hostpciX. Le nombre de problèmes causés par le passage uniquement de la fonction VGA et l’oubli de l’audio, de l’USB‑C ou d’une fonction pont est impressionnant d’un point de vue déprimant.
Vérifiez la configuration 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
Signification : Les deux fonctions sont passées ; OVMF + Q35 est typiquement la base recommandée pour le passthrough GPU moderne.
Décision : Si vous ne passez que 01:00.0, corrigez‑le. Si le groupe IOMMU inclut plus de fonctions (comme 01:00.2), passez‑les aussi ou reconsidérez le périphérique.
Modes de défaillance du passthrough GPU (les suspects habituels)
1) La console hôte utilise le GPU (DRM/KMS)
C’est la cause racine la plus courante derrière « in use ». Si le GPU fournit la sortie console, le pilote DRM du noyau ne lâche pas poliment. Vous pouvez parfois le détacher, mais vous obtiendrez aussi parfois une console noire, un pilote bloqué, ou une carte partiellement réinitialisée.
Conseil production : utilisez un GPU secondaire bon marché (ou les graphiques intégrés) pour l’hôte, et dédiez le GPU en passthrough à VFIO dès la première milliseconde de démarrage.
2) Vous avez passé la fonction VGA mais pas la fonction audio
Beaucoup de GPU sont deux périphériques collés ensemble : VGA et audio. Ils vivent dans le même groupe IOMMU parce qu’ils partagent des ressources. Si vous passez l’un et pas l’autre, l’hôte garde une partie et votre VM obtient l’autre. Ce n’est pas du partage ; c’est une bataille pour la garde.
3) Problèmes de reset : le GPU ne revient pas après l’arrêt/reboot de la VM
Certains périphériques n’implémentent pas correctement le Function Level Reset (FLR), ou la plateforme ne route pas les resets proprement. Symptômes :
- Le premier démarrage de VM fonctionne, le second échoue avec « device is in use » ou l’invité voit Code 43 / erreur de périphérique.
- Les logs hôte montrent le périphérique bloqué, ou VFIO ne peut pas le réinitialiser.
Que faire :
- Assurez‑vous de passer toutes les fonctions.
- Vérifiez si le périphérique supporte le reset ; certains nécessitent des quirks spécifiques au vendeur.
- Dans les pires cas, seul un reboot hôte réinitialise vraiment. Planifiez la capacité et les fenêtres de changement en conséquence.
4) Le mauvais GPU a été capturé par vfio-pci
Lier par ID de périphérique (ids=) est simple. C’est aussi brutal. Si vous avez deux GPU identiques, l’hôte pourrait lier les deux à VFIO et vous perdez la sortie console — ou vous liez la mauvaise et vous vous demandez pourquoi la VM ne voit pas la carte installée « hier ».
Approche préférée dans les systèmes multi‑GPU : lier par adresse PCI en utilisant la logique driver_override dans des scripts de démarrage, ou garder les IDs uniques en choisissant une carte différente pour l’hôte.
HBAs, contrôleurs USB, NICs : classes de périphériques différentes, pièges différents
Passer un HBA (contrôleur SAS/SATA)
Les HBAs sont généralement d’excellents candidats pour le passthrough car ils se comportent comme des périphériques PCI honnêtes et n’ont pas la nuisance de la console graphique. Le piège est plus simple : vous essayez par erreur de passer le contrôleur qui fournit le stockage de l’hôte. Linux s’y opposera, et si vous forcez, l’hôte cessera d’être hôte.
Tâche : confirmez quels dispositifs de blocs sont sur le contrôleur que vous vous apprêtez à retirer.
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
Signification : Les valeurs HCTL suggèrent des disques sur un hôte SCSI (probablement un HBA). Si cet HBA est votre pool ZFS, ce n’est pas un candidat au passthrough sauf si l’hôte ne gérera plus ce pool.
Décision : Passez uniquement un HBA qui n’héberge pas le stockage critique de Proxmox, ou acceptez que vous construisiez une architecture VM de stockage et concevez en conséquence.
Passer un contrôleur USB
Les contrôleurs USB sont populaires car ils fournissent un comportement USB « réel » (dongles, casques VR, onduleurs). Le mode d’échec est le groupement IOMMU : votre contrôleur USB partage un groupe avec d’autres périphériques du chipset dont vous avez besoin. Ou vous passez le contrôleur et perdez le clavier nécessaire pour réparer. L’humour est optionnel.
Tâche : identifier le contrôleur et le groupe.
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
Signification : Le groupe 2 contient le contrôleur USB. Maintenant vous devez vérifier quels autres périphériques sont dans le groupe 2.
Décision : Si le groupe 2 inclut d’autres essentiels du chipset, ne le passez pas. Installez plutôt une carte contrôleur USB dédiée.
Passer une NIC
Le passthrough NIC est excellent pour des charges spécialisées (firewalls, DPDK, faible latence). C’est aussi la façon dont vous vous coupez la branche si vous passez l’interface de gestion. Ayez toujours un accès hors bande (IPMI/iKVM) avant d’expérimenter le passthrough NIC.
Tâche : mapper l’adresse PCI de la NIC au nom d’interface Linux, et confirmer qu’elle n’est pas votre chemin de gestion.
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
Signification : Vous savez maintenant quelle fonction PCI correspond à quelle interface.
Décision : Ne passez pas l’interface fournissant votre session SSH actuelle à moins d’avoir un chemin OOB testé et un plan de rollback.
Trois mini‑histoires d’entreprise (comment cela casse en vrai)
Incident : la mauvaise supposition sur la « blacklisting »
Une entreprise de taille moyenne a migré quelques charges de calcul vers un cluster Proxmox. Un nœud avait un GPU de secours destiné à une VM Windows qui exécutait une clé de licence et du rendu accéléré. L’ingénieur a fait ce que tout le monde fait au début : blacklister nouveau, configurer les IDs vfio-pci, redémarrer, et célébrer quand lspci montrait VFIO.
Deux semaines plus tard, après une mise à jour du noyau, la VM a cessé de démarrer. Proxmox rapportait que le GPU était « in use ». L’ingénieur a supposé que la config « avait dû revenir en arrière » et l’a réappliquée. Toujours cassé. Ils ont revert le noyau. Ça a marché. Ils ont blâmé le nouveau noyau et ouvert un ticket.
Le vrai problème était banal : l’initramfs mis à jour contenait à nouveau le pilote GPU et le chargeait tôt. Le fichier de blacklist existait, mais il n’était pas appliqué dans le contexte initramfs à cause de la façon dont il avait été généré. Le GPU était revendiqué avant que Proxmox ait une chance. Le reboot avait masqué le problème jusqu’à ce que la mise à jour change le timing du démarrage.
La correction n’était pas héroïque. Ils se sont standardisés sur : confirmer la présence du module dans initramfs, régénérer initramfs après les changements, et vérifier les bindings après chaque mise à jour du noyau. Plus de « ça marchait le mois dernier ». La VM est redevenue ennuyeuse, ce qui est l’état correct pour l’infrastructure.
Optimisation qui s’est retournée contre eux : rebind runtime pour éviter les reboots
Une autre organisation avait un seul GPU dans un nœud qui servait parfois un tableau de bord de monitoring hôte (graphisme local) et parfois était passé dans une VM pour des expériences ML courtes. Ils ne voulaient pas redémarrer car le nœud hébergeait d’autres VM. Ils ont donc écrit un script : arrêter le gestionnaire d’affichage, unbind GPU, bind à VFIO, démarrer la VM ; plus tard inversement.
Ça marchait en test. Puis la vraie utilisation a commencé : la VM plantait, redémarrait vite, et le GPU ne se resetait pas proprement. Le script « rebindait » le périphérique, mais le matériel était dans un mauvais état. Parfois l’hôte gelait en rechargeant le pilote DRM. Parfois la VM démarrait mais voyait un GPU cassé. Parfois Proxmox refusait avec « device is in use » parce qu’un processus QEMU obsolète gardait le nœud de groupe ouvert quelques secondes de plus que prévu.
La leçon postmortem était douloureuse et prévisible : le rebind runtime est une taxe de fiabilité. Ils ont acheté un GPU peu gourmand pour la console hôte et dédié le meilleur GPU uniquement au passthrough. Soudainement il n’y avait plus de script, plus de danse, plus de mystère. L’« optimisation » avait évité un achat matériel en dépensant du temps d’ingénierie et du budget incidents à la place.
Pratique ennuyeuse mais correcte qui a sauvé la mise : validation au niveau groupe avant les changements
Une équipe en finance exécutait des hôtes Proxmox avec un contrôle de changement strict. Ils avaient un item de checklist qui irritait tout le monde : avant d’ajouter ou modifier un hostpci, ils enregistraient la composition complète du groupe IOMMU et confirmaient qu’aucun contenu de groupe n’avait changé après les mises à jour firmware.
Un trimestre, une mise à jour BIOS de routine a changé le comportement de bifurcation PCIe sur un sous‑ensemble de nœuds. Le groupe IOMMU du GPU a commencé à inclure un bridge amont et un contrôleur USB qui étaient auparavant séparés. Sur les nœuds mis à jour en premier, le passthrough a commencé à échouer avec « device is in use » et, pire, des problèmes USB intermittents sur l’hôte.
Comme l’équipe disposait de snapshots groupe avant/après dans le ticket de changement, le diagnostic a pris des minutes. Ils n’ont pas perdu des heures à détacher des pilotes et à blâmer Proxmox. Ils ont rollbacké le BIOS sur les nœuds affectés, planifié un changement de layout matériel, et évité une panne plus large. Tout le monde détestait la checklist jusqu’à ce qu’elle paie son dû, ce qui est le fonctionnement habituel des opérations fiables.
Erreurs courantes : symptôme → cause racine → correction
1) Symptom : « device is in use » au démarrage de la VM, à chaque fois
Cause racine : Pilote hôte lié (pilote GPU, pilote USB, pilote NIC) au lieu de vfio-pci.
Correction : Vérifiez avec lspci -nnk et readlink /sys/bus/pci/devices/.../driver. Configurez vfio-pci ids=, blacklistez les pilotes conflictuels, régénérez initramfs, redémarrez.
2) Symptom : ça marche après un unbind manuel, mais échoue après reboot
Cause racine : Vos changements ne sont pas appliqués assez tôt ; initramfs charge le pilote avant que la config rootfs soit active.
Correction : Vérifiez lsinitramfs pour le module en question, mettez à jour initramfs, confirmez que les modules VFIO sont inclus tôt.
3) Symptom : GPU passé, mais l’invité n’a pas d’affichage ou erreurs pilotes
Cause racine : Fonctions complémentaires manquantes (audio, USB‑C), firmware incorrect (SeaBIOS vs OVMF), ou mauvais x-vga.
Correction : Passez toutes les fonctions du groupe IOMMU ; utilisez OVMF + Q35 pour les GPU modernes ; ajoutez x-vga=1 si nécessaire.
4) Symptom : premier démarrage VM OK, second échoue sans reboot hôte
Cause racine : Problèmes de reset périphérique (pas de FLR, routage reset buggy), ou propriété de groupe laissée par un processus QEMU persistant.
Correction : Confirmez qu’il n’y a pas de processus QEMU résiduel ; vérifiez les logs ; considérez un reboot hôte comme seul reset fiable. Préférez des GPU serveur ou connus pour bien gérer les resets quand la disponibilité est critique.
5) Symptom : « /dev/vfio/X: Device or resource busy »
Cause racine : Un autre processus/VM tient le nœud du groupe IOMMU ouvert, souvent parce que vous avez passé une fonction différente du même groupe ailleurs.
Correction : Assurez‑vous de ne pas scinder un groupe entre plusieurs VMs ; arrêtez l’autre VM ; revérifiez la composition du groupe.
6) Symptom : coupure réseau juste après avoir lié une NIC à VFIO
Cause racine : Vous avez passé la NIC de gestion, ou systemd-networkd/ifupdown a perdu l’interface.
Correction : Utilisez l’accès OOB, revenez sur le binding, repensez avec une NIC dédiée pour le passthrough et une NIC séparée pour la gestion ou un bond.
7) Symptom : l’hôte freeze quand vous détachez le pilote GPU
Cause racine : Le GPU est encore utilisé comme framebuffer console actif ou par un compositor/gestionnaire d’affichage.
Correction : Ne faites pas de détachement GPU en runtime sur un hôte qui utilise ce GPU pour la console. Utilisez un GPU différent pour l’affichage hôte ou passez en mode headless.
Listes de contrôle / plan étape par étape
Checklist A : Périphérique dédié en passthrough (recommandé)
- Choisir le périphérique et lister toutes les fonctions (
lspci -nnpour 01:00.0, 01:00.1, etc.). - Vérifier les groupes IOMMU et confirmer que l’isolation du groupe est acceptable.
- Activer l’IOMMU dans le BIOS et les paramètres noyau (
intel_iommu=onouamd_iommu=on). - Charger les modules VFIO tôt via
/etc/modules. - Lier les IDs du périphérique à vfio-pci en utilisant
/etc/modprobe.d/vfio.conf. - Blacklister les pilotes hôtes conflictuels qui réclameraient le périphérique.
- Rebuild initramfs et redémarrer.
- Vérifier le binding après le démarrage avec
lspci -nnket le chemin driver sysfs. - Configurer la VM pour passer toutes les fonctions requises ; utilisez OVMF/Q35 pour les GPU.
- Tester des cycles arrêt/démarrage (au moins 5 cycles) pour détecter les problèmes de reset avant que les utilisateurs ne les rencontrent.
Checklist B : Détachement runtime (à utiliser avec parcimonie)
- Confirmer que vous avez un accès console si vous perdez le périphérique (surtout les NICs).
- Arrêter les services qui pourraient toucher le périphérique (gestionnaire d’affichage, daemons de persistance, services de stockage).
- Détacher toutes les fonctions qui seront passées.
- Définir
driver_overridesurvfio-pcipour éviter une réliaison immédiate. - Lier à
vfio-pci. - Démarrer la VM et confirmer que la propriété de
/dev/vfio/*est correcte. - Quand c’est fini, inversez soigneusement : arrêter la VM, détacher de VFIO, effacer
driver_override, re‑lier au pilote hôte, redémarrer les services.
Plan de rollback (écrivez‑le avant de commencer)
- Si l’hôte perd le réseau : utilisez la console hors bande, revenez sur le binding VFIO, redémarrez si nécessaire.
- Si le GPU reste bloqué : un reboot hôte est le reset dur ; planifiez en conséquence.
- Si les groupes IOMMU ont changé après une mise à jour : rollback du firmware/noyau ou repositionnez les cartes ; ne luttez pas contre la physique.
FAQ
1) Pourquoi Proxmox dit « device is in use » même quand aucune VM ne tourne ?
Parce que le pilote noyau hôte l’utilise. « In use » signifie généralement qu’un pilote est lié ou que le nœud du groupe IOMMU est tenu ouvert. Vérifiez lspci -nnk et /sys/bus/pci/devices/.../driver.
2) Blacklister les modules suffit‑il ?
Pas de manière fiable. Si le pilote est présent dans initramfs, il peut se charger avant que votre blacklist soit appliquée depuis le système racine. Régénérez initramfs et vérifiez la présence du module avec lsinitramfs.
3) Dois‑je passer la fonction audio du GPU ?
Généralement oui. Elle se trouve souvent dans le même groupe IOMMU et partage le dispositif physique. La laisser liée à snd_hda_intel sur l’hôte est une cause fréquente de « in use » et de comportements étranges côté invité.
4) Que signifie précisément « /dev/vfio/10 busy » ?
Que le groupe IOMMU 10 est déjà ouvert par un processus — souvent une autre instance QEMU, parfois une instance bloquée. Cela peut aussi arriver si vous répartissez les fonctions d’un groupe entre plusieurs VMs. Corrigez en assurant qu’un groupe va à une seule VM (ou aucune).
5) Puis‑je hot‑débrancher un périphérique PCI de l’hôte en toute sécurité ?
Vous pouvez détacher et relier des pilotes, mais la « sécurité » dépend de la classe de périphérique et si la plateforme supporte un reset propre. Les GPU sont les moins coopératifs ; les HBAs et NICs se comportent généralement mieux.
6) Dois‑je utiliser l’ACS override ?
Uniquement si vous comprenez le compromis : vous pourriez affaiblir les garanties d’isolation. En homelabs c’est courant ; dans des environnements régulés c’est typiquement une exception de sécurité nécessitant approbation explicite.
7) Pourquoi ça casse après des mises à jour du noyau ou du BIOS ?
Les mises à jour modifient le comportement des pilotes, le contenu de l’initramfs, l’ordre d’énumération PCIe et le groupement IOMMU. Traitez les hôtes passthrough comme des systèmes où les mises à jour doivent inclure une validation post‑changement : bindings, groupes, et cycles de démarrage/arrêt des VM.
8) Comment éviter de lier le mauvais GPU quand j’ai deux cartes identiques ?
Évitez de compter uniquement sur vfio-pci ids=. Utilisez des adresses PCI avec la logique driver_override au démarrage, ou assurez‑vous que l’hôte utilise un GPU différent (intégré ou une carte bon marché) pour pouvoir lier par ID sans dommages collatéraux.
9) Quelle est la différence entre « Kernel modules » et « Kernel driver in use » dans la sortie lspci ?
« Kernel driver in use » est le binding actif. « Kernel modules » sont les pilotes qui pourraient se lier selon le modalias. Vous vous souciez du « in use ».
10) Ai‑je besoin d’OVMF pour le passthrough GPU ?
Pour la plupart des GPU modernes et des invités modernes, oui. OVMF (UEFI) avec Q35 est la base la plus prévisible. SeaBIOS peut fonctionner dans des configurations spécifiques mais ajoute une complexité évitable.
Conclusion : étapes suivantes pour éviter les surprises à 2 h du matin
L’erreur « device is in use » n’est pas aléatoire. C’est votre système qui vous dit que l’hôte possède encore le matériel. Votre travail est de rendre la propriété non ambiguë : isolation IOMMU correcte, binding VFIO persistant dans initramfs, et configurations VM qui passent toutes les fonctions requises ensemble.
Étapes pratiques suivantes :
- Choisissez un périphérique en passthrough et inventoriez complètement ses fonctions et la composition de son groupe IOMMU.
- Rendez le binding VFIO persistant (IDs ou adresse‑basée), régénérez initramfs, et vérifiez après redémarrage.
- Effectuez des cycles répétés d’arrêt/démarrage de VM pour détecter les problèmes de reset avant que les utilisateurs ne les rencontrent.
- Rédigez un plan de rollback qui suppose que vous finirez par détacher la mauvaise chose au moins une fois dans votre carrière — parce que cela arrivera.
Si vous rendez le passthrough ennuyeux, vous l’avez bien fait. L’ennui est scalable.