selftests: drv-net: add xdp cases for ping.py

ping.py has 3 cases, test_v4, test_v6 and test_tcp.
But these cases are not executed on the XDP environment.
So, it adds XDP environment, existing tests(test_v4, test_v6, and
test_tcp) are executed too on the below XDP environment.
So, it adds XDP cases.
1. xdp-generic + single-buffer
2. xdp-generic + multi-buffer
3. xdp-native + single-buffer
4. xdp-native + multi-buffer
5. xdp-offload

It also makes test_{v4 | v6 | tcp} sending large size packets. this may
help to check whether multi-buffer is working or not.

Note that the physical interface may be down and then up when xdp is
attached or detached.
This takes some period to activate traffic. So sleep(10) is
added if the test interface is the physical interface.
netdevsim and veth type interfaces skip sleep.

Signed-off-by: Taehee Yoo <ap420073@gmail.com>
Link: https://patch.msgid.link/20250309134219.91670-9-ap420073@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Taehee Yoo 2025-03-09 13:42:19 +00:00 committed by Jakub Kicinski
parent a70f891e0f
commit 75cc19c8ff
2 changed files with 190 additions and 14 deletions

View file

@ -1,49 +1,219 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
import os
import random, string, time
from lib.py import ksft_run, ksft_exit from lib.py import ksft_run, ksft_exit
from lib.py import ksft_eq from lib.py import ksft_eq, KsftSkipEx, KsftFailEx
from lib.py import NetDrvEpEnv from lib.py import EthtoolFamily, NetDrvEpEnv
from lib.py import bkg, cmd, wait_port_listen, rand_port from lib.py import bkg, cmd, wait_port_listen, rand_port
from lib.py import ethtool, ip
remote_ifname=""
no_sleep=False
def test_v4(cfg) -> None: def _test_v4(cfg) -> None:
cfg.require_v4() cfg.require_v4()
cmd(f"ping -c 1 -W0.5 {cfg.remote_v4}") cmd(f"ping -c 1 -W0.5 {cfg.remote_v4}")
cmd(f"ping -c 1 -W0.5 {cfg.v4}", host=cfg.remote) cmd(f"ping -c 1 -W0.5 {cfg.v4}", host=cfg.remote)
cmd(f"ping -s 65000 -c 1 -W0.5 {cfg.remote_v4}")
cmd(f"ping -s 65000 -c 1 -W0.5 {cfg.v4}", host=cfg.remote)
def _test_v6(cfg) -> None:
def test_v6(cfg) -> None:
cfg.require_v6() cfg.require_v6()
cmd(f"ping -c 1 -W0.5 {cfg.remote_v6}") cmd(f"ping -c 1 -W5 {cfg.remote_v6}")
cmd(f"ping -c 1 -W0.5 {cfg.v6}", host=cfg.remote) cmd(f"ping -c 1 -W5 {cfg.v6}", host=cfg.remote)
cmd(f"ping -s 65000 -c 1 -W0.5 {cfg.remote_v6}")
cmd(f"ping -s 65000 -c 1 -W0.5 {cfg.v6}", host=cfg.remote)
def _test_tcp(cfg) -> None:
def test_tcp(cfg) -> None:
cfg.require_cmd("socat", remote=True) cfg.require_cmd("socat", remote=True)
port = rand_port() port = rand_port()
listen_cmd = f"socat -{cfg.addr_ipver} -t 2 -u TCP-LISTEN:{port},reuseport STDOUT" listen_cmd = f"socat -{cfg.addr_ipver} -t 2 -u TCP-LISTEN:{port},reuseport STDOUT"
test_string = ''.join(random.choice(string.ascii_lowercase) for _ in range(65536))
with bkg(listen_cmd, exit_wait=True) as nc: with bkg(listen_cmd, exit_wait=True) as nc:
wait_port_listen(port) wait_port_listen(port)
cmd(f"echo ping | socat -t 2 -u STDIN TCP:{cfg.baddr}:{port}", cmd(f"echo {test_string} | socat -t 2 -u STDIN TCP:{cfg.baddr}:{port}",
shell=True, host=cfg.remote) shell=True, host=cfg.remote)
ksft_eq(nc.stdout.strip(), "ping") ksft_eq(nc.stdout.strip(), test_string)
test_string = ''.join(random.choice(string.ascii_lowercase) for _ in range(65536))
with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as nc: with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as nc:
wait_port_listen(port, host=cfg.remote) wait_port_listen(port, host=cfg.remote)
cmd(f"echo ping | socat -t 2 -u STDIN TCP:{cfg.remote_baddr}:{port}", shell=True) cmd(f"echo {test_string} | socat -t 2 -u STDIN TCP:{cfg.remote_baddr}:{port}", shell=True)
ksft_eq(nc.stdout.strip(), "ping") ksft_eq(nc.stdout.strip(), test_string)
def _set_offload_checksum(cfg, netnl, on) -> None:
try:
ethtool(f" -K {cfg.ifname} rx {on} tx {on} ")
except:
return
def _set_xdp_generic_sb_on(cfg) -> None:
test_dir = os.path.dirname(os.path.realpath(__file__))
prog = test_dir + "/../../net/lib/xdp_dummy.bpf.o"
cmd(f"ip link set dev {remote_ifname} mtu 1500", shell=True, host=cfg.remote)
cmd(f"ip link set dev {cfg.ifname} mtu 1500 xdpgeneric obj {prog} sec xdp", shell=True)
if no_sleep != True:
time.sleep(10)
def _set_xdp_generic_mb_on(cfg) -> None:
test_dir = os.path.dirname(os.path.realpath(__file__))
prog = test_dir + "/../../net/lib/xdp_dummy.bpf.o"
cmd(f"ip link set dev {remote_ifname} mtu 9000", shell=True, host=cfg.remote)
ip("link set dev %s mtu 9000 xdpgeneric obj %s sec xdp.frags" % (cfg.ifname, prog))
if no_sleep != True:
time.sleep(10)
def _set_xdp_native_sb_on(cfg) -> None:
test_dir = os.path.dirname(os.path.realpath(__file__))
prog = test_dir + "/../../net/lib/xdp_dummy.bpf.o"
cmd(f"ip link set dev {remote_ifname} mtu 1500", shell=True, host=cfg.remote)
cmd(f"ip -j link set dev {cfg.ifname} mtu 1500 xdp obj {prog} sec xdp", shell=True)
xdp_info = ip("-d link show %s" % (cfg.ifname), json=True)[0]
if xdp_info['xdp']['mode'] != 1:
"""
If the interface doesn't support native-mode, it falls back to generic mode.
The mode value 1 is native and 2 is generic.
So it raises an exception if mode is not 1(native mode).
"""
raise KsftSkipEx('device does not support native-XDP')
if no_sleep != True:
time.sleep(10)
def _set_xdp_native_mb_on(cfg) -> None:
test_dir = os.path.dirname(os.path.realpath(__file__))
prog = test_dir + "/../../net/lib/xdp_dummy.bpf.o"
cmd(f"ip link set dev {remote_ifname} mtu 9000", shell=True, host=cfg.remote)
try:
cmd(f"ip link set dev {cfg.ifname} mtu 9000 xdp obj {prog} sec xdp.frags", shell=True)
except Exception as e:
cmd(f"ip link set dev {remote_ifname} mtu 1500", shell=True, host=cfg.remote)
raise KsftSkipEx('device does not support native-multi-buffer XDP')
if no_sleep != True:
time.sleep(10)
def _set_xdp_offload_on(cfg) -> None:
test_dir = os.path.dirname(os.path.realpath(__file__))
prog = test_dir + "/../../net/lib/xdp_dummy.bpf.o"
cmd(f"ip link set dev {cfg.ifname} mtu 1500", shell=True)
try:
cmd(f"ip link set dev {cfg.ifname} xdpoffload obj {prog} sec xdp", shell=True)
except Exception as e:
raise KsftSkipEx('device does not support offloaded XDP')
cmd(f"ip link set dev {remote_ifname} mtu 1500", shell=True, host=cfg.remote)
if no_sleep != True:
time.sleep(10)
def get_interface_info(cfg) -> None:
global remote_ifname
global no_sleep
remote_info = cmd(f"ip -4 -o addr show to {cfg.remote_v4} | awk '{{print $2}}'", shell=True, host=cfg.remote).stdout
remote_ifname = remote_info.rstrip('\n')
if remote_ifname == "":
raise KsftFailEx('Can not get remote interface')
local_info = ip("-d link show %s" % (cfg.ifname), json=True)[0]
if 'parentbus' in local_info and local_info['parentbus'] == "netdevsim":
no_sleep=True
if 'linkinfo' in local_info and local_info['linkinfo']['info_kind'] == "veth":
no_sleep=True
def set_interface_init(cfg) -> None:
cmd(f"ip link set dev {cfg.ifname} mtu 1500", shell=True)
cmd(f"ip link set dev {cfg.ifname} xdp off ", shell=True)
cmd(f"ip link set dev {cfg.ifname} xdpgeneric off ", shell=True)
cmd(f"ip link set dev {cfg.ifname} xdpoffload off", shell=True)
cmd(f"ip link set dev {remote_ifname} mtu 1500", shell=True, host=cfg.remote)
def test_default(cfg, netnl) -> None:
_set_offload_checksum(cfg, netnl, "off")
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
_set_offload_checksum(cfg, netnl, "on")
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
def test_xdp_generic_sb(cfg, netnl) -> None:
_set_xdp_generic_sb_on(cfg)
_set_offload_checksum(cfg, netnl, "off")
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
_set_offload_checksum(cfg, netnl, "on")
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
ip("link set dev %s xdpgeneric off" % cfg.ifname)
def test_xdp_generic_mb(cfg, netnl) -> None:
_set_xdp_generic_mb_on(cfg)
_set_offload_checksum(cfg, netnl, "off")
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
_set_offload_checksum(cfg, netnl, "on")
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
ip("link set dev %s xdpgeneric off" % cfg.ifname)
def test_xdp_native_sb(cfg, netnl) -> None:
_set_xdp_native_sb_on(cfg)
_set_offload_checksum(cfg, netnl, "off")
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
_set_offload_checksum(cfg, netnl, "on")
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
ip("link set dev %s xdp off" % cfg.ifname)
def test_xdp_native_mb(cfg, netnl) -> None:
_set_xdp_native_mb_on(cfg)
_set_offload_checksum(cfg, netnl, "off")
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
_set_offload_checksum(cfg, netnl, "on")
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
ip("link set dev %s xdp off" % cfg.ifname)
def test_xdp_offload(cfg, netnl) -> None:
_set_xdp_offload_on(cfg)
_test_v4(cfg)
_test_v6(cfg)
_test_tcp(cfg)
ip("link set dev %s xdpoffload off" % cfg.ifname)
def main() -> None: def main() -> None:
with NetDrvEpEnv(__file__) as cfg: with NetDrvEpEnv(__file__) as cfg:
ksft_run(globs=globals(), case_pfx={"test_"}, args=(cfg, )) get_interface_info(cfg)
set_interface_init(cfg)
ksft_run([test_default,
test_xdp_generic_sb,
test_xdp_generic_mb,
test_xdp_native_sb,
test_xdp_native_mb,
test_xdp_offload],
args=(cfg, EthtoolFamily()))
set_interface_init(cfg)
ksft_exit() ksft_exit()

View file

@ -10,4 +10,10 @@ int xdp_dummy_prog(struct xdp_md *ctx)
return XDP_PASS; return XDP_PASS;
} }
SEC("xdp.frags")
int xdp_dummy_prog_frags(struct xdp_md *ctx)
{
return XDP_PASS;
}
char _license[] SEC("license") = "GPL"; char _license[] SEC("license") = "GPL";