Hintergrund

Immer mehr Anbieter stellen Cloud Gaming Server bereit, auf denen Kunden, die zuhause keine gute Grafikkarte (am Prozessor liegt es ja meistens nicht) im PC bzw. Laptop verbaut haben, ihre Spiele spielen und auf das heimische Gerät streamen können. Voraussetzung dafür ist natürlich eine entsprechend schnelle Internetverbindung. In die Details der Technik, die dahintersteckt möchte ich jetzt nicht eingehen, hier eine wirklich kurze Zusammenfassung: Der Client verfügt über eine Software, die alle Tastaturbefehle und Mauseingaben an einen Server weiterleitet auf dem das jeweilige Spiel läuft und wieder an den Client zurückgestreamt wird.

Ich habe mich gefragt, ob man sowas auch auf dem heimischen Server zum laufen bekommen könnte. Der Gedanke war recht simpel, die Umsetzung leider weniger. Zuallererst musste ein neues Gehäuse her, denn im alten Mini-ITX Gehäuse war neben den vielen Festplatten wenig Platz für eine ensprechend potente Grafikkarte. Auf meinem heimischen Server läuft ein Linux auf Arch Linux Basis (Manjaro), das natürlich nicht wirklich zum Gaming geeignet ist. Leider verstehe ich bis heute nicht, warum man die ganzen Verkaufsschlager nicht auch für Linux entwickeln kann, aber zum Glück gibt es im Open-Source Sektor potente Virtualiserungssoftware, nämlich KVM bzw. Qemu.

Voraussetzungen

Erstmal ist sicherzustellen, dass die Hardware passt:

Grafikkarte

  • Eine NVIDIA Grafikkarte (NVIDIA Shield in Kombination mit dem Open Source Client Moonlight ist die Software, die zumindest ich zum Game-Streamen verwende und die meiner Meinung nach neben Parsec die beste ist)

Prozessor und Mainboard

  • Virtualisierung (VT-d bei Intel bzw. AMD-V bei AMD)
  • IOMMU Unterstützung (aktuelle Mainboards und Prozessoren siehe hier: Wikipedia, eine weitere Liste siehe hier: passthroughpo.st. Grob gesagt sind aktuelle Mainboards mit dem Chipsatz Z370 keine schlechte Wahl. Soweit ich das verstanden habe, sollten diese (zumindest von der Firma Asrock) alle IOMMU unterstützen.)
  • Grafikkarte mit UEFI-Unterstützung (bei den aktuellen Modellen der Hersteller eher weniger das Problem)

RAM

  • Zu empfehlen sind 16GB oder mehr

Eine einfache Überprüfung, ob die Hardware so passt, liefert (zumindest unter Arch Linux (Derivaten) folgender Befehl: dmesg|grep -e DMAR -e IOMMU, der in diese Antwort hervorbringen solle: DMAR: IOMMU enabled

Umsetzung

  1. Als Erstes ein komplettes Systemupdate mit sudo pacman -Syu oder analog pikaur (sehr zu empfehlen, falls jemand Pakete aus dem AUR installiert hat)
  2. sudo pacman -S nano, falls nano noch nicht installiert ist. Anschließend sudo nano /etc/default/grub. In der Zeile GRUB_CMDLINE_LINUX_DEFAULT folgendes hinzufügen intel_iommu=on (oder analog für AMD-Prozessoren amd_iommu=on) und davor in der gleichen Zeile iommu=pt, falls man plant den ACS-Patch (siehe unten) zu nutzen, optional noch pcie_acs_override=downstream.
    Im nächsten Schritt ein Befehl zum updaten vom GRUB-Bootmenü: sudo update-grub. Die Zeile sollte dann bisher folgendermaßen (Beispiel AMD) aussehen: GRUB_CMDLINE_LINUX_DEFAULT="iommu=pt amd_iommu=on ...
  3. Nächster Befehl: lspci -nn. Ergebnis des Ganzen wird eine Liste an allen aktuell angeschlossenen PCI-Geräten sein mit entsprechender Hardware ID. In der Liste muss nun die Grafikkarte gefunden werden; fällt dies schwer, kann man zur Vereinfachung der List auch diesen Befehl wählen, der dann hoffentlich nur die Grafikkarte ausgibt: lspci -nn|grep -iP "NVIDIA|Radeon".
  4. Ist die Adresse der GPU herausgefunden, notiert/kopiert man nun die Hardware ID, diese brauchen wir später.
  5. Der Knackpunkt:

    #!/bin/bash
    shopt -s nullglob
    for d in /sys/kernel/iommu_groups/*/devices/*; do 
        n=${d#*/iommu_groups/*}; n=${n%%/*}
        printf 'IOMMU Group %s ' "$n"
        lspci -nns "${d##*/}"
    done;

    Dieses Skript (hier herunterladbar, die Endung txt einfach in sh umbenennen, dann sh in das Terminal eingeben und anschließend die Datei per drag and drop in das Terminal ziehen und ausführen) zeigt uns, ob in der sogenannten IOMMU-Gruppe noch andere Geräte außer der Grafikkarte (und der meist dazugerhörenden HDMI-Soundausgabe) noch weitere Geräte finden. Beispiel: 01:00.0 NVIDIA... ist die Grafikkarte, 01:00.1 die dazugehörige Soundausgabe. Findet sich nun ein weiteres Gerät mit der Adresse 01:00.2 hat man leider ein Problem, das zwar gelöst werden kann, aber mit Mehraufwand verbunden ist. Findet man kein weiteres Gerät in der IOMMU-Gruppe kann man weiter zu Schritt 7. springen :-).

  6. ACS-Patch (optional): Findet man ein weiteres, nicht mit der Grafikkarte assoziiertes Gerät, in der gleichen IOMMU-Gruppe wie die Grafikkarte, benötigt man, so wie ich (früher) auch, den sogenannten ACS Patch.
    Ich will jetzt nicht genauer in die Tiefe gehen, auf jeden Fall bewirkt dieser, dass er die einzelnen Geräte in mehrere IOMMU Gruppen teilt und sozusagen virtuell voneinander separiert. Unter Arch Linux gibt es im AUR einen passenden Kernel, in den bereits der ACS Patch integriert ist. Das Paket findet man unter dem Namen linux-vfio. Weitere Ausführungen (z.B. eine lts-Version) lassen sich einfach über aur.archlinux.org suchen und finden.
    Hat man einen entsprechenden Paketmanger installiert, der direkt Pakete aus dem AUR installieren kann (der bekannteste ist wahrscheinlich yaourt, ich empfehle, wie bereits weiter oben erwähnt pikaur) , kann man nun pikaur -S linux-vfio ins Terminal tippen und sich erstmal einen sehr sehr großen Kaffe machen. Das Kompilieren und die Installation des Kernels dauert nämlich eine ganze Weile, bei mir im Schnitt gut 1,5h. Und das auch bei jedem Update des Kernels... - aber das ist ein anderes Thema :-D.

    Ergänzung/Nachtrag: Wer diesen Kernel unter Arch Linux nicht selber kompilieren möchte, kann mit sudo nano /etc/pacman.conf das folgende unoffizielle Repository hinzufügen:

    [markzz]
    Server = https://repo.markzz.com/arch/$repo/$arch
    SigLevel = Never

    Anschließend kann mit pikaur -Syu markzz-keyring der notwendige GPG Key importiert werden. In /etc/pacman.conf kann nun SigLevel = Never zu SigLevel = PackageRequired geändert werden. /etc/pacman.conf sollte dann so aussehen:

    ...
    [markzz]
    Server = https://repo.markzz.com/arch/$repo/$arch
    SigLevel = PackageRequired
    ...

    Der Kernel wird dann automatisch aus diesem Repository bezogen, wenn man diesen mit pikaur -Syu linux-vfio oder pikaur -Syu linux-vfio-lts installieren möchte. So spart man sich bei jedem Update gute 1,5 Stunden für das Kompilieren.

  7. Nach der Installation gehts so weiter: sudo nano /etc/modprobe.d/vfio.conf, in der Datei dann folgendes einfügen options vfio-pci ids=10de:1007,10de:0e1a, wobei die pci ids den vorher notierten Hardware IDs der Grafikkarte und der dazugehörenden Soundausgabe entsprechen.
    Ergänzung: Alternativ kann das ganze praktischerweise auch in die Datei /etc/default/grub in der folgenden Syntax eingetragen werden: vfio-pci.ids=10de:1c03,10de:10f1. Die Zeile sollte dann insgesamt so aussehen: GRUB_CMDLINE_LINUX_DEFAULT="iommu=pt amd_iommu=on vfio-pci.ids=10de:1c03,10de:10f1 .... Anschließend muss ein sudo update-grub folgen.
    Als Nächstes folgt das editieren dieser Datei: /etc/mkinitcpio.conf: Hier ergänzt man in der Zeile mit MODULES folgendes: MODULES=(... vfio_pci vfio vfio_iommu_type1 vfio_virqfd ...). Die Reihenfolge ist hierbei wichtig!
    Unter MODULES sollte außerdem das eingefügt sein: HOOKS=(... modconf ...). Als nächstes muss mkinitcpio die Konfiguartion des Kernels updaten: mkinitcpio -p linux.
    Nach einem Neustart kann man nun überprüfen, ob alles so geklappt hat, wie man es sich vorstellt: lspci -k, hierbei nach dem wieder nach der IOMMU-Gruppe der Grafikkarte suchen; unter "Kernel driver in use:" sollte nun "vfio-pci" stehen, also z.B so:

    01:00.0 VGA compatible controller: NVIDIA Corporation GK110 [GeForce GTX 780 Rev. 2] (rev a1)
        Subsystem: eVga.com. Corp. Device 3787
        Kernel driver in use: vfio-pci
        Kernel modules: nouveau
    01:00.1 Audio device: NVIDIA Corporation GK110 HDMI Audio (rev a1)
        Subsystem: eVga.com. Corp. Device 3787
        Kernel driver in use: vfio-pci
        Kernel modules: snd_hda_intel
  8. Ist die Grafikkarte samt dazugehöriger Soundausgabe vom Rest in einer IOMMU-Gruppe isoliert, kann man nun den nächsten Schritt starten: Installation von qemu und dem GUI zum verwalten der Virtuellen Maschine: sudo pacman -S qemu virt-manager libvirt. Anschließend sudo systemctl enable libvirtd und sudo systemctl start libvirtd.
    Als nächstes möchte ich gerne auf folgendes Video zur grafischen Konfiguration einer virtuellen Windows-Machine verweisen: Grafische Konfiguaration. Bilder sprechen in diesem Fall mehr als tausend Worte. Der dazugehörende Artikel ist hier zu finden.
  9. Für jene Nutzer, die wie ich eine NVIDIA-Grafikkarte für die Windows-Machine benutzen wollen: sudo virsh edit "NAME_DER_VIRTUELLEN_MASCHINE". Die erste Zeile wird gelöscht und mit damit ersetzt: <domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>.
    Vor dem letzen Tag muss nun folgenden eingefügt werden:

    <qemu:commandline>
        <qemu:arg value='-cpu'/>
        <qemu:arg value='host,hv_time,kvm=off,hv_vendor_id=null'/>
    </qemu:commandline>
  10. Im letzten Schritt erfolgt die Installation vom Windows, den NVIDIA-Treibern und der restlichen Software (z.B. Spiele), die man sonst so braucht. Hat man als Treiber in der grafischen Konfiguration virtio für die Festplatten und den Ethernet-Adapter ausgewählt, braucht man zur Installation von Windows entsprechende virtio Treiber, die man hier als .iso herunterladen kann. An dieser Stelle empfehle ich, die virtio-win-stable Treiber zu benutzen und nicht die virtio-win-latest. Diese bindet man zur Installation von Windows neben der Windows .iso als zweites Laufwerk ein und klickt dann, wenn man gebeten wird, die Festplatte auszuwählen, auf der Windows installiert werden soll, Treiber installieren aus und sucht dann den entsprechenden SATA Treiber aus. Nach dessen Installation sollte die virtio Festplatte zum Auswählen auftauchen. Nach der Installation von Windows muss man gegebenenfalls im Geräte-Manager den Ethernet-Treiber manuell installieren, indem man als Verzeichnis einfach das eingebundene virtio Laufwerk angibt.
    Unter "GeForce Experience" kann man nun unter SHIELD-Einstellungen die extra App: mstsc.exe einfügen. Diese zeigt den Desktop an, so wie man ihn kennt. Von hier aus kann man dann alle Programme oder Spiele starten.
    Auf der Client-Seite lädt man sich hier die Software "Moonlight" herunter, die man dann entsprechend per GUI sehr einfach konfigurieren kann.
  11. Nachdem die Grafikkarte ohne angeschlossenen Bildschirm nicht anspringt, braucht man, falls man keinen freien Bildhschirm zur Verfügung hat, einen sogenannten HDMI Emulator. Auf Amazon gibt es z.B. genug Angebote.

Gaming übers Internet

Das alles funktioniert selbstverstänlich auch übers Internet, entsprechende Internetverbindung vorausgesetzt. Auf Serverseite ist dabei natürlich der Upload entscheidend, aus eigener Erfahrung sollte für stabiles Full HD Gaming min ein konstanter 30 Mbit/s Upload vorhanden sein. Für 720p kann 15 Mbit/s bereits ausreichen. Eine schnellere Internetverbindung bzw. Upload ist natürlich von Vorteil für die Stabilität, im privaten Sektor wird das allerdings dank ADSL in den meisten Fällen leider schnwer zu bekommen sein.

Voraussetzungen
Wichtig für Gaming übers Internet ist eine statische IP. (Eine dynamische IP mit entsprechend aktualisierender Domain habe ich nicht ausprobiert, dies könnte unter Umständen evtl. funktionieren.) Desweiteren müssen entsprechende Port-Freigaben im Router eingestellt werden:

TCP: 47984
     47989
     48010
UDP: 47998 - 48000
     48002
     48010

In manchen Fällen kann ein VPN zum Server die Stabilität erhöhen (warum auch immer). Ich persönlich nutze dafür auf dem Manjaro Host einen Docker Container mit einem Open VPN Server Image (github.com) und entsprechender Port-Freigabe (UDP: 1194) im Router. Die Client-Konfiguration geht über das genannte Image sehr einfach und unkompliziert. Noch dazu ist man bei Open VPN nur auf den Port 1194 angewiesen, anders als bei IKEv2 bzw. IPsec mit 500 und 4500, das unter Umständen andere konfigurierte VPNs im Router stören könnte, wenn man diese Ports auf den Server weiterleitet.

Anmerkung

Dieses Tutorial erhebt keinen Anspruch auf Vollständigkeit. Falls ich etwas Wichtiges vergessen haben sollte, bitte ich um Mitteilung per Mail oder Matrix.
Anbei meine Quellen, sollte etwas unklar sein, kann man dort auch Vieles nachlesen :-).
In diesem Sinne viel Vergnügen beim Gamen!

Client-Wahl

Moonlight läuft sowohl auf Linux, Mac OS als auch Windows. Wer aber eine entsprechende Gaming-Maus verwendet, der sollte in diesem Fall auf Linux als Client verzichten, da die Mehrzahl der Softwarehersteller meist nur Software für Windows und manchmal noch Mac OS bereitstellt.

Appendix A

Für alle, die vom Client (Windows) den Host (Manjaro) erreichen wollen und direct bzw. macvtap als Netzwerkmodus ausgewählt haben, können folgende Quelle nutzen, um eine Verbindung mit dem Host zu ermöglichen: redhat.com. Falls während der Konfiguartion der Fehler Failed to initialize a valid firewall backend auftreten sollte, installiert man per sudo pacman -Syu ebtables dnsmasq fehlende Pakete und startet libvirtd mit sudo systemctl restart libvirtd neu.

Quellen

Nächster Beitrag Vorheriger Beitrag