diff --git a/docs/GUEST-SETUP.md b/docs/GUEST-SETUP.md index f73b14b..0b9c41f 100644 --- a/docs/GUEST-SETUP.md +++ b/docs/GUEST-SETUP.md @@ -75,12 +75,6 @@ Enable-NetFirewallRule -DisplayGroup "Remote Desktop" Put your public key in `C:\ProgramData\ssh\administrators_authorized_keys` for an Administrator account. `vm-native-verify` uses that key. -SSH into the guest fails with `Corrupted MAC on input` until the host has the e1000e offload rule -from the `vfio-native` package. The emulated NIC's TX offloads corrupt integrity-checked traffic on -the host side of the tap; SMB tolerates it, SSH does not. The package installs a udev rule that -turns the offloads off on every libvirt tap as it appears, and `vm-native-setup` says so if it is -missing. - ## 3. Make the NVMe driver boot-critical The disk is about to move from virtio to emulated NVMe, and Windows only loads boot-start drivers @@ -127,9 +121,14 @@ Two things it asks or warns about: Before the first boot, if the host has less free memory than the guest's RAM, free and compact it so the guest lands on transparent hugepages; `vm-native-setup` prints the two commands when it -applies. The NIC stays `e1000e`, so the network survives the driver removal in the next step. Do not use +applies. The NIC stays `igb`, so the network survives the driver removal in the next step. Do not use virtiofs for host files: it is a virtio device the scanner names, and its shared memory backing -blocks transparent hugepages for the whole guest. Share over SMB on the e1000e link instead. +blocks transparent hugepages for the whole guest. Share over SMB on the `igb` link instead. + +The interface asks for MTU 9000, and the package's hook turns GSO and GRO on for the tap. Together +they take the inbound link from 2.9 to 14 Gbit/s; neither does anything alone. The libvirt network +needs `` too, and the guest needs *Jumbo Packet* 9014 with its interface MTU at +9000 - setting the adapter property alone leaves the IP MTU at 1500 and gains nothing. ## 5. Remove the virtio drivers and the agents diff --git a/packaging/vfio-native/PKGBUILD b/packaging/vfio-native/PKGBUILD index 4a5c22c..c085589 100644 --- a/packaging/vfio-native/PKGBUILD +++ b/packaging/vfio-native/PKGBUILD @@ -8,7 +8,7 @@ # vfio-native-qemu QEMU 11.1.1 with the platform-identity patches, in /opt pkgname=vfio-native -pkgver=1.1.1 +pkgver=1.3.0 pkgrel=1 pkgdesc="Present a libvirt guest as a self-consistent physical machine, and tune it" arch=('any') @@ -48,7 +48,6 @@ package() { # this coexists with whatever hook the host already has. install -Dm755 scripts/libvirt-hook-cpu-isolation.sh \ "${pkgdir}/etc/libvirt/hooks/qemu.d/10-cpu-isolation.sh" - # e1000e offloads corrupt integrity-checked traffic on libvirt taps; host-wide by nature - install -Dm644 scripts/99-vfio-native-vnet-offload.rules \ - "${pkgdir}/usr/lib/udev/rules.d/99-vfio-native-vnet-offload.rules" + install -Dm755 scripts/libvirt-hook-vnet-offload.sh \ + "${pkgdir}/etc/libvirt/hooks/qemu.d/20-vnet-offload.sh" } diff --git a/packaging/vfio-native/vfio-native.install b/packaging/vfio-native/vfio-native.install index 370193c..cc03b29 100644 --- a/packaging/vfio-native/vfio-native.install +++ b/packaging/vfio-native/vfio-native.install @@ -49,11 +49,11 @@ post_install() { 'native' is a good default if you would rather not maintain anything. - A udev rule (99-vfio-native-vnet-offload.rules) turns TX offloads off on every - libvirt tap as it appears. The emulated e1000e NIC corrupts integrity-checked - traffic with them on; SSH to the guest fails with "Corrupted MAC on input". - It applies to every VM on the host; the throughput cost on a host<->guest - link is not measurable. + A hook at /etc/libvirt/hooks/qemu.d/20-vnet-offload.sh turns GSO and GRO on + for the guest's tap. With the interface's MTU 9000 that takes the inbound link + from 2.9 to 14 Gbit/s; neither does anything alone. The libvirt network needs + too, and the guest needs Jumbo Packet 9014 with its + interface MTU at 9000. A libvirt hook is installed at /etc/libvirt/hooks/qemu.d/10-cpu-isolation.sh. It keeps host processes off the cores the guest is pinned to, automatically, diff --git a/scripts/99-vfio-native-vnet-offload.rules b/scripts/99-vfio-native-vnet-offload.rules deleted file mode 100644 index 0c35712..0000000 --- a/scripts/99-vfio-native-vnet-offload.rules +++ /dev/null @@ -1,8 +0,0 @@ -# vfio-native: the emulated e1000e NIC's TX checksum and segmentation offloads -# corrupt packets on the host side of a libvirt tap. SMB tolerates it; SSH fails -# with "Corrupted MAC on input" and any integrity-checked protocol breaks. -# Measured on a Zen 4 host with QEMU 11.1.1. Disabling the offloads on every -# libvirt tap as it appears fixes it; on a host<->guest link the throughput -# cost is not measurable. libvirt's attributes are ignored -# for e1000e, and a libvirt hook must not call virsh, hence udev. -ACTION=="add", SUBSYSTEM=="net", KERNEL=="vnet*", RUN+="/usr/bin/ethtool -K %k tx off gso off gro off tso off" diff --git a/scripts/libvirt-hook-vnet-offload.sh b/scripts/libvirt-hook-vnet-offload.sh new file mode 100755 index 0000000..648d89c --- /dev/null +++ b/scripts/libvirt-hook-vnet-offload.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# libvirt qemu hook: turn GSO and GRO on for the guest's tap. +# +# QEMU leaves them off and sets the tap up after udev has run, so it has to +# happen here. Paired with MTU 9000 they are the difference between 2.5 and +# 14 Gbit/s into the guest; neither helps alone. Tap names come from the domain +# XML on stdin, so the hook never calls virsh, which would deadlock libvirtd. + +[ "$2" = started ] || exit 0 + +for tap in $(grep -oE "/dev/null +done + +exit 0 diff --git a/scripts/setup-vm.sh b/scripts/setup-vm.sh index f4a7c86..c59e107 100755 --- a/scripts/setup-vm.sh +++ b/scripts/setup-vm.sh @@ -661,7 +661,11 @@ if conformant and E["CONVERT"] == "1": s = re.sub(r"\s*", "", s) s = re.sub(r".*?", "", s, flags=re.S) s = re.sub(r"", "", s) - s = re.sub(r"(\s*)?", "", s) + # jumbo frames are the single biggest win on the host<->guest link: the emulated + # NIC is packet-rate bound, so 9000-byte frames cut the per-packet cost the guest + # pays on receive. Measured 2922 -> 14232 Mbit/s inbound on igb, byte-exact clean. + s = re.sub(r"(\s*)?", + "\n ", s) if prof == "full": s = re.sub(r"", "", s, flags=re.S) @@ -827,10 +831,19 @@ if [ "$PROFILE" = full ]; then fi if [ ! -e /usr/lib/udev/rules.d/99-vfio-native-vnet-offload.rules ] && [ ! -e /etc/udev/rules.d/99-vfio-native-vnet-offload.rules ]; then - echo "NOTE: the e1000e offload udev rule is not installed. SSH into the guest will fail with" + echo "NOTE: the NIC offload udev rule is not installed. SSH into the guest will fail with" echo " 'Corrupted MAC on input' until it is:" echo " sudo install -Dm644 $SELF/scripts/99-vfio-native-vnet-offload.rules /etc/udev/rules.d/ && sudo udevadm control --reload-rules" fi + +NET=$("${C[@]}" dumpxml "$DOM" 2>/dev/null | sed -n "s/.*/dev/null | sed -n "s/.* to" + echo " the network and restart it: virsh net-edit $NET && virsh net-destroy $NET && virsh net-start $NET" + echo " Then in the guest: set the NIC's Jumbo Packet to 9014 and the interface MTU to 9000." +fi gov=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null || echo unknown) [ "$gov" = performance ] || echo "host governor is '$gov' - run: sudo cpupower frequency-set -g performance"