Installer une VM Windows avec un GPU dédié sous QEMU/KVM

|

Le projet

La virtualisation c’est bien beau, mais les performance graphiques des VM sont souvent très limitées, et ne permettent pas certains usages. Dans mon cas, je suis 95% du temps sous Linux, mais il m’arrive parfois de devoir rebooter sous Windows pour quelques tâches très précises : traitement de photos sous Lightroom, création musicale (mon vieux Line6 TonePort KB37 n’est pas supporté sous Linux), et jeu - bien que la situation s’est grandement améliorée de ce côté là sous Linux ces dernières années -.

Concernant le traitement de photos et le jeu, une carte graphique est indispensable pour obtenir des performances correctes. C’est pour cette raison que je préfèrais rebooter sous Windows plutôt que de passer par une VM.

Une solution existe cependant : le PCI passthrough. Il permet de dédier un périphérique PCI (dans notre cas une carte graphique) à une VM, avec des performances quasiment intactes.

Prérequis

  • Deux cartes graphiques (ou une carte + celle intégrée à votre CPU), préférablement de deux marques différentes (NVidia et AMD par exemple)
  • Beaucoup d’espace disque (Windows prend vite ses aises) voire idéalement un SSD dédié
  • Pas mal de RAM (32 Go dans mon cas)
  • Un CPU supportant la virtualisation processeur et chipset (chez Intel, il s’agit des technologies VT-x et VT-d)
  • Deux moniteurs

Mon matériel

  • Carte mère Asus H97-Plus
  • CPU Intel i7 4790k
  • RAM 32 Go
  • GPU 1 : Nvidia K620 (que je souhaite dédier à l’hôte)
  • GPU 2 : AMD Radeon RX480 (que je souhaite dédier à la VM)
  • SSD 256 Go dédié à la VM

Mise en place

Configuration BIOS

Dans mon cas, les deux options à activer se trouvent sous :

  • Advanced > System Agent Configuration > VT-d
  • Advanced > Intel Virtualization Technology

Installation de packages

sudo apt-get install qemu-kvm virt-manager
sudo adduser $USER kvm

Activer l’IOMMU

IOMMU : Input-Output Memory Management Unit

Dans /etc/default/grub :

On remplace :

GRUB_CMDLINE_LINUX=""

Par :

GRUB_CMDLINE_LINUX="intel_iommu=on"

Puis on met à jour grub et on reboot :

sudo update-grub
reboot

Après reboot, le script suivant doit afficher pas mal de choses :

#!/bin/bash
shopt -s nullglob
for g in /sys/kernel/iommu_groups/*; do
    echo "IOMMU Group ${g##*/}:"
    for d in $g/devices/*; do
        echo -e "\t$(lspci -nns ${d##*/})"
    done;
done;

Dans mon cas je répère les deux lignes suivantes, correspondant au GPU que je souhaite dédier à ma VM (et l’interface audio associée):

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 c7)
01:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere HDMI Audio [Radeon RX 470/480 / 570/580/590] [1002:aaf0]

Il faut noter les deux identifiants indiqués (dans mon cas 1002:67df et 1002:aaf0)

Bloquer le chargement du GPU à dédier à notre VM

Dans mon cas, je ne souhaite pas charger les modules correspondant à mon AMD Radeon, je vais donc blacklister le module amdgpu (vous devrez peut-être blacklister le module radeon si c’est celui que vous utilisez, cf. lsmod).

Je crée donc le fichier /etc/modprobe.d/blacklist-amdgpu.conf contenant :

blacklist amdgpu

J’active aussi le VFIO (Virtual Function I/O) passthrough en créant le fichier /etc/modprobe.d/vfio.conf contenant :

options vfio-pci ids=1002:67df,1002:aaf0

Les identifiants sont bien évidemment à adapter.

Dernière mise à jour de grub et recontruction de l’initramfs pour prendre en compte ces changements :

sudo update-grub
sudo update-initramfs -u
reboot

Après reboot, cette commande devrait afficher quelques lignes :

dmesg | grep vfio-pci

Création d’une VM Windows

J’utilise pour celà l’outil graphique virt-manager. Je ne rentrerai pas dans les détails de la création, mais le plus important est d’ajouter à notre VM deux hôtes PCI correspondant à la carte graphique (et son interface audio) que l’on souhaite dédier à la VM.

On n’oublie pas de brancher un écran sur notre carte graphique en passthrough, et on lance l’installation de Windows, puis des drivers graphiques de notre carte.

Partage souris clavier entre hôte et VM

On installe logiciel Barrier sur l’hôte et la VM (serveur côté hôte, client côté VM). On pourra ainsi passer la souris d’un écran à l’autre même lorsque les deux écrans ne sont pas connecté au même OS. Barrier permet aussi d’effectuer des copier-coller entre le OS.

TIP : la touche “scroll-lock” (ou “verrou défil.”) du clavier permet de verrouiller la souris à l’écran actuel. Celà permet par exemple de jouer à des FPS sans que la souris ne quitte malencontreusement l’écran du jeu en cours de partie. Dans ce contexte, il est aussi nécessaire d’activer l’option “mouvements de souris relatif” côté serveur Barrier pour ne pas être limité dans ses mouvements de souris (sinon on a parfois l’impression de “buter” sur les bords de l’écran en déplaçant la souris).

Bonus

Je tenais à conserver le dual-screen sous Linux, mais à pouvoir dédier un de mes écrans à la VM Windows lorsque je le souhaitais. Il est possible de brancher un des écrans aux deux cartes puis de changer la source d’entrée au niveau de l’écran, mais celà peut nécessiter un peu de développement côté Linux pour qu’il désactive un des écrans lorsque l’on passe sur notre VM Windows, et n’est pas forcément très érgonomique en fonction de l’écran.

J’ai opté pour une solution beaucoup plus simple : un switch HDMI (ça vaut dans les 10€). De cette manière, lorsque je switch l’entrée connectée au GPU dédié à Windows, mon GPU Linux détecte une déconnection physique d’un l’écran et adapte automatiquement son affichage (même chose à la reconnexion).

Cablage

Quelques photos

Horizon

Horizon Zero Dawn sous Windows à gauche, Linux à droite

Apex

Apex Legends sous Windows à gauche, Linux à droite