Gaming en machine virtuelle?

Gaming en machine virtuelle?

Bien sûr, je ne parle pas de jouer à Angry Birds. Si vous m'aviez posé la question il y a quelques années, je me serais probablement moqué de vous. En effet, la virtualisation de type desktop n'a jamais été très performante au niveau de l'accélération 3D. Contrairement aux processeurs (UPC) qui ont depuis plusieurs années des extensions de virtualisation, les cartes vidéos n'ont jamais ce type de fonctionnalités.

Au mieux, les hyperviseurs vont aller chercher à émuler un pilote graphique haute performance en utilisant certaines capacités du GPU (Graphic Processing Unit) de l'hôte pour accélérer un peu les systèmes invités. C'est notamment ce qui permet de rouler Aero, l'effet de vitre et de transparence de Windows Vista et Windows 7 en machine virtuelle.

Au delà de l'affichage du bureau avec une accélération matérielle, on se rend compte des limites de ces pilotes 3D. Les produits comme VMware Player et Oracle Virtualbox n'arrivent pas à fournir des performances adéquates, notamment pour les jeux en 3D.

En faisant des recherches, il y a quelques années, je me suis apperçu que l'hyperviseur Xen permettait de déléguer entièrement une carte graphique à un système invité1. Le problème que j'ai rencontré avec Xen est qu'il nécessitait d'ajouter certains modules au kernel Linux. Tout ça ne jouait pas très bien avec les pilotes Nvidia pour ma carte vidéo. J'ai donc abandonné l'idée de faire fonctionner Xen.

En juin dernier, VFIO-VGA a été ajouté au kernel Linux 3.92. Puisque KVM fait parti du kernel depuis longtemps, j'avais déjà un hyperviseur qui fonctionnait sans problèmes avec ma Geforce. J'ai lu un peu sur le sujet et j'ai récupéré une Radeon HD 5850 pour tester cette nouvelle fonctionnalité.

Le reste de cet article est repris d'un document que j'ai rédigé l'hiver dernier. J'ai retravaillé un peu la structure du texte pour rendre la lecture plus accessible. Je le publie ici aujourd’hui puisque j'ai l'intention de revenir sur le sujet prochainement, histoire de démontrer l'évolution rapide de QEMU et de KVM.

Une image vaut mille mots :

kvm_3dmark06

Image - Performance d'une VM sous 3D Mark 2006

Mise en garde

Comme cette technologie est en cours de développement et que les patchs sont régulièrement acceptés, il n'est pas conseillé de déployer VFIO-VGA dans un environnent de production sans avoir une très bonne connaissance du sujet. De plus, je recommande d'avoir une bonne compréhension du fonctionnement des hyperviseurs avant de vous aventurer plus loin.

De ce fait, pour augmenter la stabilité et les performances, il est idéal de suivre de près les notes des développeurs des logiciels. Plusieurs patchs doivent être appliqués pour permettre le bon fonctionnement en fonction du matériel que vous utilisez. Pour appliquer des patchs, il faut donc savoir aller chercher le code source des programmes et être en mesure de compiler le programme patché, ce qui est en dehors du cadre de ce document.

Chaque distribution de Linux possède ses différences. Toutefois, il est possible d'arriver à un résultat similaire à partir de n'importe quelle distribution. Il y a des gens qui ont réussi à faire fonctionner VFIO-VGA avec Arch Linux, Fedora, Debian et Ubuntu. Si vous n'êtes pas familier avec Linux et un environnement en ligne de commande, vous aurez beaucoup de chemin à faire.

Préparation

Matériel nécessaire

Certaines caractéristiques sont requises au niveau du matériel afin de partager à une machine virtuelle (VM) vos périphériques. Pour faire du VGA passthrough, le système hôte doit avoir des capacités IOMMU3.

Avant l'arrivée des contrôleurs de mémoire intégrée et de la fusion du Northbridge avec le processeur, il fallait s'assurer que le processeur et le chipset supporte les fonctionnalités. Maintenant, VT-d et AMD-Vi sont des fonctions intégrées à l'UPC. Le BIOS (ou UEFI) doit tout de même supporter VT-d ou AMD-Vi.

Processeur

Chez Intel, il faut un processeur avec les extensions de virtualisation4 VT-x et VT-d (vérifiable ici). Si vous avez un processeur K (par exemple un Core i7 4770K), il y a de fortes chances que VT-d ne soit pas supporté. Vous ne pourrez pas faire de VGA passthrough sans VT-d.

Pour AMD, il faut un processeur avec les extensions AMD-V et AMD-Vi. AMD n'a pas de base de données compréhensive sur le sujet, malheureusement, mais les processeurs les plus récents semblent tous avoir ces extensions.

Carte mère (Chipset et BIOS/UEFI)

La carte-mère doit également supporter VT-x/AMD-V et VT-d/AMD-Vi. Vous devez donc regarder avec le manufacturier pour le modèle de carte-mère si ces technologies sont supportées. Presque tous les constructeurs supportent VT-x/AMD-V.

Pour ce qui est de VT-d/AMD-Vi, la plupart des cartes construites par Gigabyte et par Asrock semblent supporter cette extension de virtualisation. Pour les autres constructeurs, comme Asus et MSI, c'est mal documenté.

Carte vidéo

Idéalement, il faut deux cartes vidéos sur le système. Une pour l'hôte et une pour l'invité. Gardez à l'esprit que le GPU n'est pas partagé entre les deux systèmes. Lorsque VFIO-VGA passe le contrôle d'un périphérique à un système invité, il n'est plus utilisable par l'hôte. Donc si vous n'avez qu'une carte vidéo, vous perdrez l'affichage de votre hôte et il ne sera possible d'y avoir accès que par SSH. Ça s'approche du Dom0 pour Xen.

Chez AMD/ATI, une Radeon de série HD 3000 ou plus récent est souhaitable. Chez Nvidia, une Geforce de série 8000 ou plus récent est recommandé. Les équivalents FirePro et Quadro devraient également fonctionner.

L'affichage devra être dirigé vers un moniteur à partir d'un des ports de la carte vidéo qui est passée à la VM. Il faut donc prévoir en conséquence.

Contrôle de la VM (affichage, clavier, souris)

Spice, RDP, VNC ou NX peuvent être utilisés pour contrôler la VM. Ces solutions réseaux n'arrivent pas à fournir un débit souhaitable pour jouer par contre.

Pour faciliter l'installation du système d'exploitation, il est possible d'utiliser -vga qxl qui présentera l'affichage de la VM dans la fenêtre de QEMU. Le fonctionnement ressemblera à celui de VMware Player.

Toutefois, lorsqu'un jeu sera en marche, -vga qxl ne devrait pas être utilisé pour éviter les pertes de performances. Dans ce cas, il faut prévoir une méthode pour contrôler le clavier et la souris de la VM. Synergy peut être utilisé pour partager ces fonctionnalités entre l'hôte et l'invité. Autrement, on peut passer le contrôle de n'importe quel périphérique USB à la VM. On pourrait donc utiliser une switchbox KVM (comme celle-ci) pour changer le focus sur la VM ou sur l'hôte.

Logiciels nécessaires

Pour permettre le VGA passthrough, nous allons donc utiliser KVM et QEMU. Optionnellement, vous pouvez utiliser Libvirt pour faciliter la gestion des VM si vous en avez plusieurs sur le même hôte. Il est également possible d'utiliser Virtio pour augmenter la performance des périphériques réseaux et des contrôleurs de disques de la VM.

Kernel Linux

Il faut un kernel Linux au moins équivalent à la version 3.9. À partir de cette version, le VFIO-VGA est introduit. J'ai personnellement testé depuis 3.11. Vous pouvez prendre un kernel qui est fourni par votre distribution mais il y a de fortes chances que vous devrez le recompiler avec les options suivantes :

CONFIG_VFIO_IOMMU_TYPE1
CONFIG_VFIO
CONFIG_VFIO_PCI
CONFIG_VFIO_PCI_VGA

CONFIG_VIRTIO_PCI
CONFIG_VIRTIO_BALLOON
CONFIG_VIRTIO_MMIO
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES

De plus, pour que l'hôte continue de bien fonctionner quand une VM est en fonction, il faut un patch pour modifier l'arbitrage des GPU. Dans mon cas, je dois appliquer ce patch pour permettre à ma Geforce de bien fonctionner quand une VM est en marche.

QEMU (et SeaBIOS)

Pour éviter d'avoir à redémarrer l'hôte à chaque fois que la VM se ferme, prévoyez QEMU à partir de la version 1.7 avec le patch VGA RESET. Si VGA RESET n'est pas appliqué, vous ne pourrez pas démarrer une machine virtuelle deux fois de suite sans redémarrer ou mettre en veille le système hôte. VGA RESET à été intégré à QEMU depuis la version 2.0. J'utilise https://github.com/awilliam/qemu-vfio.

SeaBIOS à été fusionné avec QEMU depuis la version 1.7. Il n'est donc pas nécessaire de compiler SeaBIOS ailleurs.

Configuration

Les modules

Une fois les logiciels installés, il faut configurer le système hôte pour permettre à VFIO-PCI de s'accrocher à notre matériel physique. Pour ce faire, il y a un sujet assez compréhensif sur les forums d'Arch Linux. Je vous conseille d'y jeter un coup d'œil.

Pour mon système, j'ai blacklisté le module radeon pour l'empêcher de s'approprier ma deuxième carte vidéo. Il suffit d'ajouter ou de créer un fichier dans le dossier /etc/modprobe.d/.

# /etc/modprobe.d/blacklist.conf
blacklist radeon

Ensuite, j'ai créé un fichier pour m'assurer que les modules KVM et VFIO chargent certaines valeurs.

# /etc/modprobe.d/kvm.conf
options kvm ignore_msrs=1
options kvm_intel emulate_invalid_guest_state=0

# Si votre carte mere ne supporte pas bien IOMMU
# Surveillez vos fichiers logs attentivement si vous activez cette option
options vfio_iommu_type1 allow_unsafe_interrupts=1

Finalement, j'ai ajouté la ligne de démarrage de Linux dans /etc/default/grub :

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash iommu=on intel_iommu=on"

Après avoir modifié les paramétrés de GRUB, il faut toujours mettre à jour les fichiers qui sont chargés au démarrage. Sur Debian et Ubuntu, les scripts update-initramfs et update-grub permettent de le faire facilement. Au redémarrage du système, on peut vérifier que les modules sont bien chargés.

$ dmesg | grep -i pci-dma
[    0.923886] PCI-DMA: Intel(R) Virtualization Technology for Directed I/O

VFIO

J'utilise le script suivant placé dans /usr/bin/ pour attacher mes périphériques à vfio-pci :

#!/bin/bash
#
# /usr/bin/vfio-bind
# Attach devices to vfio-pci

modprobe vfio-pci

for dev in "$@"; do
        vendor=$(cat /sys/bus/pci/devices/$dev/vendor)
        device=$(cat /sys/bus/pci/devices/$dev/device)
        if [ -e /sys/bus/pci/devices/$dev/driver ]; then
                echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
        fi
        echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id
done

Pour faciliter l'expérience, on peut se loguer en super utilisateur (oui en root!). Ça évitera beaucoup de complications au niveau des permissions et vous n'aurez pas besoin de faire sudo pour chaque commande.

Nous pouvons appeler le script avec les bons périphériques en vérifiant leur ID : lspci | grep -i radeon.

Personnellement, je partage ma carte vidéo (les ID sont 02:00.0 et 02:00.1).

$ vfio-bind 0000:02:00.0 0000:02:00.1

Si vous n'avez pas eu de segfault ou de messages d'erreurs, alors tout va très bien!

La machine virtuelle

Pour contrôler ma VM, j'ai un deuxième clavier et une deuxième souris qui sont connectés sur des ports USB. Je peux noter leur ID pour les passer à ma VM en faisant ceci :

$ lsusb | grep -i microsoft
Bus 002 Device 005: ID 093a:2510 Microsoft Corp. Wired Mouse 600
Bus 002 Device 003: ID 045e:0750 Microsoft Corp. Wired Keyboard 600

Ensuite, il suffit d'appeler qemu-system-x86_64 --enable-kvm et lui ajouter les paramêtres qu'on désire. Pour pouvoir passer une carte vidéo à une VM, il faut au moins -vga none et ce -device :

-vga none \
-device ioh3420,bus=pcie.0,addr=1c.0,multifunction=on,port=1,chassis=1,id=root.1 \

Ensuite, on peut simplement ajouter notre carte vidéo avec le bon ID (que nous avons noté plus haut) :

-device vfio-pci,host=$VOTRE_CARTE_VIDEO,bus=root.1,addr=00.0,multifunction=on,x-vga=on \
-device vfio-pci,host=$GPU_AUDIO,bus=root.1, addr=00.1

Pour transmettre l'audio de la VM à l'hôte, vous pouvez utiliser ceci :

-device ich9-intel-hda,bus=pcie.0,addr=1b.0,id=sound0 \
-device hda-duplex,id=sound0-codec0,bus=sound0.0,cad=0

J'ai tout rassemblé à l'intérieur d'un script pour simplifier l'appel de commande. Vous pouvez vous en inspirer :

#!/bin/bash
###############################################################################
# Settings
###############################################################################
# VM Name
NAME="Windows"

# BIOS
SYSTEM_BIOS="/usr/share/qemu/bios.bin"

# CPU
CPU="host"
CORES="2"

# RAM
RAM="4096"

# Passthrough devices
GPU_RADEON="02:00.0"
GPU_AUDIO="02:00.1"
GPU_BIOS="/var/tmp/kvm/vgabios-gigabyte-hd5850-1024m.rom"

# USB Keyboard
USB_KBD="045e:0750"

# USB Mouse
USB_MOU="093a:2510"

# Networking
MAC_ADDR="ENTREZ-UNE-ADRESSE-MAC"

# Hard Drive
HD_PATH="/var/tmp/kvm/windows/windows.img"  

# CD Rom
CD_PATH_WIN="CHEMIN-VERS-ISO-DE-SYSTEME-DEXPLOITATION"
CD_PATH_VIRTIO="/var/tmp/kvm/Downloads/kvirtio-win-0.1-65.iso"

# Execute
###############################################################################
qemu-system-x86_64 --enable-kvm \
-M q35 -m $RAM -cpu $CPU,hv-time -name $NAME \
-smp $(($CORES*2)),sockets=1,cores=$CORES,threads=1 \
-bios $BIOS -vga none -rtc base=localtime,clock=host \
-device ioh3420,bus=pcie.0,addr=1c.0,multifunction=on,port=1,chassis=1,id=root.1 \
-device piix4-ide,bus=pcie.0,id=ide \
-device vfio-pci,host=$GPU_RADEON,bus=root.1,addr=00.0,multifunction=on,x-vga=on,rombar=0,romfile=$GPU_BIOS \
-device vfio-pci,host=$GPU_AUDIO,bus=root.1, addr=00.1 \
-device ich9-intel-hda,bus=pcie.0,addr=1b.0,id=sound0 \
-device hda-duplex,id=sound0-codec0,bus=sound0.0,cad=0 \
-usb -usbdevice host:$USB_KBD -usbdevice host:$USB_MOU \
-net nic,macaddr=$MAC_ADDR,model=virtio -net tap,ifname=tap0,script=no,downscript=no \
-drive file=$HD_PATH,id=disk,if=virtio \
-drive file=$CD_PATH_WIN,id=wincd -device ide-cd,bus=ido.0,drive=wincd \
-drive file=$CD_PATH_VIRTIO,id=isocd -device ide-cd,bus=ide.1,drive=isocd \
-boot order=dc, menu=on
###############################################################################

Si tout fonctionne bien, votre VM démarre et vous avez de l'affichage à partir de votre deuxième carte vidéo. Vous pouvez remplacer -vga none par -vga qxl afin de partager votre clavier et souris avec l'hôte.

Observations

La stabilité est très bonne, tant au niveau de l'hôte que du système invité. Je peux continuer à recevoir mes courriels sur mon hôte et je peux y conserver mes travaux en cours pendant que la VM fonctionne. En utilisation, je n'ai eu aucun crash et j'ai fait fonctionner ma VM pendant plus d'une semaine sans redémarrage.

Voici une petit aperçu :

Vidéo - Metro 2033 sous KVM/QEMU

Après avoir installé les pilotes Catalyst et le DirectX 9.0c, il est possible de jouer à pratiquement n'importe quel jeu. Les plus modernes que j'ai testé sont des jeux de stratégie (RTS) : Planetary Annhilation et Wargame Red Dragon. Le tout roule sans problème majeur, toutefois ces jeux sont eux-mêmes encore en phase bêta.

J'ai observé des problèmes de performances avec des jeux qui utilisent le moteur d'Unreal, par exemple XCOM : Enemy Unknown et Borderlands. Ces jeux font appel à des registres de débogage sur le CPU et KVM ne sait pas trop quoi en faire, ce qui a un impact majeur sur les performances avec ses jeux. Toutefois, une série de patchs ont été proposés depuis mes essais et il sembleraient rectifier la situation.

Conclusion

Pour conclure, je crois que d'ici quelques mois, je pourrai entièrement retirer la partition NTFS de Windows 7 sur mon disque dur. Les performances sont extrêmement satisfaisantes. Contrairement à un dual boot, l'utilisation de machine virtuelle me permet de ne pas interrompre mon travail sur l'hôte pour aller sur Windows pendant quelques heures.

VIRTIO fournit des performances d'I/O très bonnes pour l'interface réseau et pour le disque dur virtuel. Dans le cas du transfert sur le réseau, c'est presque à vitesse native.

Je vois cette technique comme très intéressante pour plusieurs raisons :

  1. Maximisation de performance – Possible de maximiser les performances pour des jeux 3D ou pour des logiciels exigeants comme tout ce qui est CAD, Adobe Premiere, etc.
  2. Intégration avec Libvirt et Virsh – Permet de gérer relativement facilement les machines virtuelles de KVM à partir de l'hôte. C'est l'idéal pour faire des tests de développement, tant développement d'applications web que locales pour tester différents environnements de travail.
  3. Logiciel libre / FOSS – On peut exécuter, étudier, modifier et distribuer le code librement.

Il y a quelques limitations pour l'instant :

  1. Instable / En cours de développement – Il y a souvent des mises à jours pour ces fonctionnalités, donc ce n'est pas encore souhaitable pour un déploiement dans une environnement de production.
  2. Relativement complexe – Contrairement à de la virtualisation desktop comme Virtualbox, la technique de VGA passthrough pour KVM est beaucoup plus complexe. Tout d'abord, il faut une assez bonne connaissance de l’environnement Linux. Sans ça, il y a de grandes chances que des erreurs soient simplement dues à l'utilisateur. Même un power user Mac ou Windows aura beaucoup de lecture à faire avant de se lancer dans l'aventure. En lien avec le numéro 1, s'il faut être à l'aise pour diagnostiquer, comprendre et réparer les erreurs qui peuvent survenir. /var/log est une mine d'or pour ça par exemple.

Enfin, c'était un projet qui m'intéressait depuis maintenant quelques années. Ma curiosité m'a amené à composer ce document pour montrer aux autres à quel point la virtualisation est un sujet captivant.

Références supplémentaires :