Merge branch 'virtio-net-disable-delayed-refill-when-pausing-rx'

Bui Quang Minh says:

====================
virtio-net: disable delayed refill when pausing rx

Hi everyone,

This only includes the selftest for virtio-net deadlock bug. The fix
commit has been applied already.

Link: https://lore.kernel.org/virtualization/174537302875.2111809.8543884098526067319.git-patchwork-notify@kernel.org/T/
====================

Link: https://patch.msgid.link/20250425071018.36078-1-minhquangbui99@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-04-28 15:49:14 -07:00
10 changed files with 98 additions and 12 deletions

View File

@@ -1,3 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
napi_id_helper
xdp_helper

View File

@@ -8,7 +8,6 @@ TEST_INCLUDES := $(wildcard lib/py/*.py) \
TEST_GEN_FILES := \
napi_id_helper \
xdp_helper \
# end of TEST_GEN_FILES
TEST_PROGS := \

View File

@@ -21,6 +21,7 @@ TEST_PROGS = \
rss_ctx.py \
rss_input_xfrm.py \
tso.py \
xsk_reconfig.py \
#
TEST_FILES := \

View File

@@ -0,0 +1,60 @@
#!/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.
#
# The Qemu command to setup virtio-net
# -netdev tap,id=hostnet1,vhost=on,script=no,downscript=no
# -device virtio-net-pci,netdev=hostnet1,iommu_platform=on,disable-legacy=on
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 time
def _get_rx_ring_entries(cfg):
output = ethtool(f"-g {cfg.ifname}", json=True)
return output[0]["rx"]
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')
try:
return bkg(f'{cfg.net_lib_dir / "xdp_helper"} {cfg.ifindex} ' \
'{xdp_queue_id} -z', ksft_wait=3)
except:
raise KsftSkipEx('Failed to bind XDP socket in zerocopy.\n' \
'Please consider adding iommu_platform=on ' \
'when setting up virtio-net-pci')
def check_xdp_bind(cfg):
with setup_xsk(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):
with setup_xsk(cfg):
rx_ring = _get_rx_ring_entries(cfg)
ethtool(f"-G %s rx %d" % (cfg.ifname, rx_ring // 2))
ethtool(f"-G %s rx %d" % (cfg.ifname, rx_ring))
def main():
with NetDrvEnv(__file__, nsim_test=False) as cfg:
ksft_run([check_xdp_bind, check_rx_resize],
args=(cfg, ))
ksft_exit()
if __name__ == "__main__":
main()

View File

@@ -8,7 +8,7 @@
#include <arpa/inet.h>
#include <sys/socket.h>
#include "ksft.h"
#include "../../net/lib/ksft.h"
int main(int argc, char *argv[])
{

View File

@@ -26,13 +26,13 @@ def nl_get_queues(cfg, nl, qtype='rx'):
def check_xsk(cfg, nl, xdp_queue_id=0) -> None:
# Probe for support
xdp = cmd(f'{cfg.test_dir / "xdp_helper"} - -', fail=False)
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')
with bkg(f'{cfg.test_dir / "xdp_helper"} {cfg.ifindex} {xdp_queue_id}',
with bkg(f'{cfg.net_lib_dir / "xdp_helper"} {cfg.ifindex} {xdp_queue_id}',
ksft_wait=3):
rx = tx = False

View File

@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
csum
xdp_helper

View File

@@ -10,6 +10,7 @@ TEST_FILES += ../../../../net/ynl
TEST_GEN_FILES += csum
TEST_GEN_FILES += $(patsubst %.c,%.o,$(wildcard *.bpf.c))
TEST_GEN_FILES += xdp_helper
TEST_INCLUDES := $(wildcard py/*.py sh/*.sh)

View File

@@ -17,6 +17,12 @@
#define NUM_DESC (UMEM_SZ / 2048)
static void print_usage(const char *bin)
{
fprintf(stderr, "Usage: %s ifindex queue_id [-z]\n\n"
"where:\n\t-z: force zerocopy mode", bin);
}
/* this is a simple helper program that creates an XDP socket and does the
* minimum necessary to get bind() to succeed.
*
@@ -32,12 +38,13 @@ int main(int argc, char **argv)
struct sockaddr_xdp sxdp = { 0 };
int num_desc = NUM_DESC;
void *umem_area;
int retry = 0;
int ifindex;
int sock_fd;
int queue;
if (argc != 3) {
fprintf(stderr, "Usage: %s ifindex queue_id\n", argv[0]);
if (argc != 3 && argc != 4) {
print_usage(argv[0]);
return 1;
}
@@ -87,11 +94,29 @@ int main(int argc, char **argv)
sxdp.sxdp_queue_id = queue;
sxdp.sxdp_flags = 0;
if (bind(sock_fd, (struct sockaddr *)&sxdp, sizeof(sxdp)) != 0) {
munmap(umem_area, UMEM_SZ);
perror("bind failed");
close(sock_fd);
return 1;
if (argc > 3) {
if (!strcmp(argv[3], "-z")) {
sxdp.sxdp_flags = XDP_ZEROCOPY;
} else {
print_usage(argv[0]);
return 1;
}
}
while (1) {
if (bind(sock_fd, (struct sockaddr *)&sxdp, sizeof(sxdp)) == 0)
break;
if (errno == EBUSY && retry < 3) {
retry++;
sleep(1);
continue;
} else {
perror("bind failed");
munmap(umem_area, UMEM_SZ);
close(sock_fd);
return 1;
}
}
ksft_ready();