Hi everyone,
This series tries to fix a deadlock in virtio-net when binding/unbinding
XDP program, XDP socket or resizing the rx queue.
When pausing rx (e.g. set up xdp, xsk pool, rx resize), we call
napi_disable() on the receive queue's napi. In delayed refill_work, it
also calls napi_disable() on the receive queue's napi. When
napi_disable() is called on an already disabled napi, it will sleep in
napi_disable_locked while still holding the netdev_lock. As a result,
later napi_enable gets stuck too as it cannot acquire the netdev_lock.
This leads to refill_work and the pause-then-resume tx are stuck
altogether.
This scenario can be reproducible by binding a XDP socket to virtio-net
interface without setting up the fill ring. As a result, try_fill_recv
will fail until the fill ring is set up and refill_work is scheduled.
This fix adds virtnet_rx_(pause/resume)_all helpers and fixes up the
virtnet_rx_resume to disable future and cancel all inflights delayed
refill_work before calling napi_disable() to pause the rx.
Version 3 changes:
- Patch 1: refactor to avoid code duplication
Version 2 changes:
- Add selftest for deadlock scenario
Thanks,
Quang Minh.
Bui Quang Minh (3):
virtio-net: disable delayed refill when pausing rx
selftests: net: move xdp_helper to net/lib
selftests: net: add a virtio_net deadlock selftest
drivers/net/virtio_net.c | 69 +++++++++++++++----
tools/testing/selftests/Makefile | 2 +-
tools/testing/selftests/drivers/net/Makefile | 2 -
tools/testing/selftests/drivers/net/queues.py | 4 +-
.../selftests/drivers/net/virtio_net/Makefile | 2 +
.../selftests/drivers/net/virtio_net/config | 1 +
.../drivers/net/virtio_net/lib/py/__init__.py | 16 +++++
.../drivers/net/virtio_net/xsk_pool.py | 52 ++++++++++++++
tools/testing/selftests/net/lib/.gitignore | 1 +
tools/testing/selftests/net/lib/Makefile | 1 +
.../{drivers/net => net/lib}/xdp_helper.c | 0
11 files changed, 133 insertions(+), 17 deletions(-)
create mode 100644 tools/testing/selftests/drivers/net/virtio_net/lib/py/__init__.py
create mode 100755 tools/testing/selftests/drivers/net/virtio_net/xsk_pool.py
rename tools/testing/selftests/{drivers/net => net/lib}/xdp_helper.c (100%)
--
2.43.0
When pausing rx (e.g. set up xdp, xsk pool, rx resize), we call
napi_disable() on the receive queue's napi. In delayed refill_work, it
also calls napi_disable() on the receive queue's napi. When
napi_disable() is called on an already disabled napi, it will sleep in
napi_disable_locked while still holding the netdev_lock. As a result,
later napi_enable gets stuck too as it cannot acquire the netdev_lock.
This leads to refill_work and the pause-then-resume tx are stuck
altogether.
This scenario can be reproducible by binding a XDP socket to virtio-net
interface without setting up the fill ring. As a result, try_fill_recv
will fail until the fill ring is set up and refill_work is scheduled.
This commit adds virtnet_rx_(pause/resume)_all helpers and fixes up the
virtnet_rx_resume to disable future and cancel all inflights delayed
refill_work before calling napi_disable() to pause the rx.
Fixes: 413f0271f396 ("net: protect NAPI enablement with netdev_lock()")
Signed-off-by: Bui Quang Minh <redacted>
---
drivers/net/virtio_net.c | 69 +++++++++++++++++++++++++++++++++-------
1 file changed, 57 insertions(+), 12 deletions(-)
@@ -5959,12 +6006,12 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog,if(prog)bpf_prog_add(prog,vi->max_queue_pairs-1);+virtnet_rx_pause_all(vi);+/* Make sure NAPI is not using any XDP TX queues for RX. */if(netif_running(dev)){-for(i=0;i<vi->max_queue_pairs;i++){-virtnet_napi_disable(&vi->rq[i]);+for(i=0;i<vi->max_queue_pairs;i++)virtnet_napi_tx_disable(&vi->sq[i]);-}}if(!prog){
diff --git a/tools/testing/selftests/drivers/net/xdp_helper.c b/tools/testing/selftests/net/lib/xdp_helper.csimilarity index 100%rename from tools/testing/selftests/drivers/net/xdp_helper.crename to tools/testing/selftests/net/lib/xdp_helper.c
--
2.43.0
@@ -0,0 +1,52 @@+#!/usr/bin/env python3+# SPDX-License-Identifier: GPL-2.0++# This is intended to be run on a virtio-net guest interface.+# The test binds the XDP socket to the interface without setting+# the fill ring to trigger delayed refill_work. This helps to+# make it easier to reproduce the deadlock when XDP program,+# XDP socket bind/unbind, rx ring resize race with refill_work on+# the buggy kernel.++fromlib.pyimportksft_exit,ksft_run+fromlib.pyimportKsftSkipEx,KsftFailEx+fromlib.pyimportNetDrvEnv+fromlib.pyimportbkg,ip,cmd,ethtool+importre++def_get_rx_ring_entries(cfg):+output=ethtool(f"-g {cfg.ifname}").stdout+values=re.findall(r'RX:\s+(\d+)',output)+returnint(values[1])++defsetup_xsk(cfg,xdp_queue_id=0)->bkg:+# Probe for support+xdp=cmd(f'{cfg.net_lib_dir/"xdp_helper"} - -',fail=False)+ifxdp.ret==255:+raiseKsftSkipEx('AF_XDP unsupported')+elifxdp.ret>0:+raiseKsftFailEx('unable to create AF_XDP socket')++returnbkg(f'{cfg.net_lib_dir/"xdp_helper"}{cfg.ifindex}{xdp_queue_id}',+ksft_wait=3)++defcheck_xdp_bind(cfg):+ip(f"link set dev %s xdp obj %s sec xdp"%+(cfg.ifname,cfg.net_lib_dir/"xdp_dummy.bpf.o"))+ip(f"link set dev %s xdp off"%cfg.ifname)++defcheck_rx_resize(cfg,queue_size=128):+rx_ring=_get_rx_ring_entries(cfg)+ethtool(f"-G %s rx %d"%(cfg.ifname,queue_size))+ethtool(f"-G %s rx %d"%(cfg.ifname,rx_ring))++defmain():+withNetDrvEnv(__file__,nsim_test=False)ascfg:+xsk_bkg=setup_xsk(cfg)++ksft_run([check_xdp_bind,check_rx_resize],+args=(cfg,))+ksft_exit()++if__name__=="__main__":+main()
From: "Michael S. Tsirkin" <mst@redhat.com> Date: 2025-04-15 13:19:34
On Tue, Apr 15, 2025 at 02:43:39PM +0700, Bui Quang Minh wrote:
When pausing rx (e.g. set up xdp, xsk pool, rx resize), we call
napi_disable() on the receive queue's napi. In delayed refill_work, it
also calls napi_disable() on the receive queue's napi. When
napi_disable() is called on an already disabled napi, it will sleep in
napi_disable_locked while still holding the netdev_lock. As a result,
later napi_enable gets stuck too as it cannot acquire the netdev_lock.
This leads to refill_work and the pause-then-resume tx are stuck
altogether.
This scenario can be reproducible by binding a XDP socket to virtio-net
interface without setting up the fill ring. As a result, try_fill_recv
will fail until the fill ring is set up and refill_work is scheduled.
This commit adds virtnet_rx_(pause/resume)_all helpers and fixes up the
virtnet_rx_resume to disable future and cancel all inflights delayed
refill_work before calling napi_disable() to pause the rx.
Fixes: 413f0271f396 ("net: protect NAPI enablement with netdev_lock()")
Signed-off-by: Bui Quang Minh <redacted>
@@ -5959,12 +6006,12 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog,if(prog)bpf_prog_add(prog,vi->max_queue_pairs-1);+virtnet_rx_pause_all(vi);+/* Make sure NAPI is not using any XDP TX queues for RX. */if(netif_running(dev)){-for(i=0;i<vi->max_queue_pairs;i++){-virtnet_napi_disable(&vi->rq[i]);+for(i=0;i<vi->max_queue_pairs;i++)virtnet_napi_tx_disable(&vi->sq[i]);-}}if(!prog){
From: "Michael S. Tsirkin" <mst@redhat.com> Date: 2025-04-15 14:04:51
On Tue, Apr 15, 2025 at 02:43:38PM +0700, Bui Quang Minh wrote:
Hi everyone,
This series tries to fix a deadlock in virtio-net when binding/unbinding
XDP program, XDP socket or resizing the rx queue.
When pausing rx (e.g. set up xdp, xsk pool, rx resize), we call
napi_disable() on the receive queue's napi. In delayed refill_work, it
also calls napi_disable() on the receive queue's napi. When
napi_disable() is called on an already disabled napi, it will sleep in
napi_disable_locked while still holding the netdev_lock. As a result,
later napi_enable gets stuck too as it cannot acquire the netdev_lock.
This leads to refill_work and the pause-then-resume tx are stuck
altogether.
This scenario can be reproducible by binding a XDP socket to virtio-net
interface without setting up the fill ring. As a result, try_fill_recv
will fail until the fill ring is set up and refill_work is scheduled.
This fix adds virtnet_rx_(pause/resume)_all helpers and fixes up the
virtnet_rx_resume to disable future and cancel all inflights delayed
refill_work before calling napi_disable() to pause the rx.
Version 3 changes:
- Patch 1: refactor to avoid code duplication
Version 2 changes:
- Add selftest for deadlock scenario
Thanks,
Quang Minh.
Unfortunately this doesn't work on a basic QEMU setup:
# ethtool -G eth0 rx 128
[ 15.680655][ T287] virtio_net virtio2 eth0: resize rx fail: rx queue index: 0 err: -2
netlink error: No such file or directory
Is there a way to enable more capable virtio_net with QEMU?
Unfortunately this doesn't work on a basic QEMU setup:
# ethtool -G eth0 rx 128
[ 15.680655][ T287] virtio_net virtio2 eth0: resize rx fail: rx queue index: 0 err: -2
netlink error: No such file or directory
Is there a way to enable more capable virtio_net with QEMU?
I guess that virtio-pci-legacy is used in your setup.
Here is how I setup virtio-net with Qemu
-netdev tap,id=hostnet1,vhost=on,script=$NETWORK_SCRIPT,downscript=no \
-device virtio-net-pci,netdev=hostnet1,iommu_platform=on,disable-legacy=on \
The iommu_platform=on is necessary to make vring use dma API which is a requirement to enable xsk_pool in virtio-net (XDP socket will be in zerocopy mode for this case). Otherwise, the XDP socket will fallback to copy mode, xsk_pool is not enabled in virtio-net that makes the probability to reproduce bug to be very small. Currently, when you don't have iommu_platform=on, you can pass the test even before the fix, so I think I will try to harden the selftest to make it return skip in this case.
Thanks,
Quang Minh.
Unfortunately this doesn't work on a basic QEMU setup:
# ethtool -G eth0 rx 128
[ 15.680655][ T287] virtio_net virtio2 eth0: resize rx fail: rx queue index: 0 err: -2
netlink error: No such file or directory
Is there a way to enable more capable virtio_net with QEMU?
What's the qemu command line and version?
Resize depends on queue_reset which should be supported from Qemu 7.2
I guess that virtio-pci-legacy is used in your setup.
Note that modern devices are used by default.
Here is how I setup virtio-net with Qemu
-netdev tap,id=hostnet1,vhost=on,script=$NETWORK_SCRIPT,downscript=no \
-device
virtio-net-pci,netdev=hostnet1,iommu_platform=on,disable-legacy=on \
The iommu_platform=on is necessary to make vring use dma API which is a
requirement to enable xsk_pool in virtio-net (XDP socket will be in
zerocopy mode for this case). Otherwise, the XDP socket will fallback to
copy mode, xsk_pool is not enabled in virtio-net that makes the
probability to reproduce bug to be very small. Currently, when you don't
have iommu_platform=on, you can pass the test even before the fix, so I
think I will try to harden the selftest to make it return skip in this case.
I would like to keep the resize test as it doesn't require iommu_platform.
Thanks
Unfortunately this doesn't work on a basic QEMU setup:
# ethtool -G eth0 rx 128
[ 15.680655][ T287] virtio_net virtio2 eth0: resize rx fail: rx queue index: 0 err: -2
netlink error: No such file or directory
Is there a way to enable more capable virtio_net with QEMU?
What's the qemu command line and version?
Resize depends on queue_reset which should be supported from Qemu 7.2
quoted
I guess that virtio-pci-legacy is used in your setup.
Note that modern devices are used by default.
quoted
Here is how I setup virtio-net with Qemu
-netdev tap,id=hostnet1,vhost=on,script=$NETWORK_SCRIPT,downscript=no \
-device
virtio-net-pci,netdev=hostnet1,iommu_platform=on,disable-legacy=on \
The iommu_platform=on is necessary to make vring use dma API which is a
requirement to enable xsk_pool in virtio-net (XDP socket will be in
zerocopy mode for this case). Otherwise, the XDP socket will fallback to
copy mode, xsk_pool is not enabled in virtio-net that makes the
probability to reproduce bug to be very small. Currently, when you don't
have iommu_platform=on, you can pass the test even before the fix, so I
think I will try to harden the selftest to make it return skip in this case.
I would like to keep the resize test as it doesn't require iommu_platform.
Okay, in next version I will force the XDP socket binding to zerocopy to setup xsk_pool. When the binding fails, 2 tests still run but I will print a warning message.
Thanks,
Quang Minh.
From: Jakub Kicinski <kuba@kernel.org> Date: 2025-04-17 00:08:59
On Wed, 16 Apr 2025 15:46:42 +0800 Jason Wang wrote:
On Wed, Apr 16, 2025 at 2:54 PM Bui Quang Minh [off-list ref] wrote:
quoted
On 4/16/25 11:27, Jakub Kicinski wrote:
quoted
Unfortunately this doesn't work on a basic QEMU setup:
# ethtool -G eth0 rx 128
[ 15.680655][ T287] virtio_net virtio2 eth0: resize rx fail: rx queue index: 0 err: -2
netlink error: No such file or directory
Is there a way to enable more capable virtio_net with QEMU?
What's the qemu command line and version?
Resize depends on queue_reset which should be supported from Qemu 7.2
I'm using virtme-ng with --net loop and:
QEMU emulator version 9.1.3 (qemu-9.1.3-2.fc41)
--net loop resolves to:
-device virtio-net-device,netdev=n0 \
-netdev hubport,id=n0,hubid=0 \
-device virtio-net-device,netdev=n1 \
-netdev hubport,id=n1,hubid=0
quoted
I guess that virtio-pci-legacy is used in your setup.
Note that modern devices are used by default.
quoted
Here is how I setup virtio-net with Qemu
-netdev tap,id=hostnet1,vhost=on,script=$NETWORK_SCRIPT,downscript=no \
-device
virtio-net-pci,netdev=hostnet1,iommu_platform=on,disable-legacy=on \
That works! I rejigged the CI, for posterity I used two times:
-device virtio-net-pci,netdev=n0,iommu_platform=on,disable-legacy=on,mq=on,vectors=18
-netdev tap,id=n0,ifname=tap4,vhost=on,script=no,downscript=no,queues=8
and then manually bridged the taps together on the hypervisor side.
quoted
The iommu_platform=on is necessary to make vring use dma API which is a
requirement to enable xsk_pool in virtio-net (XDP socket will be in
zerocopy mode for this case). Otherwise, the XDP socket will fallback to
copy mode, xsk_pool is not enabled in virtio-net that makes the
probability to reproduce bug to be very small. Currently, when you don't
have iommu_platform=on, you can pass the test even before the fix, so I
think I will try to harden the selftest to make it return skip in this case.
I would like to keep the resize test as it doesn't require iommu_platform.
Sounds good but lets just add them to the drivers/net/hw directory.
I don't think there's anything virtio specific in the test itself?
Right now drivers/net/virtio_net has a test which expects to see
both netdevs in the VM, while drivers / Python based tests expect
to have the env prepared where only one end is on the local machine,
and the other is accessible over SSH or in another netns. So it's a bit
painful to marry the two kinds of tests in the CI. At least our netdev
CI does not know how to figure this out :( It preps the env and then
runs the whole kselftest TARGET in the same setup.