125 lines
5.1 KiB
Markdown

# PFS libvirt/qemu GPU Passthrough für Windows Gaming VM
## Links
- Arch Linux Wiki: https://wiki.archlinux.org/title/PCI_passthrough_via_OVMF
- VFIO Subreddit: https://www.reddit.com/r/VFIO/
- Weitere Links, die ich in Kommentaren der XML gepackt hab.
## Linux kernel command line args
- `vfio-pci.ids=10de:2684,10de:22ba`: Guest GPU PCI IDs "rausnehmen", dass der kernel
damit nichts macht (GPU und HDMI Audio im Beispiel). Find ich bei mir via `lspci -nn | grep NVIDIA`.
- obsolet: `isolcpus=0-7,16-23`: CPUs für Guest vom System isolieren.
Kann man mittlerweile zur Laufzeit via cgroups machen.
- even obsoleter: `nohz_full=0-7,16-23`: Siehe isolcpus. Bei Interesse docs auf kernel.org lesen. Aber total obsolet.
- `video=efifb:off`: Ich hatte Probleme, weil selbst mit vfio der efifb noch irgendwie die NVIDIA "gegrabt" hat, deshalb efifb:off.
- `module_blacklist=nvidia`: Ich hab bei mir den NVIDIA Treiber UND den AMD Treiber auf dem Host
installiert (AMD = Linux, NVIDIA = Win Guest), weil ich LLMs mit ollama lokal laufen lassen will
und dafür unter Linux auch die NVIDIA Treiber brauche. Wenn ich aber hybrid boote, also VM nutzen
will, dann blackliste ich noch zusätzlich das NVIDIA Modul, macht insgesamt dann weniger Probleme.
- `systemd.unit=pfs-winvm-hybrid.target`: Custom boot target, siehe "rEFInd Config" und "Custom systemd targets".
## rEfInd Config
```
$ cat /boot/refind_linux.conf
"Arch Linux vfio NVIDIA 4090 hybrid WinVM off" "[...] amd_iommu=on iommu=pt vfio-pci.ids=10de:2684,10de:22ba video=efifb:off module_blacklist=nvidia"
"Arch Linux vfio NVIDIA 4090 hybrid WinVM on" "[...] amd_iommu=on iommu=pt vfio-pci.ids=10de:2684,10de:22ba video=efifb:off module_blacklist=nvidia systemd.unit=pfs-winvm-hybrid.target"
"Arch Linux vfio NVIDIA 4090 WinVM only" "[...] amd_iommu=on iommu=pt vfio-pci.ids=10de:2684,10de:22ba video=efifb:off module_blacklist=nvidia systemd.unit=pfs-winvm.target"
"Arch Linux vfio NVIDIA 4090 Linux only NO NVIDIA" "[...] amd_iommu=on iommu=pt module_blacklist=nvidia"
"Arch Linux vfio NVIDIA 4090 Linux only" "[...] amd_iommu=on iommu=pt"
```
## Custom systemd targets
### Hybrid
```ini
$ systemctl cat pfs-winvm-hybrid.target
# /etc/systemd/system/pfs-winvm-hybrid.target
[Unit]
Description=PFS WinVM hybrid Target
Requires=graphical.target
Conflicts=rescue.service rescue.target
After=graphical.target rescue.service rescue.target
AllowIsolate=yes
$ systemctl cat pfs-winvm-hybrid.service
# /etc/systemd/system/pfs-winvm-hybrid.service
[Unit]
Description=PFS WinVM hybrid
After=libvirtd.service
[Service]
ExecStart=virsh start win11new
Restart=no
Type=oneshot
[Install]
WantedBy=pfs-winvm-hybrid.target
```
### Nur VM
```ini
$ systemctl cat pfs-winvm.target
# /etc/systemd/system/pfs-winvm.target
[Unit]
Description=PFS WinVM Target
Requires=multi-user.target
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target
AllowIsolate=yes
$ systemctl cat pfs-winvm.service
# /etc/systemd/system/pfs-winvm.service
[Unit]
Description=PFS WinVM
After=pfs-winvm.target
[Service]
ExecStart=virsh start win11new
Restart=no
Type=oneshot
[Install]
WantedBy=pfs-winvm.target
```
## IOMMU (wichtig)
Damit man einzelne PCI devices zum Guest durchreichen kann, muss im BIOS IOMMU aktiv sein (SVM sowieso für Virtualisierung).
Es können **immer nur ganze IOMMU Gruppen durchgereicht werden**, nicht einzelne Geräte innerhalb einer Gruppe.
```ini
IOMMU Group 13:
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD102 [GeForce RTX 4090] [10de:2684] (rev a1)
01:00.1 Audio device [0403]: NVIDIA Corporation AD102 High Definition Audio Controller [10de:22ba] (rev a1)
IOMMU Group 14:
02:00.0 Non-Volatile memory controller [0108]: Seagate Technology PLC FireCuda 530 SSD [1bb1:5018] (rev 01)
IOMMU Group 15:
03:00.0 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] Device [1022:43f4] (rev 01)
```
Hier ist Group 13 die Gruppe in der mein NVIDIA VGA Controller und das NVIDIA Audio Device ist. Deshalb auch beide auf jeden Fall durchreichen!
USB Controller reiche ich auch durch, aber da musste ich schauen, welche überhaupt einzeln durchreichbar waren.
Skript um die IOMMU Gruppen schnell zu sehen:
```bash
$ cat iommu_groups.sh
#!/bin/bash
shopt -s nullglob
for iommu_group in /sys/kernel/iommu_groups/*; do
echo "IOMMU Group ${iommu_group##*/}:"
for d in "${iommu_group}/devices"/*; do
echo -e "\t$(lspci -nns "${d##*/}")"
done
done
```
## CPU Topologie und Cache
`lstopo`: Tool um schnell die Topologie der CPU zu sehen. Also um herauszufinden, welche Kerne auf welchem CCD liegen.
Bei meiner CPU haben die Cores 0-7 die virtuellen Cores 0-7 und 16-23 und die Cores 8-15 die virtuellen Cores 8-15 und 24-31.
Außerdem ist mit `lstopo` erkennbar, dass die Cores 0-7 mit dem ersten L3 Cache verbunden sind, und die Cores 8-15 mit dem Zweiten.
Es ist ratsam für die Performance, dass sich die VM Cores und die Host Cores keinen Cache teilen, also in meinem Fall alle virtuellen
Cores der Cores 0-7 durchzureichen, sprich 0-7,16-23. Siehe XML File.