diff mbox series

[v3,3/3] selftests: net: add a virtio_net deadlock selftest

Message ID 20250415074341.12461-4-minhquangbui99@gmail.com (mailing list archive)
State Superseded
Headers show
Series virtio-net: disable delayed refill when pausing rx | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Guessed tree name to be net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 1 this patch: 1
netdev/build_tools success Errors and warnings before: 26 (+2) this patch: 26 (+2)
netdev/cc_maintainers warning 3 maintainers not CCed: shuah@kernel.org linux-kselftest@vger.kernel.org willemb@google.com
netdev/build_clang success Errors and warnings before: 2 this patch: 2
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 6 this patch: 6
netdev/checkpatch warning WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest fail net-next-2025-04-16--00-00 (tests: 916)

Commit Message

Bui Quang Minh April 15, 2025, 7:43 a.m. UTC
The selftest reproduces the deadlock scenario when binding/unbinding XDP
program, XDP socket, rx ring resize on virtio_net interface.

Signed-off-by: Bui Quang Minh <minhquangbui99@gmail.com>
---
 tools/testing/selftests/Makefile              |  2 +-
 .../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 +++++++++++++++++++
 5 files changed, 72 insertions(+), 1 deletion(-)
 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

Comments

Jakub Kicinski April 16, 2025, 4:27 a.m. UTC | #1
On Tue, 15 Apr 2025 14:43:41 +0700 Bui Quang Minh wrote:
> +def setup_xsk(cfg, xdp_queue_id = 0) -> bkg:
> +    # Probe for support
> +    xdp = cmd(f'{cfg.net_lib_dir / "xdp_helper"} - -', fail=False)
> +    if xdp.ret == 255:
> +        raise KsftSkipEx('AF_XDP unsupported')
> +    elif xdp.ret > 0:
> +        raise KsftFailEx('unable to create AF_XDP socket')
> +
> +    return bkg(f'{cfg.net_lib_dir / "xdp_helper"} {cfg.ifindex} {xdp_queue_id}',
> +               ksft_wait=3)
> +
> +def check_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)
> +
> +def check_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))

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?
Bui Quang Minh April 16, 2025, 6:54 a.m. UTC | #2
On 4/16/25 11:27, Jakub Kicinski wrote:
> On Tue, 15 Apr 2025 14:43:41 +0700 Bui Quang Minh wrote:
>> +def setup_xsk(cfg, xdp_queue_id = 0) -> bkg:
>> +    # Probe for support
>> +    xdp = cmd(f'{cfg.net_lib_dir / "xdp_helper"} - -', fail=False)
>> +    if xdp.ret == 255:
>> +        raise KsftSkipEx('AF_XDP unsupported')
>> +    elif xdp.ret > 0:
>> +        raise KsftFailEx('unable to create AF_XDP socket')
>> +
>> +    return bkg(f'{cfg.net_lib_dir / "xdp_helper"} {cfg.ifindex} {xdp_queue_id}',
>> +               ksft_wait=3)
>> +
>> +def check_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)
>> +
>> +def check_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))
> 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.
Jason Wang April 16, 2025, 7:46 a.m. UTC | #3
On Wed, Apr 16, 2025 at 2:54 PM Bui Quang Minh <minhquangbui99@gmail.com> wrote:
>
> On 4/16/25 11:27, Jakub Kicinski wrote:
> > On Tue, 15 Apr 2025 14:43:41 +0700 Bui Quang Minh wrote:
> >> +def setup_xsk(cfg, xdp_queue_id = 0) -> bkg:
> >> +    # Probe for support
> >> +    xdp = cmd(f'{cfg.net_lib_dir / "xdp_helper"} - -', fail=False)
> >> +    if xdp.ret == 255:
> >> +        raise KsftSkipEx('AF_XDP unsupported')
> >> +    elif xdp.ret > 0:
> >> +        raise KsftFailEx('unable to create AF_XDP socket')
> >> +
> >> +    return bkg(f'{cfg.net_lib_dir / "xdp_helper"} {cfg.ifindex} {xdp_queue_id}',
> >> +               ksft_wait=3)
> >> +
> >> +def check_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)
> >> +
> >> +def check_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))
> > 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

>
> Thanks,
> Quang Minh.
>
Bui Quang Minh April 16, 2025, 9 a.m. UTC | #4
On 4/16/25 14:46, Jason Wang wrote:
> On Wed, Apr 16, 2025 at 2:54 PM Bui Quang Minh <minhquangbui99@gmail.com> wrote:
>> On 4/16/25 11:27, Jakub Kicinski wrote:
>>> On Tue, 15 Apr 2025 14:43:41 +0700 Bui Quang Minh wrote:
>>>> +def setup_xsk(cfg, xdp_queue_id = 0) -> bkg:
>>>> +    # Probe for support
>>>> +    xdp = cmd(f'{cfg.net_lib_dir / "xdp_helper"} - -', fail=False)
>>>> +    if xdp.ret == 255:
>>>> +        raise KsftSkipEx('AF_XDP unsupported')
>>>> +    elif xdp.ret > 0:
>>>> +        raise KsftFailEx('unable to create AF_XDP socket')
>>>> +
>>>> +    return bkg(f'{cfg.net_lib_dir / "xdp_helper"} {cfg.ifindex} {xdp_queue_id}',
>>>> +               ksft_wait=3)
>>>> +
>>>> +def check_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)
>>>> +
>>>> +def check_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))
>>> 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.

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.
Jakub Kicinski April 17, 2025, 12:08 a.m. UTC | #5
On Wed, 16 Apr 2025 15:46:42 +0800 Jason Wang wrote:
> On Wed, Apr 16, 2025 at 2:54 PM Bui Quang Minh <minhquangbui99@gmail.com> wrote:
> > On 4/16/25 11:27, Jakub Kicinski wrote:  
> > > 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

> > 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 \

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.

> > 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.
diff mbox series

Patch

diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index c77c8c8e3d9b..0a6b096f98b7 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -130,7 +130,7 @@  TARGETS_HOTPLUG = cpu-hotplug
 TARGETS_HOTPLUG += memory-hotplug
 
 # Networking tests want the net/lib target, include it automatically
-ifneq ($(filter net drivers/net drivers/net/hw,$(TARGETS)),)
+ifneq ($(filter net drivers/net drivers/net/hw drivers/net/virtio_net,$(TARGETS)),)
 ifeq ($(filter net/lib,$(TARGETS)),)
 	INSTALL_DEP_TARGETS := net/lib
 endif
diff --git a/tools/testing/selftests/drivers/net/virtio_net/Makefile b/tools/testing/selftests/drivers/net/virtio_net/Makefile
index 7ec7cd3ab2cc..82adda96ef15 100644
--- a/tools/testing/selftests/drivers/net/virtio_net/Makefile
+++ b/tools/testing/selftests/drivers/net/virtio_net/Makefile
@@ -1,6 +1,7 @@ 
 # SPDX-License-Identifier: GPL-2.0+ OR MIT
 
 TEST_PROGS = basic_features.sh \
+        xsk_pool.py \
         #
 
 TEST_FILES = \
@@ -8,6 +9,7 @@  TEST_FILES = \
         #
 
 TEST_INCLUDES = \
+        $(wildcard lib/py/*.py ../lib/py/*.py) \
         ../../../net/forwarding/lib.sh \
         ../../../net/lib.sh \
         #
diff --git a/tools/testing/selftests/drivers/net/virtio_net/config b/tools/testing/selftests/drivers/net/virtio_net/config
index bcf7555eaffe..12e8caa22613 100644
--- a/tools/testing/selftests/drivers/net/virtio_net/config
+++ b/tools/testing/selftests/drivers/net/virtio_net/config
@@ -6,3 +6,4 @@  CONFIG_NET_L3_MASTER_DEV=y
 CONFIG_NET_VRF=m
 CONFIG_VIRTIO_DEBUG=y
 CONFIG_VIRTIO_NET=y
+CONFIG_XDP_SOCKETS=y
diff --git a/tools/testing/selftests/drivers/net/virtio_net/lib/py/__init__.py b/tools/testing/selftests/drivers/net/virtio_net/lib/py/__init__.py
new file mode 100644
index 000000000000..b582885786f5
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/virtio_net/lib/py/__init__.py
@@ -0,0 +1,16 @@ 
+# SPDX-License-Identifier: GPL-2.0
+
+import sys
+from pathlib import Path
+
+KSFT_DIR = (Path(__file__).parent / "../../../../..").resolve()
+
+try:
+    sys.path.append(KSFT_DIR.as_posix())
+    from net.lib.py import *
+    from drivers.net.lib.py import *
+except ModuleNotFoundError as e:
+    ksft_pr("Failed importing `net` library from kernel sources")
+    ksft_pr(str(e))
+    ktap_result(True, comment="SKIP")
+    sys.exit(4)
diff --git a/tools/testing/selftests/drivers/net/virtio_net/xsk_pool.py b/tools/testing/selftests/drivers/net/virtio_net/xsk_pool.py
new file mode 100755
index 000000000000..3f80afd97d4e
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/virtio_net/xsk_pool.py
@@ -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.
+
+from lib.py import ksft_exit, ksft_run
+from lib.py import KsftSkipEx, KsftFailEx
+from lib.py import NetDrvEnv
+from lib.py import bkg, ip, cmd, ethtool
+import re
+
+def _get_rx_ring_entries(cfg):
+    output = ethtool(f"-g {cfg.ifname}").stdout
+    values = re.findall(r'RX:\s+(\d+)', output)
+    return int(values[1])
+
+def setup_xsk(cfg, xdp_queue_id = 0) -> bkg:
+    # Probe for support
+    xdp = cmd(f'{cfg.net_lib_dir / "xdp_helper"} - -', fail=False)
+    if xdp.ret == 255:
+        raise KsftSkipEx('AF_XDP unsupported')
+    elif xdp.ret > 0:
+        raise KsftFailEx('unable to create AF_XDP socket')
+
+    return bkg(f'{cfg.net_lib_dir / "xdp_helper"} {cfg.ifindex} {xdp_queue_id}',
+               ksft_wait=3)
+
+def check_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)
+
+def check_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))
+
+def main():
+    with NetDrvEnv(__file__, nsim_test=False) as cfg:
+        xsk_bkg = setup_xsk(cfg)
+
+        ksft_run([check_xdp_bind, check_rx_resize],
+                 args=(cfg, ))
+    ksft_exit()
+
+if __name__ == "__main__":
+    main()