feat: igb nic, jumbo frames and tap offloads on the guest link

This commit is contained in:
2026-09-06 05:39:38 +02:00
parent 91fd1c45e1
commit f90745e68e
6 changed files with 45 additions and 27 deletions

View File

@@ -75,12 +75,6 @@ Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
Put your public key in `C:\ProgramData\ssh\administrators_authorized_keys` for an Administrator Put your public key in `C:\ProgramData\ssh\administrators_authorized_keys` for an Administrator
account. `vm-native-verify` uses that key. 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 ## 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 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 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 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 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 `<mtu size='9000'/>` 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 ## 5. Remove the virtio drivers and the agents

View File

@@ -8,7 +8,7 @@
# vfio-native-qemu QEMU 11.1.1 with the platform-identity patches, in /opt # vfio-native-qemu QEMU 11.1.1 with the platform-identity patches, in /opt
pkgname=vfio-native pkgname=vfio-native
pkgver=1.1.1 pkgver=1.3.0
pkgrel=1 pkgrel=1
pkgdesc="Present a libvirt guest as a self-consistent physical machine, and tune it" pkgdesc="Present a libvirt guest as a self-consistent physical machine, and tune it"
arch=('any') arch=('any')
@@ -48,7 +48,6 @@ package() {
# this coexists with whatever hook the host already has. # this coexists with whatever hook the host already has.
install -Dm755 scripts/libvirt-hook-cpu-isolation.sh \ install -Dm755 scripts/libvirt-hook-cpu-isolation.sh \
"${pkgdir}/etc/libvirt/hooks/qemu.d/10-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 -Dm755 scripts/libvirt-hook-vnet-offload.sh \
install -Dm644 scripts/99-vfio-native-vnet-offload.rules \ "${pkgdir}/etc/libvirt/hooks/qemu.d/20-vnet-offload.sh"
"${pkgdir}/usr/lib/udev/rules.d/99-vfio-native-vnet-offload.rules"
} }

View File

@@ -49,11 +49,11 @@ post_install() {
'native' is a good default if you would rather not maintain anything. '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 A hook at /etc/libvirt/hooks/qemu.d/20-vnet-offload.sh turns GSO and GRO on
libvirt tap as it appears. The emulated e1000e NIC corrupts integrity-checked for the guest's tap. With the interface's MTU 9000 that takes the inbound link
traffic with them on; SSH to the guest fails with "Corrupted MAC on input". from 2.9 to 14 Gbit/s; neither does anything alone. The libvirt network needs
It applies to every VM on the host; the throughput cost on a host<->guest <mtu size='9000'/> too, and the guest needs Jumbo Packet 9014 with its
link is not measurable. interface MTU at 9000.
A libvirt hook is installed at /etc/libvirt/hooks/qemu.d/10-cpu-isolation.sh. 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, It keeps host processes off the cores the guest is pinned to, automatically,

View File

@@ -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 <driver><host .../> 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"

View File

@@ -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 "<target dev='(vnet|tap|macvtap)[^']*'" | sed "s/.*dev='//; s/'$//"); do
ethtool -K "$tap" gso on gro on 2>/dev/null
done
exit 0

View File

@@ -661,7 +661,11 @@ if conformant and E["CONVERT"] == "1":
s = re.sub(r"\s*<input type='[^']*' bus='virtio'/>", "", s) s = re.sub(r"\s*<input type='[^']*' bus='virtio'/>", "", s)
s = re.sub(r"<memballoon model='virtio'>.*?</memballoon>", "<memballoon model='none'/>", s, flags=re.S) s = re.sub(r"<memballoon model='virtio'>.*?</memballoon>", "<memballoon model='none'/>", s, flags=re.S)
s = re.sub(r"<memballoon model='virtio'/>", "<memballoon model='none'/>", s) s = re.sub(r"<memballoon model='virtio'/>", "<memballoon model='none'/>", s)
s = re.sub(r"<model type='virtio'/>(\s*<driver [^/]*/>)?", "<model type='e1000e'/>", 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"<model type='virtio'/>(\s*<driver [^/]*/>)?",
"<model type='igb'/>\n <mtu size='9000'/>", s)
if prof == "full": if prof == "full":
s = re.sub(r"<video>.*?</video>", "<video>\n <model type='none'/>\n </video>", s, flags=re.S) s = re.sub(r"<video>.*?</video>", "<video>\n <model type='none'/>\n </video>", s, flags=re.S)
@@ -827,10 +831,19 @@ if [ "$PROFILE" = full ]; then
fi 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 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 " '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" echo " sudo install -Dm644 $SELF/scripts/99-vfio-native-vnet-offload.rules /etc/udev/rules.d/ && sudo udevadm control --reload-rules"
fi fi
NET=$("${C[@]}" dumpxml "$DOM" 2>/dev/null | sed -n "s/.*<source network='\([^']*\)'.*/\1/p" | head -1)
NETMTU=$([ -n "$NET" ] && "${C[@]}" net-dumpxml --inactive "$NET" 2>/dev/null | sed -n "s/.*<mtu size='\([0-9]*\)'.*/\1/p")
if [ -n "$NET" ] && [ "${NETMTU:-1500}" -lt 9000 ]; then
echo "NOTE: the guest interface asks for MTU 9000 but libvirt network '$NET' is at ${NETMTU:-1500}."
echo " Jumbo needs both ends; inbound throughput is ~5x with it. Add <mtu size='9000'/> 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=$(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" [ "$gov" = performance ] || echo "host governor is '$gov' - run: sudo cpupower frequency-set -g performance"