Permanent virtual-network channel deadlock on sun4v (lost RX interrupt).
Affected software: drivers/net/ethernet/sun/sunvnet_common.c — mainline Linux,
all sun4v (LDoms) guests using sunvnet / ldmvsw. Verified on Debian sid
kernel 7.1.4-1 (7.1.4+deb14-sparc64-smp)
Hardware setup used for tests: UltraSPARC T2+ machine (SPARC Enterprise T5240), Hypervisor 1.10.7.f, guest of Oracle VM Server for SPARC 3.5.0.3.3
Under sustained transfer, a vnet interface stalls completely and permanently in
both directions. Nothing is logged, no error/drop counters increment on either
end, and the channel never recovers on its own. Only an interface bounce
(ip link set ethX down && ip link set ethX up, sometimes needed twice —
the race can also strike the recovery VIO handshake) restores traffic. On the
test system the stall reproduced within seconds to minutes of bulk traffic
(~5 race hits per second under load; the stock driver deadlocks on the first).
Solaris vsw peer signature during a stall (kstat, per affected channel):
dring_data_msgs_sent = dring_data_acks_rcvd + N (N = 1..6), both frozen;
reverse direction balanced. Guest signature: the channel's RX IRQ count in
/proc/interrupts frozen while the peer demonstrably keeps writing.
The sun4v LDC RX interrupt (cookie VIRQ) is generated only when the RX queue makes an empty → non-empty transition while the VIRQ is valid. An event that occurs while the VIRQ is invalid is not latched by the hypervisor.
sunvnet's NAPI flow:
ldc_rx(hard IRQ) →sunvnet_event_common→vio_set_intr(rx_ino, HV_INTR_DISABLED)(=sun4v_vintr_set_validoff) →napi_schedule(sunvnet_common.c:935).- NAPI:
sunvnet_poll_common→vnet_event_napi→ldc_read→read_nonrawuntil it observesrx_head == rx_tail(ldc.c:1756). [T1] napi_complete_done→vio_set_intr(rx_ino, HV_INTR_ENABLED)(sunvnet_common.c:923). [T2] No queue re-check follows.
A message written by the peer inside (T1, T2) makes the queue's only empty → non-empty transition while the VIRQ is invalid: no interrupt, event not latched. After T2 the queue is non-empty and never transitions again, so no interrupt can ever fire. The peer's ACKs for guest TX sit unread in the same dead RX queue, so the guest's VIO TX window exhausts too — the stall is bidirectional. The Solaris vsw suppresses further DRING_DATA doorbells while any are unacked, so nothing on the wire ever re-raises the channel.
Applies to Debian kernel 7.1.4+deb14-sparc64-smp (linux-source-7.1 7.1.4-1).
Only two modules are rebuilt: sunvnet_common.ko and sunvnet.ko. My running
kernel has CONFIG_MODVERSIONS and CONFIG_MODULE_SIG_FORCE off, so a plain
rebuild with matching vermagic loads cleanly.
cd /usr/src
tar xJf linux-source-7.1.tar.xz # if not already unpacked
cd linux-source-7.1
cp /boot/config-$(uname -r) .config
make olddefconfig
make -j8 modules_prepare
# Fix vermagic by hand:
echo "#define UTS_RELEASE \"$(uname -r)\"" > include/generated/utsrelease.h
# Apply the patch
patch -p1 < /root/sunvnet-rx-race-fix/sunvnet-rx-race-fix.patch
# No Module.symvers exists without a full kernel build, so modpost must be
# told to warn (not fail) on symbols resolved at load time from vmlinux:
make M=drivers/net/ethernet/sun modules KBUILD_MODPOST_WARN=1
modinfo -F vermagic drivers/net/ethernet/sun/sunvnet_common.ko
# must print proper kernel version stringupdates/ takes precedence over kernel/ in depmod's search order, so the
stock .ko.xz files stay untouched as an automatic fallback:
mkdir -p /lib/modules/$(uname -r)/updates
cp /usr/src/linux-source-7.1/drivers/net/ethernet/sun/sunvnet_common.ko \
/usr/src/linux-source-7.1/drivers/net/ethernet/sun/sunvnet.ko \
/lib/modules/$(uname -r)/updates/
depmod -a
modinfo -n sunvnet_common # must print the /updates/ pathThen reboot the machine.
sunvnet is not in the initramfs (root is on sunvdc), so no
update-initramfs is needed; verify with
lsinitramfs /boot/initrd.img-$(uname -r) | grep sunvnet if in doubt.
modinfo -n sunvnet_common # /lib/modules/.../updates/... path
cat /sys/module/sunvnet_common/parameters/rx_race_recovered
# The parameter only exists in the patched module. It counts recovered
# lost-RX-interrupt events; expect it to grow under bulk traffic (~5/s on a
# T2+). Each increment is a deadlock that did not happen.
dmesg | grep "recovered lost RX"rm /lib/modules/$(uname -r)/updates/sunvnet_common.ko \
/lib/modules/$(uname -r)/updates/sunvnet.ko
depmod -a
# then reboot, or repeat the detached reload script (it will load stock)- A kernel package upgrade installs a new
/lib/modules/<newver>/tree; the fix must be rebuilt/reinstalled for the new kernel (or the fix accepted into the distro/upstream kernel).