GPU Passthrough sous Proxmox VE Installation et configuration (Part. 00x04)

Page content

Je dois donner une formation pour une cliente qui a récemment acquis un iMac. J’aurais aimé avoir un mac sous la main pour préparer le support de la formation, malheureusement mon ancien MacBook pro de 2012 a décidé de rendre l’âme. Je me suis donc motivé à me re-lancer dans un projet que j’avais laissé de côté depuis quelques mois. Ce projet le voici : Dédier un hyperviseur (Proxmox) à la virtualisation de desktop macOS, Windows et Linux. L’objectif étant de faire bénéficier à l’ensemble des VMs d’une bonne accélération graphique grâce au PCI Passthrough . En un mot, avoir les avantages de la virtualisation sans trop rogner sur les performances.

Commençons par un petit point explicatif. Dans un contexte de virtualisation le “PCI passthrough” consiste dans l’attribution d’un périphérique PCI (par exemple une carte graphique), à une machine virtuelle (permettant pour la VM un accès direct aux capacités graphiques du GPU). Alors que, traditionnellement, la puissance de calcul des GPU est partagée entre l’hyperviseur et les machines virtuelles, ce qui limite significativement les performances des VMs.

Cette fonctionnalité, n’est pas réservée qu’à vos seules cartes graphiques et est également utilisée pour le stockage ou le réseau (par exemple : l’attribution de NIC en Passthrough pour une VM opensense).

Dans cet article, je vous propose donc de voir de plus près le processus de configuration pour paramétrer le PCI Passthrough sous Proxmox VE 8.0 (j’avais initialement prévu cet article pour Proxmox VE 7, mais vu que la nouvelle version viens juste de sortir, c’est l’occasion de tester !).

Cet article est le premier d’une série de cinq articles traitant de l’installation et de la configuration de VMs (Linux, Windows, macOS et BSD) en PCI Passthrough sur Proxmox VE 8.

Prérequis & contexte

Dans mon cas, je vais prendre pour base un ancien PC “Gaming” format ATX et installer Proxmox dessus. D’une part parce que je n’ai rien d’autre de potable sous la main, et d’autre part parce que l’ensemble des VMs resterons cantonnée à un usage “desktop”.

Ma config :

  • MOTHERBOARD: ASRock 970 Pro3 R2.0
  • CPU: AMD FX 4300 Quad-Core Processor
  • GPU: SAPPHIRE Pulse Radeon RX 580 8 Go GDDR5

Si vous souhaitez dédier un pc, pour faire de la virtualisation avec Proxmox, assurez-vous d’avoir un système compatible (CPU/MOTHERBOARD) :

  • Le Processeur ; doit pouvoir prendre en charge les extensions de virtualisation VT-x/VT-d Pour les processeurs Intel, et AMD-V/AMD SVM pour les processeurs AMD, pour que le support d’IOMMU soit effectif, il en va de même pour les cartes mères.
  • Il ne vous restera plus ensuite qu’à activer ces extensions de virtualisation depuis votre interface BIOS ou UEFI. Vous pouvez vous référer à cette page qui je trouve est assez synthétique https://www.informatiweb.net/tutoriels/informatique/bios/activer-iommu-ou-vt-d-dans-le-bios.html.

Dans mon cas, j’ai donc, après avoir eu accès au BIOS, modifier c’est deux valeurs :

Advanced/CPU_Configuration/ Secure Virtual Machine [Enabled] 
Advanced/North_Bridge/IOMMU [Enabled]

Ajout de nouvelles variables au fichier d’amorçage

Suivant votre configuration système à savoir, quel bootloader est utilisé par proxmox, deux configurations sont applicables une pour GRUB et l’autre pour systemd-boot. Afin de déterminer lequel des deux est utilisé, exécuter la commande suivante depuis Proxmox :

efibootmgr -v
  • Si la commande renvoie un message indiquant que les variables EFI ne sont pas prises en charge, GRUB est utilisé en mode BIOS/Legacy.

  • Si la sortie contient une ligne qui ressemble à ce qui suit, GRUB est utilisé en mode UEFI.

Boot0005* proxmox [...] Fichier(\EFI\proxmox\grubx64.efi)
  • Si la sortie contient une ligne similaire à la suivante, systemd-boot est utilisé.
Boot0006 * Linux Boot Manager [...] Fichier (\EFI\systemd\systemd-bootx64.efi)

Activation IOMMU pour GRUB

Si GRUB est votre bootloader qu’il soit en mode BIOS/Legacy ou UEFI, pour un CPU AMD, ajoutez les arguments suivants à votre fichier de boot :

/etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT="quiet iommu=pt"

Pour un processeur Intel c’est la configuration suivante qui s’applique :

GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"

Procéder ensuite au refresh de grub avec :

update-grub

Ne reste plus qu’a reboot le système.

Cependant, en fonction de votre configuration, il sera peut-être nécessaire de customiser votre commande de boot si le Passthrough échoue.

Dans mon cas, j’ai ajouté les arguments suivants :

GRUB_CMDLINE_LINUX_DEFAULT="quiet iommu=pt nomodeset pcie_acs_override=downstream initcall_blacklist=sysfb_init"

Ces commandes supplémentaires consolident la méthode de division des périphériques PCI dans leur propre groupe IOMMU, en activant l’ACS Override, désactivant le chargement des pilotes graphiques, et empêchant l’initialisation du framebuffer au démarrage du noyau.

Concernant “pcie_acs_override” cela doit être considéré comme une option de dernier recours pour avoir des groupes IOMMU distincts, mais n’est pas sans risques. En effet, son activation impliquera que la machine virtuelle pourra lire toute la mémoire de d’hôte Proxmox (incidemment celle des autres machines virtuelles), à utiliser donc, à vos propres risques.

En savoir plus sur : https://pve.proxmox.com/wiki/PCI_Passthrough#Verify_IOMMU_isolation

L’argument “initcall_blacklist=sysfb_init” viens en remplacement des arguments “video=efifb:off” et “video=simplefb:off, compte tenu que “initcall_blacklist=sysfb_init” donne de meilleurs résultats depuis la version 7.2 de Proxmox.

En savoir plus sur : https://forum.proxmox.com/threads/gpu-passthrough-issues-after-upgrade-to-7-2.109051

Activation IOMMU pour systemd-boot

Dans le cas ou systemd-boot est utilisé, ajouter les arguments suivant à votre fichier de boot :

/etc/kernel/cmdline

root=ZFS=rpool/ROOT/pve-1 boot=zfs quiet iommu=pt

Pour un processeur intel : /etc/kernel/cmdline

root=ZFS=rpool/ROOT/pve-1 boot=zfs quiet intel_iommu=on iommu=pt

Procéder ensuite au refresh de systemd-boot avec :

pve-efiboot-tool refresh

On reboot ensuite le système et l’on vérifie que IOMMU est bien activé :

dmesg | grep -e IOMMU

Si l’opération est un succès, vous devriez avoir le résultat suivant :

[    0.000000] Warning: PCIe ACS overrides enabled; This may allow non-IOMMU protected peer-to-peer DMA

VFIO modules et vérification du support du remapping

Nous allons devoir ajouter quelques modules VFIO à notre système Proxmox.

echo "vfio" >> /etc/modules
echo "vfio_iommu_type1" >> /etc/modules
echo "vfio_pci" >> /etc/modules

Sur les versions précédantes de Proxmox le module “vfio_virqfd” devait être également ajouter, mais n’est plus disponible dans PVE 8.

Faite ensuite la mise à jours de vos images initramfs et redémarrer Proxmox :

update-initramfs -u -k all
systemctl reboot

Après avoir redémarré le système, nous pouvons faire la vérification de l’état des modules VFIO:

dmesg | grep -i vfio

Vous devriez avoir en sortie, quelque chose de ce type :

[    7.262027] VFIO - User Level meta-driver version: 0.3

Vérification si votre système prend en charge le remappage des interruptions :

dmesg | grep 'remapping'

Si la commande revoie: “AMD-Vi: Interrupt remapping enabled” ou “DMAR-IR: Enabled IRQ remapping in x2apic mode” alors le remappage est pris en charge. Dans le cas contraire, vous pouvez autoriser les interruptions non sécurisées avec:

echo "options vfio_iommu_type1 allow_unsafe_interrupts=1" > /etc/modprobe.d/iommu_unsafe_interrupts.conf

Ajout de correctifs de stabilité et optimisation pour les cartes NVIDIA er AMD

Carte Nvidia

Certaines applications Windows comme l’expérience Geforce, Passmark Performance Test, peuvent faire planter votre machine virtuelle. Pour palier à cela ajouter :

echo "options kvm ignore_msrs=1 report_ignored_msrs=0" > /etc/modprobe.d/kvm.conf

Cartes AMD correction du “Reset Bug”

C’est un bug bien connu de certaines cartes AMD, le problème survenant lorsqu’une machine virtuelle utilise la carte graphique dédiée via le GPU passthrough, mais lorsqu’elle est arrêtée ou redémarrée, la carte graphique ne se réinitialise pas correctement. Cela pouvant entraîner une incapacité pour l’hyperviseur de réallouer le GPU, et dans d’autre cas le plantage du système hôte. La solution consistera dans ce cas de figure à purement et simplement redémarrer complètement l’hôte.

Cependant, pour prévenir ce genre de désagréments, qui j’en conviens peuvent être pénible, nous pouvons déployer ce module kernel supplémentaire https://github.com/gnif/vendor-reset qui tentera de corriger ce problème de réallocation.

apt install pve-headers-$(uname -r)
apt install git dkms build-essential
git clone https://github.com/gnif/vendor-reset.git
cd vendor-reset
dkms install .
echo "vendor-reset" >> /etc/modules
update-initramfs -u
shutdown -r now

Après le chargement de ce module (vous pouvez le vérifier avec “dmesg | grep vendor_reset”). Éditons maintenant un service pour nous assurer que la réinitialisation s’effectuera bien sur notre carte graphique.

Je commence par récupérer l’ID PCI de mon GPU avec :

lspci -nn | grep 'AMD' 

J’obtiens le résultat suivant, (dans mon cas l’ID correspondant à ma carte graphique est 01:00.0) :

01:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] [1002:67df] (rev e7)
01:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere HDMI Audio [Radeon RX 470/480 / 570/580/590] [1002:aaf0]

On crée maintenant notre service :

cat << EOF >>  /etc/systemd/system/vreset.service
[Unit]
Description=AMD GPU reset method to 'device_specific'
After=multi-user.target

[Service]
ExecStart=/usr/bin/bash -c 'echo device_specific > /sys/bus/pci/devices/0000:01:00.0/reset_method'

[Install]
WantedBy=multi-user.target
EOF
systemctl enable vreset.service && systemctl start vreset.service

Bien entendu n’oubliez pas d’adapter le service en fonction de l’ID de votre GPU.

GPU isolation et drivers graphiques

Comme nous venons de le voir, pour récupérer l’ID PCI de notre carte graphique, il suffit d’utiliser les commandes suivantes :

lspci -nn | grep 'AMD' 

pour les cartes AMD.

lspci -nn | grep 'NVIDIA'

pour les cartes Nvidia.

Comme nous le savons désormais, dans mon cas ce sont les ID 01:00.0 et 01:00.1 qui m’intéressent, correspondant à ma carte graphique AMD RX 580, ou plus précisément les VendorID et Device ID 1002:67df & 1002:aaf0 :

01:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] [1002:67df] (rev e7)
01:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere HDMI Audio [Radeon RX 470/480 / 570/580/590] [1002:aaf0]

A ce stade, vous avez le choix entre deux types de configuration possible :

  • Soit vous décidez de ne pas créer une blacklist des pilotes. Mais vous devrez vous assurez que le module vfio-pci se chargera en premier à l’aide de softdep.
  • Soit vous décidez de simplement faire un blacklisting global des drivers.

Première méthode : “Modules order”

Nous allons créer un fichier de configuration pour spécifier les ID PCI à isoler 1002:67df et 1002:aaf0. Mais également définir un ordre de chargement pour des modules.

echo "options vfio-pci ids=1002:67df,1002:aaf0" >> /etc/modprobe.d/vfio.conf
# For AMD
echo "softdep radeon pre: vfio-pci" >> /etc/modprobe.d/vfio.conf
echo "softdep amdgpu pre: vfio-pci" >> /etc/modprobe.d/vfio.conf
# For Nvidia
echo "softdep nouveau pre: vfio-pci" >> /etc/modprobe.d/vfio.conf
echo "softdep nvidia pre: vfio-pci" >> /etc/modprobe.d/vfio.conf
echo "softdep nvidiafb pre: vfio-pci" >> /etc/modprobe.d/vfio.conf
echo "softdep nvidia_drm pre: vfio-pci" >> /etc/modprobe.d/vfio.conf
echo "softdep drm pre: vfio-pci" >> /etc/modprobe.d/vfio.conf
# For Intel
echo "softdep snd_hda_intel pre: vfio-pci" >> /etc/modprobe.d/vfio.conf
echo "softdep snd_hda_codec_hdmi pre: vfio-pci" >> /etc/modprobe.d/vfio.conf
echo "softdep i915 pre: vfio-pci" >> /etc/modprobe.d/vfio.conf

On reboot ensuite le système.

Seconde méthode : “Blacklisting drivers”

Comme précédemment nous allons créer un fichier de configuration pour spécifier les ID PCI à isoler 1002:67df et 1002:aaf0.

echo "options vfio-pci ids=1002:67df,1002:aaf0" > /etc/modprobe.d/vfio.conf

Puis, assurons-nous de “blacklisté” les pilotes correspondant à notre type de carte graphique, afin d’éviter tout conflit avec l’hôte Proxmox.

# AMD drivers
echo "blacklist radeon" >> /etc/modprobe.d/blacklist.conf 
echo "blacklist amdgpu" >> /etc/modprobe.d/blacklist.conf
# NVIDIA drivers
echo "blacklist nouveau" >> /etc/modprobe.d/blacklist.conf 
echo "blacklist nvidia" >> /etc/modprobe.d/blacklist.conf 
echo "blacklist nvidiafb" >> /etc/modprobe.d/blacklist.conf
echo "blacklist nvidia_drm" >> /etc/modprobe.d/blacklist.conf
# Intel drivers
echo "snd_hda_intel" >> /etc/modprobe.d/blacklist.conf
echo "snd_hda_codec_hdmi" >> /etc/modprobe.d/blacklist.conf
echo "i915" >> /etc/modprobe.d/blacklist.conf

On reboot ensuite le système.

Test et vérification finale

Comme vous avez pu le constater, il n’y a pas de méthode unique pour réaliser un PCI Passthrough vers votre GPU, compte tenu de la diversité des environnements de chacun. Il n’est donc pas à exclure que votre configuration ne fonctionne pas du premier coup, garder à l’esprit les particularités que je vous ai listé et persévéré avec une approche (essai > erreur > recherche) me parait être le meilleur état d’esprit.

Afin de vous garantir de meilleures chances ce succès voici les commandes utiles pour réaliser un débogage le plus complet possible au fur et à mesure de votre avancée :

Suport d’IOMMU:

dmesg | grep -E "DMAR|IOMMU"

output AMD :

[    0.000000] Warning: PCIe ACS overrides enabled; This may allow non-IOMMU protected peer-to-peer DMA

output Intel :

[    0.110221] DMAR: IOMMU enabled
[    0.951433] DMAR: Intel(R) Virtualization Technology for Directed I/O

Suport du remapping:

dmesg | grep 'remapping'

output AMD :

[    0.598913] AMD-Vi: Interrupt remapping enabled

output Intel :

[    0.190148] DMAR-IR: Queued invalidation will be enabled to support x2apic and Intr-remapping.
[    0.191599] DMAR-IR: Enabled IRQ remapping in x2apic mode

VFIO:

dmesg | grep -i vfio

output :

[    7.262027] VFIO - User Level meta-driver version: 0.3
[    7.329352] vfio-pci 0000:01:00.0: vgaarb: deactivate vga console
[    7.329359] vfio-pci 0000:01:00.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=io+mem:owns=io+mem
[    7.329490] vfio_pci: add [1002:67df[ffffffff:ffffffff]] class 0x000000/00000000
[    7.376427] vfio_pci: add [1002:aaf0[ffffffff:ffffffff]] class 0x000000/00000000

Chargement correct des drivers :

lspci -nnk | grep -E "VGA" -A 3

output :

01:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] [1002:67df] (rev e7)
        Subsystem: Sapphire Technology Limited Radeon RX 570 Pulse 4GB [1da2:e353]
        Kernel driver in use: vfio-pci
        Kernel modules: amdgpu
01:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere HDMI Audio [Radeon RX 470/480 / 570/580/590] [1002:aaf0]
        Subsystem: Sapphire Technology Limited Ellesmere HDMI Audio [Radeon RX 470/480 / 570/580/590] [1da2:aaf0]
        Kernel driver in use: vfio-pci
        Kernel modules: snd_hda_intel

Ressources :