mirror of https://github.com/torvalds/linux.git
659 lines
22 KiB
Python
Executable File
659 lines
22 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
|
|
"""
|
|
This file contains tests to verify native XDP support in network drivers.
|
|
The tests utilize the BPF program `xdp_native.bpf.o` from the `selftests.net.lib`
|
|
directory, with each test focusing on a specific aspect of XDP functionality.
|
|
"""
|
|
import random
|
|
import string
|
|
from dataclasses import dataclass
|
|
from enum import Enum
|
|
|
|
from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_ne, ksft_pr
|
|
from lib.py import KsftFailEx, NetDrvEpEnv, EthtoolFamily, NlError
|
|
from lib.py import bkg, cmd, rand_port, wait_port_listen
|
|
from lib.py import ip, bpftool, defer
|
|
|
|
|
|
class TestConfig(Enum):
|
|
"""Enum for XDP configuration options."""
|
|
MODE = 0 # Configures the BPF program for a specific test
|
|
PORT = 1 # Port configuration to communicate with the remote host
|
|
ADJST_OFFSET = 2 # Tail/Head adjustment offset for extension/shrinking
|
|
ADJST_TAG = 3 # Adjustment tag to annotate the start and end of extension
|
|
|
|
|
|
class XDPAction(Enum):
|
|
"""Enum for XDP actions."""
|
|
PASS = 0 # Pass the packet up to the stack
|
|
DROP = 1 # Drop the packet
|
|
TX = 2 # Route the packet to the remote host
|
|
TAIL_ADJST = 3 # Adjust the tail of the packet
|
|
HEAD_ADJST = 4 # Adjust the head of the packet
|
|
|
|
|
|
class XDPStats(Enum):
|
|
"""Enum for XDP statistics."""
|
|
RX = 0 # Count of valid packets received for testing
|
|
PASS = 1 # Count of packets passed up to the stack
|
|
DROP = 2 # Count of packets dropped
|
|
TX = 3 # Count of incoming packets routed to the remote host
|
|
ABORT = 4 # Count of packets that were aborted
|
|
|
|
|
|
@dataclass
|
|
class BPFProgInfo:
|
|
"""Data class to store information about a BPF program."""
|
|
name: str # Name of the BPF program
|
|
file: str # BPF program object file
|
|
xdp_sec: str = "xdp" # XDP section name (e.g., "xdp" or "xdp.frags")
|
|
mtu: int = 1500 # Maximum Transmission Unit, default is 1500
|
|
|
|
|
|
def _exchg_udp(cfg, port, test_string):
|
|
"""
|
|
Exchanges UDP packets between a local and remote host using the socat tool.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
port: Port number to use for the UDP communication.
|
|
test_string: String that the remote host will send.
|
|
|
|
Returns:
|
|
The string received by the test host.
|
|
"""
|
|
cfg.require_cmd("socat", remote=True)
|
|
|
|
rx_udp_cmd = f"socat -{cfg.addr_ipver} -T 2 -u UDP-RECV:{port},reuseport STDOUT"
|
|
tx_udp_cmd = f"echo -n {test_string} | socat -t 2 -u STDIN UDP:{cfg.baddr}:{port}"
|
|
|
|
with bkg(rx_udp_cmd, exit_wait=True) as nc:
|
|
wait_port_listen(port, proto="udp")
|
|
cmd(tx_udp_cmd, host=cfg.remote, shell=True)
|
|
|
|
return nc.stdout.strip()
|
|
|
|
|
|
def _test_udp(cfg, port, size=256):
|
|
"""
|
|
Tests UDP packet exchange between a local and remote host.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
port: Port number to use for the UDP communication.
|
|
size: The length of the test string to be exchanged, default is 256 characters.
|
|
|
|
Returns:
|
|
bool: True if the received string matches the sent string, False otherwise.
|
|
"""
|
|
test_str = "".join(random.choice(string.ascii_lowercase) for _ in range(size))
|
|
recvd_str = _exchg_udp(cfg, port, test_str)
|
|
|
|
return recvd_str == test_str
|
|
|
|
|
|
def _load_xdp_prog(cfg, bpf_info):
|
|
"""
|
|
Loads an XDP program onto a network interface.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
bpf_info: BPFProgInfo object containing information about the BPF program.
|
|
|
|
Returns:
|
|
dict: A dictionary containing the XDP program ID, name, and associated map IDs.
|
|
"""
|
|
abs_path = cfg.net_lib_dir / bpf_info.file
|
|
prog_info = {}
|
|
|
|
cmd(f"ip link set dev {cfg.remote_ifname} mtu {bpf_info.mtu}", shell=True, host=cfg.remote)
|
|
defer(ip, f"link set dev {cfg.remote_ifname} mtu 1500", host=cfg.remote)
|
|
|
|
cmd(
|
|
f"ip link set dev {cfg.ifname} mtu {bpf_info.mtu} xdp obj {abs_path} sec {bpf_info.xdp_sec}",
|
|
shell=True
|
|
)
|
|
defer(ip, f"link set dev {cfg.ifname} mtu 1500 xdp off")
|
|
|
|
xdp_info = ip(f"-d link show dev {cfg.ifname}", json=True)[0]
|
|
prog_info["id"] = xdp_info["xdp"]["prog"]["id"]
|
|
prog_info["name"] = xdp_info["xdp"]["prog"]["name"]
|
|
prog_id = prog_info["id"]
|
|
|
|
map_ids = bpftool(f"prog show id {prog_id}", json=True)["map_ids"]
|
|
prog_info["maps"] = {}
|
|
for map_id in map_ids:
|
|
name = bpftool(f"map show id {map_id}", json=True)["name"]
|
|
prog_info["maps"][name] = map_id
|
|
|
|
return prog_info
|
|
|
|
|
|
def format_hex_bytes(value):
|
|
"""
|
|
Helper function that converts an integer into a formatted hexadecimal byte string.
|
|
|
|
Args:
|
|
value: An integer representing the number to be converted.
|
|
|
|
Returns:
|
|
A string representing hexadecimal equivalent of value, with bytes separated by spaces.
|
|
"""
|
|
hex_str = value.to_bytes(4, byteorder='little', signed=True)
|
|
return ' '.join(f'{byte:02x}' for byte in hex_str)
|
|
|
|
|
|
def _set_xdp_map(map_name, key, value):
|
|
"""
|
|
Updates an XDP map with a given key-value pair using bpftool.
|
|
|
|
Args:
|
|
map_name: The name of the XDP map to update.
|
|
key: The key to update in the map, formatted as a hexadecimal string.
|
|
value: The value to associate with the key, formatted as a hexadecimal string.
|
|
"""
|
|
key_formatted = format_hex_bytes(key)
|
|
value_formatted = format_hex_bytes(value)
|
|
bpftool(
|
|
f"map update name {map_name} key hex {key_formatted} value hex {value_formatted}"
|
|
)
|
|
|
|
|
|
def _get_stats(xdp_map_id):
|
|
"""
|
|
Retrieves and formats statistics from an XDP map.
|
|
|
|
Args:
|
|
xdp_map_id: The ID of the XDP map from which to retrieve statistics.
|
|
|
|
Returns:
|
|
A dictionary containing formatted packet statistics for various XDP actions.
|
|
The keys are based on the XDPStats Enum values.
|
|
|
|
Raises:
|
|
KsftFailEx: If the stats retrieval fails.
|
|
"""
|
|
stats_dump = bpftool(f"map dump id {xdp_map_id}", json=True)
|
|
if not stats_dump:
|
|
raise KsftFailEx(f"Failed to get stats for map {xdp_map_id}")
|
|
|
|
stats_formatted = {}
|
|
for key in range(0, 5):
|
|
val = stats_dump[key]["formatted"]["value"]
|
|
if stats_dump[key]["formatted"]["key"] == XDPStats.RX.value:
|
|
stats_formatted[XDPStats.RX.value] = val
|
|
elif stats_dump[key]["formatted"]["key"] == XDPStats.PASS.value:
|
|
stats_formatted[XDPStats.PASS.value] = val
|
|
elif stats_dump[key]["formatted"]["key"] == XDPStats.DROP.value:
|
|
stats_formatted[XDPStats.DROP.value] = val
|
|
elif stats_dump[key]["formatted"]["key"] == XDPStats.TX.value:
|
|
stats_formatted[XDPStats.TX.value] = val
|
|
elif stats_dump[key]["formatted"]["key"] == XDPStats.ABORT.value:
|
|
stats_formatted[XDPStats.ABORT.value] = val
|
|
|
|
return stats_formatted
|
|
|
|
|
|
def _test_pass(cfg, bpf_info, msg_sz):
|
|
"""
|
|
Tests the XDP_PASS action by exchanging UDP packets.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
bpf_info: BPFProgInfo object containing information about the BPF program.
|
|
msg_sz: Size of the test message to send.
|
|
"""
|
|
|
|
prog_info = _load_xdp_prog(cfg, bpf_info)
|
|
port = rand_port()
|
|
|
|
_set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.PASS.value)
|
|
_set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port)
|
|
|
|
ksft_eq(_test_udp(cfg, port, msg_sz), True, "UDP packet exchange failed")
|
|
stats = _get_stats(prog_info["maps"]["map_xdp_stats"])
|
|
|
|
ksft_ne(stats[XDPStats.RX.value], 0, "RX stats should not be zero")
|
|
ksft_eq(stats[XDPStats.RX.value], stats[XDPStats.PASS.value], "RX and PASS stats mismatch")
|
|
|
|
|
|
def test_xdp_native_pass_sb(cfg):
|
|
"""
|
|
Tests the XDP_PASS action for single buffer case.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
"""
|
|
bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500)
|
|
|
|
_test_pass(cfg, bpf_info, 256)
|
|
|
|
|
|
def test_xdp_native_pass_mb(cfg):
|
|
"""
|
|
Tests the XDP_PASS action for a multi-buff size.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
"""
|
|
bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000)
|
|
|
|
_test_pass(cfg, bpf_info, 8000)
|
|
|
|
|
|
def _test_drop(cfg, bpf_info, msg_sz):
|
|
"""
|
|
Tests the XDP_DROP action by exchanging UDP packets.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
bpf_info: BPFProgInfo object containing information about the BPF program.
|
|
msg_sz: Size of the test message to send.
|
|
"""
|
|
|
|
prog_info = _load_xdp_prog(cfg, bpf_info)
|
|
port = rand_port()
|
|
|
|
_set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.DROP.value)
|
|
_set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port)
|
|
|
|
ksft_eq(_test_udp(cfg, port, msg_sz), False, "UDP packet exchange should fail")
|
|
stats = _get_stats(prog_info["maps"]["map_xdp_stats"])
|
|
|
|
ksft_ne(stats[XDPStats.RX.value], 0, "RX stats should be zero")
|
|
ksft_eq(stats[XDPStats.RX.value], stats[XDPStats.DROP.value], "RX and DROP stats mismatch")
|
|
|
|
|
|
def test_xdp_native_drop_sb(cfg):
|
|
"""
|
|
Tests the XDP_DROP action for a signle-buff case.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
"""
|
|
bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500)
|
|
|
|
_test_drop(cfg, bpf_info, 256)
|
|
|
|
|
|
def test_xdp_native_drop_mb(cfg):
|
|
"""
|
|
Tests the XDP_DROP action for a multi-buff case.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
"""
|
|
bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000)
|
|
|
|
_test_drop(cfg, bpf_info, 8000)
|
|
|
|
|
|
def test_xdp_native_tx_mb(cfg):
|
|
"""
|
|
Tests the XDP_TX action for a multi-buff case.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
"""
|
|
cfg.require_cmd("socat", remote=True)
|
|
|
|
bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000)
|
|
prog_info = _load_xdp_prog(cfg, bpf_info)
|
|
port = rand_port()
|
|
|
|
_set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.TX.value)
|
|
_set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port)
|
|
|
|
test_string = ''.join(random.choice(string.ascii_lowercase) for _ in range(8000))
|
|
rx_udp = f"socat -{cfg.addr_ipver} -T 2 -u UDP-RECV:{port},reuseport STDOUT"
|
|
tx_udp = f"echo {test_string} | socat -t 2 -u STDIN UDP:{cfg.baddr}:{port}"
|
|
|
|
with bkg(rx_udp, host=cfg.remote, exit_wait=True) as rnc:
|
|
wait_port_listen(port, proto="udp", host=cfg.remote)
|
|
cmd(tx_udp, host=cfg.remote, shell=True)
|
|
|
|
stats = _get_stats(prog_info['maps']['map_xdp_stats'])
|
|
|
|
ksft_eq(rnc.stdout.strip(), test_string, "UDP packet exchange failed")
|
|
ksft_eq(stats[XDPStats.TX.value], 1, "TX stats mismatch")
|
|
|
|
|
|
def _validate_res(res, offset_lst, pkt_sz_lst):
|
|
"""
|
|
Validates the result of a test.
|
|
|
|
Args:
|
|
res: The result of the test, which should be a dictionary with a "status" key.
|
|
|
|
Raises:
|
|
KsftFailEx: If the test fails to pass any combination of offset and packet size.
|
|
"""
|
|
if "status" not in res:
|
|
raise KsftFailEx("Missing 'status' key in result dictionary")
|
|
|
|
# Validate that not a single case was successful
|
|
if res["status"] == "fail":
|
|
if res["offset"] == offset_lst[0] and res["pkt_sz"] == pkt_sz_lst[0]:
|
|
raise KsftFailEx(f"{res['reason']}")
|
|
|
|
# Get the previous offset and packet size to report the successful run
|
|
tmp_idx = offset_lst.index(res["offset"])
|
|
prev_offset = offset_lst[tmp_idx - 1]
|
|
if tmp_idx == 0:
|
|
tmp_idx = pkt_sz_lst.index(res["pkt_sz"])
|
|
prev_pkt_sz = pkt_sz_lst[tmp_idx - 1]
|
|
else:
|
|
prev_pkt_sz = res["pkt_sz"]
|
|
|
|
# Use these values for error reporting
|
|
ksft_pr(
|
|
f"Failed run: pkt_sz {res['pkt_sz']}, offset {res['offset']}. "
|
|
f"Last successful run: pkt_sz {prev_pkt_sz}, offset {prev_offset}. "
|
|
f"Reason: {res['reason']}"
|
|
)
|
|
|
|
|
|
def _check_for_failures(recvd_str, stats):
|
|
"""
|
|
Checks for common failures while adjusting headroom or tailroom.
|
|
|
|
Args:
|
|
recvd_str: The string received from the remote host after sending a test string.
|
|
stats: A dictionary containing formatted packet statistics for various XDP actions.
|
|
|
|
Returns:
|
|
str: A string describing the failure reason if a failure is detected, otherwise None.
|
|
"""
|
|
|
|
# Any adjustment failure result in an abort hence, we track this counter
|
|
if stats[XDPStats.ABORT.value] != 0:
|
|
return "Adjustment failed"
|
|
|
|
# Since we are using aggregate stats for a single test across all offsets and packet sizes
|
|
# we can't use RX stats only to track data exchange failure without taking a previous
|
|
# snapshot. An easier way is to simply check for non-zero length of received string.
|
|
if len(recvd_str) == 0:
|
|
return "Data exchange failed"
|
|
|
|
# Check for RX and PASS stats mismatch. Ideally, they should be equal for a successful run
|
|
if stats[XDPStats.RX.value] != stats[XDPStats.PASS.value]:
|
|
return "RX stats mismatch"
|
|
|
|
return None
|
|
|
|
|
|
def _test_xdp_native_tail_adjst(cfg, pkt_sz_lst, offset_lst):
|
|
"""
|
|
Tests the XDP tail adjustment functionality.
|
|
|
|
This function loads the appropriate XDP program based on the provided
|
|
program name and configures the XDP map for tail adjustment. It then
|
|
validates the tail adjustment by sending and receiving UDP packets
|
|
with specified packet sizes and offsets.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
prog: Name of the XDP program to load.
|
|
pkt_sz_lst: List of packet sizes to test.
|
|
offset_lst: List of offsets to validate support for tail adjustment.
|
|
|
|
Returns:
|
|
dict: A dictionary with test status and failure details if applicable.
|
|
"""
|
|
port = rand_port()
|
|
bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000)
|
|
|
|
prog_info = _load_xdp_prog(cfg, bpf_info)
|
|
|
|
# Configure the XDP map for tail adjustment
|
|
_set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.TAIL_ADJST.value)
|
|
_set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port)
|
|
|
|
for offset in offset_lst:
|
|
tag = format(random.randint(65, 90), "02x")
|
|
|
|
_set_xdp_map("map_xdp_setup", TestConfig.ADJST_OFFSET.value, offset)
|
|
if offset > 0:
|
|
_set_xdp_map("map_xdp_setup", TestConfig.ADJST_TAG.value, int(tag, 16))
|
|
|
|
for pkt_sz in pkt_sz_lst:
|
|
test_str = "".join(random.choice(string.ascii_lowercase) for _ in range(pkt_sz))
|
|
recvd_str = _exchg_udp(cfg, port, test_str)
|
|
stats = _get_stats(prog_info["maps"]["map_xdp_stats"])
|
|
|
|
failure = _check_for_failures(recvd_str, stats)
|
|
if failure is not None:
|
|
return {
|
|
"status": "fail",
|
|
"reason": failure,
|
|
"offset": offset,
|
|
"pkt_sz": pkt_sz,
|
|
}
|
|
|
|
# Validate data content based on offset direction
|
|
expected_data = None
|
|
if offset > 0:
|
|
expected_data = test_str + (offset * chr(int(tag, 16)))
|
|
else:
|
|
expected_data = test_str[0:pkt_sz + offset]
|
|
|
|
if recvd_str != expected_data:
|
|
return {
|
|
"status": "fail",
|
|
"reason": "Data mismatch",
|
|
"offset": offset,
|
|
"pkt_sz": pkt_sz,
|
|
}
|
|
|
|
return {"status": "pass"}
|
|
|
|
|
|
def test_xdp_native_adjst_tail_grow_data(cfg):
|
|
"""
|
|
Tests the XDP tail adjustment by growing packet data.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
"""
|
|
pkt_sz_lst = [512, 1024, 2048]
|
|
offset_lst = [1, 16, 32, 64, 128, 256]
|
|
res = _test_xdp_native_tail_adjst(
|
|
cfg,
|
|
pkt_sz_lst,
|
|
offset_lst,
|
|
)
|
|
|
|
_validate_res(res, offset_lst, pkt_sz_lst)
|
|
|
|
|
|
def test_xdp_native_adjst_tail_shrnk_data(cfg):
|
|
"""
|
|
Tests the XDP tail adjustment by shrinking packet data.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
"""
|
|
pkt_sz_lst = [512, 1024, 2048]
|
|
offset_lst = [-16, -32, -64, -128, -256]
|
|
res = _test_xdp_native_tail_adjst(
|
|
cfg,
|
|
pkt_sz_lst,
|
|
offset_lst,
|
|
)
|
|
|
|
_validate_res(res, offset_lst, pkt_sz_lst)
|
|
|
|
|
|
def get_hds_thresh(cfg):
|
|
"""
|
|
Retrieves the header data split (HDS) threshold for a network interface.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
|
|
Returns:
|
|
The HDS threshold value. If the threshold is not supported or an error occurs,
|
|
a default value of 1500 is returned.
|
|
"""
|
|
netnl = cfg.netnl
|
|
hds_thresh = 1500
|
|
|
|
try:
|
|
rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
|
|
if 'hds-thresh' not in rings:
|
|
ksft_pr(f'hds-thresh not supported. Using default: {hds_thresh}')
|
|
return hds_thresh
|
|
hds_thresh = rings['hds-thresh']
|
|
except NlError as e:
|
|
ksft_pr(f"Failed to get rings: {e}. Using default: {hds_thresh}")
|
|
|
|
return hds_thresh
|
|
|
|
|
|
def _test_xdp_native_head_adjst(cfg, prog, pkt_sz_lst, offset_lst):
|
|
"""
|
|
Tests the XDP head adjustment action for a multi-buffer case.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
netnl: Network namespace or link object (not used in this function).
|
|
|
|
This function sets up the packet size and offset lists, then performs
|
|
the head adjustment test by sending and receiving UDP packets.
|
|
"""
|
|
cfg.require_cmd("socat", remote=True)
|
|
|
|
prog_info = _load_xdp_prog(cfg, BPFProgInfo(prog, "xdp_native.bpf.o", "xdp.frags", 9000))
|
|
port = rand_port()
|
|
|
|
_set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.HEAD_ADJST.value)
|
|
_set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port)
|
|
|
|
hds_thresh = get_hds_thresh(cfg)
|
|
for offset in offset_lst:
|
|
for pkt_sz in pkt_sz_lst:
|
|
# The "head" buffer must contain at least the Ethernet header
|
|
# after we eat into it. We send large-enough packets, but if HDS
|
|
# is enabled head will only contain headers. Don't try to eat
|
|
# more than 28 bytes (UDPv4 + eth hdr left: (14 + 20 + 8) - 14)
|
|
l2_cut_off = 28 if cfg.addr_ipver == 4 else 48
|
|
if pkt_sz > hds_thresh and offset > l2_cut_off:
|
|
ksft_pr(
|
|
f"Failed run: pkt_sz ({pkt_sz}) > HDS threshold ({hds_thresh}) and "
|
|
f"offset {offset} > {l2_cut_off}"
|
|
)
|
|
return {"status": "pass"}
|
|
|
|
test_str = ''.join(random.choice(string.ascii_lowercase) for _ in range(pkt_sz))
|
|
tag = format(random.randint(65, 90), '02x')
|
|
|
|
_set_xdp_map("map_xdp_setup",
|
|
TestConfig.ADJST_OFFSET.value,
|
|
offset)
|
|
_set_xdp_map("map_xdp_setup", TestConfig.ADJST_TAG.value, int(tag, 16))
|
|
_set_xdp_map("map_xdp_setup", TestConfig.ADJST_OFFSET.value, offset)
|
|
|
|
recvd_str = _exchg_udp(cfg, port, test_str)
|
|
|
|
# Check for failures around adjustment and data exchange
|
|
failure = _check_for_failures(recvd_str, _get_stats(prog_info['maps']['map_xdp_stats']))
|
|
if failure is not None:
|
|
return {
|
|
"status": "fail",
|
|
"reason": failure,
|
|
"offset": offset,
|
|
"pkt_sz": pkt_sz
|
|
}
|
|
|
|
# Validate data content based on offset direction
|
|
expected_data = None
|
|
if offset < 0:
|
|
expected_data = chr(int(tag, 16)) * (0 - offset) + test_str
|
|
else:
|
|
expected_data = test_str[offset:]
|
|
|
|
if recvd_str != expected_data:
|
|
return {
|
|
"status": "fail",
|
|
"reason": "Data mismatch",
|
|
"offset": offset,
|
|
"pkt_sz": pkt_sz
|
|
}
|
|
|
|
return {"status": "pass"}
|
|
|
|
|
|
def test_xdp_native_adjst_head_grow_data(cfg):
|
|
"""
|
|
Tests the XDP headroom growth support.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
|
|
This function sets up the packet size and offset lists, then calls the
|
|
_test_xdp_native_head_adjst_mb function to perform the actual test. The
|
|
test is passed if the headroom is successfully extended for given packet
|
|
sizes and offsets.
|
|
"""
|
|
pkt_sz_lst = [512, 1024, 2048]
|
|
|
|
# Negative values result in headroom shrinking, resulting in growing of payload
|
|
offset_lst = [-16, -32, -64, -128, -256]
|
|
res = _test_xdp_native_head_adjst(cfg, "xdp_prog_frags", pkt_sz_lst, offset_lst)
|
|
|
|
_validate_res(res, offset_lst, pkt_sz_lst)
|
|
|
|
|
|
def test_xdp_native_adjst_head_shrnk_data(cfg):
|
|
"""
|
|
Tests the XDP headroom shrinking support.
|
|
|
|
Args:
|
|
cfg: Configuration object containing network settings.
|
|
|
|
This function sets up the packet size and offset lists, then calls the
|
|
_test_xdp_native_head_adjst_mb function to perform the actual test. The
|
|
test is passed if the headroom is successfully shrunk for given packet
|
|
sizes and offsets.
|
|
"""
|
|
pkt_sz_lst = [512, 1024, 2048]
|
|
|
|
# Positive values result in headroom growing, resulting in shrinking of payload
|
|
offset_lst = [16, 32, 64, 128, 256]
|
|
res = _test_xdp_native_head_adjst(cfg, "xdp_prog_frags", pkt_sz_lst, offset_lst)
|
|
|
|
_validate_res(res, offset_lst, pkt_sz_lst)
|
|
|
|
|
|
def main():
|
|
"""
|
|
Main function to execute the XDP tests.
|
|
|
|
This function runs a series of tests to validate the XDP support for
|
|
both the single and multi-buffer. It uses the NetDrvEpEnv context
|
|
manager to manage the network driver environment and the ksft_run
|
|
function to execute the tests.
|
|
"""
|
|
with NetDrvEpEnv(__file__) as cfg:
|
|
cfg.netnl = EthtoolFamily()
|
|
ksft_run(
|
|
[
|
|
test_xdp_native_pass_sb,
|
|
test_xdp_native_pass_mb,
|
|
test_xdp_native_drop_sb,
|
|
test_xdp_native_drop_mb,
|
|
test_xdp_native_tx_mb,
|
|
test_xdp_native_adjst_tail_grow_data,
|
|
test_xdp_native_adjst_tail_shrnk_data,
|
|
test_xdp_native_adjst_head_grow_data,
|
|
test_xdp_native_adjst_head_shrnk_data,
|
|
],
|
|
args=(cfg,))
|
|
ksft_exit()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|