Le PCI Passthrough est une technique de virtualisation récente qui permet à un hyperviseur de connecter un périphérique PCI (ou PCIe) directement à un invité. Le périphérique est alors dédié avec un accès direct par l’invité. Voici les astuces de configuration à mettre en place pour faire fonctionner les cartes Nvidia les plus récentes avec une machine invité Windows virtualisée par KVM, testé ici avec une Inno3D Nvidia GTX 1050 et une EVGA Nvidia GTX 1060.

Introduction

Il faut savoir que Nvidia a délibérément rendu le PCI passthrough difficile avec les drivers Windows de ses cartes graphiques grand public. Il y a encore un an avec ESXi c’était encore chose impossible selon certaines sources. Selon Nvidia nous devrions acheter leur gamme pro Quadro pour effectuer cette opération. Même la licence d’utilisation de Nvidia interdit le déploiement cloud.

Bien qu’ils aient de moins bonnes performances énergétiques, il semblerait que les GPU AMD de la gamme RX soient moins bridés sur ce point.

La configuration décrite ci-dessous est réputée fonctionner à partir d’informations issues d’internet, et ce n’est pas toujours chose simple, mais il y a peut-être des étapes non-nécessaires. En effet, je me demande si certains paramètres ne sont pas justes utiles pour l’étape d’installation du driver Nvidia.

Pré-requis

La méthode ci-dessous a été réalisée avec l’hyperviseur KVM sur une plateforme matérielle récente (processeur Xeon E5 v3) et une distribution Linux à jour (Debian 9) avec un noyau supérieur à 4.1 (ici 4.9). La carte graphique retenue est une Inno3D 1-slot Nvidia GTX 1050 mais n’importe quelle carte graphique grand public récente (compatible UEFI) devrait convenir.

Un autre ordinateur moins récent a été configuré avec succès sur ancienne architecture CPU Bulldozer, un AMD FX-8350 socket AM3+ avec une EVGA GTX 1060, toujours sous Debian 9.

L’essentiel semble être de disposer d’un processeur et carte mère compatible IOMMU (extensions Intel VT-d ou AMD-Vi).

Il faudrait idéalement s’assurer que la carte graphique est capable de rebooter sans extinction de la machine hôte. Certaines cartes AMD n’en sont pas capables.

Configuration de l’hyperviseur

Les modules noyau

Pour commencer il faut s’assurer que l’hyperviseur KVM active bien ses extensions Intel Vt-d ou équivalent AMD-Vi qui équivalent pour le BIOS et pour Linux à IOMMU. Si une option du BIOS est nommée ainsi, il faut l’activer.

Ensuite on ajoute l’option de noyau intel_iommu=on ou amd_iommu=on sur la ligne  GRUB_CMDLINE_LINUX_DEFAULT  de /etc/defaults/grub . Un update-grub2  est requis ensuite.

Après un reboot on pourra voir passer une référence à « IOMMU » ou à « DMAR » dans dmesg mais ce n’est pas toujours le cas. La présence des fichiers suivants semble indiquer que IOMMU est activé dans le noyau.

Le script suivant trouvé sur le wiki Arch permet de montrer les groupes d’isolation IOMMU. Il faut que le périphérique en passthrough soit isolé. Si ce n’est pas le cas on peut utiliser ACS mais je n’en parlerai pas ici.

On rajoute également les fichiers suivants afin d’obliger le chargement de modules avec le noyau.

On reconnait 10de:1c81 et 10de:0fb9 qui sont les adresses matérielles associées au GPU et visibles entre crochets par un lspci -nn . Si vous voyez sur internet des références à pci-stub, sachez que c’est une méthode plus ancienne et que désormais vfio-pci est recommandé.

De plus, j’ajoute les modules vfio, vfio_iommu_type1 et vfio_pci dans /etc/initramfs-tools/modules afin de les forcer au démarrage du noyau.

Après un  update-initramfs -u  puis reboot les modules suivants sont chargés.

Le script suivant trouvé sur Reddit permet d’afficher les modules noyau avec leurs paramètres (ici kvm avec ignore_msrs et vfio-pcu avec ids et disable_vga).

 

Libvirt avec OVMF

Les cartes Nvidia les plus récentes nécessiteront un BIOS virtualisé UEFI (nommé ovmf), et pas le SeaBIOS par défaut. En tout cas je n’ai jamais réussi à faire fonctionner le passthrough Nvidia avec SeaBIOS malgré de multiples tentatives.

On dé commente les lignes suivantes dans /etc/libvirt/qemu.conf .

 

Dump de la ROM de la carte graphique

On peut trouver sur internet la manipulation permettant de copier le micrologiciel en ROM de la carte graphique dans un fichier qui sera présenté par l’hyperviseur à la machine invité. La commande suivante permet de faire cette opération uniquement si le GPU n’est pas le GPU principal de l’hyperviseur ni utilisé par elle.

Ensuite on peut vérifier que la ROM est correcte avec l’utilitaire rom-parser à compiler depuis les sources github.

On voir ici « type 3 (EFI) » ce qui signifie que la carte est compatible UEFI. Si elle ne l’était pas il faudrait que je mette à jour la ROM de la carte graphique dans l’espoir qu’elle le devienne. Normalement les cartes après 2014 ne devraient pas poser de problème.

Sa taille est d’approximativement 128 Ko. Les ROM que l’on trouve sur internet font parfois 256 Ko et pour les utiliser on pourrait utiliser sans garantie l’utilitaire NVIDIA-vBIOS-VFIO-Patcher pour les convertir au format 128 Ko.

Après quelques tests, je pense que cette manipulation de dump ROM n’est pas nécessaire pour ma GTX 1050, et cela a été confirmé aussi avec une GTX 1060.

Création de la machine virtuelle Windows

Dans l’idée on va utiliser plusieurs astuces pour faire croire à la machine virtuelle qu’elle n’est pas virtualisée, et éviter ainsi que le driver Nvidia sous Windows bloque le GPU.

Pour ce faire j’utilise virt-manager pour créer une machine virtuelle minimale. Demander à modifier les réglages avant de démarrer la VM afin d’être en mesure de choisir le BIOS adéquat. Je choisis les options suivantes :

  • Microprogramme (BIOS) : UEFI x86_64: /usr/share/OVMF/OVMF_CODE.fd
  • Chipset i440FX (Q35 non testé ici)
  • Processeur modèle : host-passthrough (facultatif)
  • Processeur topologie manuelle : 1 socket, 4 cœurs, 2 fils/threads (facultatif)
  • Les 2 périphériques PCI correspondants à la carte graphique sont ajoutés (GPU et sortie audio numérique HDMI).
  • Affichage VNC (pas spice).
  • Video VGA (pas QXL).

 

Ensuite en éditant la configuration XML on peut vérifier les réglages définis par virt-manager, puis en ajouter de nouveaux comme : « hyperv vendor_id », « kvm hidden », et « hostdev rom[« file »] ».

De mon coté c’est ici que je définis un disque avec un zvol ZFS mais c’est hors-sujet.

Pour information, j’ai testé de retirer les éléments de configuration suivants après installation des drivers Nvidia, sans soucis : rom[bar=’on’] ; rom[file=’/image.rom’] ; vmport. Idem pour cpu[mode=’host-passthrough’] qui n’est pas indispensable.

Pour faire court le BIOS OVMF, le paramètre kvm[hidden state=’on’], et vendor_id me semblent indispensables.

Voici ci-dessous le fichier xml entier qui fonctionne sur mon matériel. Certaines valeurs sont cachées comme les uuid et MAC réseau.

 

Installation de la machine virtuelle Windows

Une machine virtuelle Windows installée avec un SeaBIOS aura bien du mal à booter en OVMF. A cause de cela une conversion P2V est difficile. Le mieux est d’installer la VM avec le BIOS OVMF d’origine.

Les pilotes matériels virtio peuvent être ajoutés lors de l’installation avec l’image ISO citée dans cet article sur les pilotes.

Après installation du driver Nvidia sous Windows, l’affichage VNC avec carte VGA ne fonctionnera plus car le GPU sera privilégié. En connexion HDMI ou RDP à distance, le Gestionnaire de périphériques devrait afficher le GPU correctement sans erreur 43.

La VM Windows a planté ? Non son affichage VNC n'est plus utilisable au profit du GPU matériel.

La VM Windows a planté là ? Non son affichage VNC n’est plus utilisable au profit du GPU matériel.

Le programme GPU-Z devrait afficher toutes les fréquences normalement ainsi que toutes les sondes (et pas seulement 2 à 0 MHz).

GPU-Z qui affiche les bonne valeurs et CPU-Z qui indique le BIOS OVMF

GPU-Z qui affiche les bonne valeurs et CPU-Z qui indique le BIOS OVMF

De nombreuses possibilités offertes

Avec  une machine virtuelle qui dispose d’un GPU en PCI passthrough, plein de possibilités s’offrent à vous : configuration bureau dualhead, calculs CUDA, minage cryptomonnaies, exploitation de l’encodage H264 Nvenc, j’en passe et des meilleures.

Avec ça on peut en mettre des GPU (Supermicro X8DTH-iF)

Avec ça on peut en mettre des GPU 1-slot (carte-mère Supermicro X8DTH-iF)

Un GPU matériel ne peut être affecté qu’à une seule VM en même temps. Libvirt vous autorisera tout de même l’affectation du même GPU à plusieurs VM à condition qu’elles ne tournent pas en même temps. Cela offre la possibilité d’affecter un GPU à une VM en temps voulu, un principe qui doit probablement être mis en place par des services de cloud gaming.

Lorsqu’il n’est pas utilisé par une machine virtuelle, on peut se servir du GPU sur l’hyperviseur à condition de charger-décharger vfio à la demande. Le projet bumblebee (implémentation libre de Nvidia Optimus) permet de bénéficier des performances du GPU sans qu’aucun écran ne soit connecté dessus.

En général, le gpu passthrough implique d’utiliser le GPU avec ses ports externes mais une option se présente pour afficher l’image générée par le GPU de la VM en mode fenêtré sur l’hyperviseur : looking glass.

Alternative plus simple

Le présent article présente la configuration complète pour KVM sur Debian 9 mais il existe des distributions clé-en-mains qui permettent de faire l’équivalent avec moins de configuration : il s’agit de Proxmox, orientée virtualisation, et de UnRAID, orientée stockage et également virtualisation.

J’ai notamment déjà lu un article dans Hardware Mag et vu une vidéo de Linus Tech Tips qui présente la méthode avec UnRAID.

 

Sources