selftests: drv-net: replace the nsim ring test with a drv-net one

We are trying to move away from netdevsim-only tests and towards
tests which can be run both against netdevsim and real drivers.

Replace the simple bash script we have for checking ethtool -g/-G
on netdevsim with a Python test tweaking those params as well
as channel count.

The new test is not exactly equivalent to the netdevsim one,
but real drivers don't often support random ring sizes,
let alone modifying max values via debugfs.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20251029164930.2923448-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-10-29 09:49:30 -07:00
parent 4920abacb1
commit ecca75ae5a
4 changed files with 168 additions and 86 deletions

View File

@ -22,6 +22,7 @@ TEST_PROGS := \
ping.py \
psp.py \
queues.py \
ring_reconfig.py \
shaper.py \
stats.py \
xdp.py \

View File

@ -8,7 +8,6 @@ TEST_PROGS := \
ethtool-features.sh \
ethtool-fec.sh \
ethtool-pause.sh \
ethtool-ring.sh \
fib.sh \
fib_notifications.sh \
hw_stats_l3.sh \

View File

@ -1,85 +0,0 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-only
source ethtool-common.sh
function get_value {
local query="${SETTINGS_MAP[$1]}"
echo $(ethtool -g $NSIM_NETDEV | \
tail -n +$CURR_SETT_LINE | \
awk -F':' -v pattern="$query:" '$0 ~ pattern {gsub(/[\t ]/, "", $2); print $2}')
}
function update_current_settings {
for key in ${!SETTINGS_MAP[@]}; do
CURRENT_SETTINGS[$key]=$(get_value $key)
done
echo ${CURRENT_SETTINGS[@]}
}
if ! ethtool -h | grep -q set-ring >/dev/null; then
echo "SKIP: No --set-ring support in ethtool"
exit 4
fi
NSIM_NETDEV=$(make_netdev)
set -o pipefail
declare -A SETTINGS_MAP=(
["rx"]="RX"
["rx-mini"]="RX Mini"
["rx-jumbo"]="RX Jumbo"
["tx"]="TX"
)
declare -A EXPECTED_SETTINGS=(
["rx"]=""
["rx-mini"]=""
["rx-jumbo"]=""
["tx"]=""
)
declare -A CURRENT_SETTINGS=(
["rx"]=""
["rx-mini"]=""
["rx-jumbo"]=""
["tx"]=""
)
MAX_VALUE=$((RANDOM % $((2**32-1))))
RING_MAX_LIST=$(ls $NSIM_DEV_DFS/ethtool/ring/)
for ring_max_entry in $RING_MAX_LIST; do
echo $MAX_VALUE > $NSIM_DEV_DFS/ethtool/ring/$ring_max_entry
done
CURR_SETT_LINE=$(ethtool -g $NSIM_NETDEV | grep -i -m1 -n 'Current hardware settings' | cut -f1 -d:)
# populate the expected settings map
for key in ${!SETTINGS_MAP[@]}; do
EXPECTED_SETTINGS[$key]=$(get_value $key)
done
# test
for key in ${!SETTINGS_MAP[@]}; do
value=$((RANDOM % $MAX_VALUE))
ethtool -G $NSIM_NETDEV "$key" "$value"
EXPECTED_SETTINGS[$key]="$value"
expected=${EXPECTED_SETTINGS[@]}
current=$(update_current_settings)
check $? "$current" "$expected"
set +x
done
if [ $num_errors -eq 0 ]; then
echo "PASSED all $((num_passes)) checks"
exit 0
else
echo "FAILED $num_errors/$((num_errors+num_passes)) checks"
exit 1
fi

View File

@ -0,0 +1,167 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
"""
Test channel and ring size configuration via ethtool (-L / -G).
"""
from lib.py import ksft_run, ksft_exit, ksft_pr
from lib.py import ksft_eq
from lib.py import NetDrvEpEnv, EthtoolFamily, GenerateTraffic
from lib.py import defer, NlError
def channels(cfg) -> None:
"""
Twiddle channel counts in various combinations of parameters.
We're only looking for driver adhering to the requested config
if the config is accepted and crashes.
"""
ehdr = {'header':{'dev-index': cfg.ifindex}}
chans = cfg.eth.channels_get(ehdr)
all_keys = ["rx", "tx", "combined"]
mixes = [{"combined"}, {"rx", "tx"}, {"rx", "combined"}, {"tx", "combined"},
{"rx", "tx", "combined"},]
# Get the set of keys that device actually supports
restore = {}
supported = set()
for key in all_keys:
if key + "-max" in chans:
supported.add(key)
restore |= {key + "-count": chans[key + "-count"]}
defer(cfg.eth.channels_set, ehdr | restore)
def test_config(config):
try:
cfg.eth.channels_set(ehdr | config)
get = cfg.eth.channels_get(ehdr)
for k, v in config.items():
ksft_eq(get.get(k, 0), v)
except NlError as e:
failed.append(mix)
ksft_pr("Can't set", config, e)
else:
ksft_pr("Okay", config)
failed = []
for mix in mixes:
if not mix.issubset(supported):
continue
# Set all the values in the mix to 1, other supported to 0
config = {}
for key in all_keys:
config[key + "-count"] = 1 if key in mix else 0
test_config(config)
for mix in mixes:
if not mix.issubset(supported):
continue
if mix in failed:
continue
# Set all the values in the mix to max, other supported to 0
config = {}
for key in all_keys:
config[key + "-count"] = chans[key + '-max'] if key in mix else 0
test_config(config)
def _configure_min_ring_cnt(cfg) -> None:
""" Try to configure a single Rx/Tx ring. """
ehdr = {'header':{'dev-index': cfg.ifindex}}
chans = cfg.eth.channels_get(ehdr)
all_keys = ["rx-count", "tx-count", "combined-count"]
restore = {}
config = {}
for key in all_keys:
if key in chans:
restore[key] = chans[key]
config[key] = 0
if chans.get('combined-count', 0) > 1:
config['combined-count'] = 1
elif chans.get('rx-count', 0) > 1 and chans.get('tx-count', 0) > 1:
config['tx-count'] = 1
config['rx-count'] = 1
else:
# looks like we're already on 1 channel
return
cfg.eth.channels_set(ehdr | config)
defer(cfg.eth.channels_set, ehdr | restore)
def ringparam(cfg) -> None:
"""
Tweak the ringparam configuration. Try to run some traffic over min
ring size to make sure it actually functions.
"""
ehdr = {'header':{'dev-index': cfg.ifindex}}
rings = cfg.eth.rings_get(ehdr)
restore = {}
maxes = {}
params = set()
for key in rings.keys():
if 'max' in key:
param = key[:-4]
maxes[param] = rings[key]
params.add(param)
restore[param] = rings[param]
defer(cfg.eth.rings_set, ehdr | restore)
# Speed up the reconfig by configuring just one ring
_configure_min_ring_cnt(cfg)
# Try to reach min on all settings
for param in params:
val = rings[param]
while True:
try:
cfg.eth.rings_set({'header':{'dev-index': cfg.ifindex},
param: val // 2})
if val == 0:
break
val //= 2
except NlError:
break
get = cfg.eth.rings_get(ehdr)
ksft_eq(get[param], val)
ksft_pr(f"Reached min for '{param}' at {val} (max {rings[param]})")
GenerateTraffic(cfg).wait_pkts_and_stop(10000)
# Try max across all params, if the driver supports large rings
# this may OOM so we ignore errors
try:
ksft_pr("Applying max settings")
config = {p: maxes[p] for p in params}
cfg.eth.rings_set(ehdr | config)
except NlError as e:
ksft_pr("Can't set max params", config, e)
else:
GenerateTraffic(cfg).wait_pkts_and_stop(10000)
def main() -> None:
""" Ksft boiler plate main """
with NetDrvEpEnv(__file__) as cfg:
cfg.eth = EthtoolFamily()
ksft_run([channels,
ringparam],
args=(cfg, ))
ksft_exit()
if __name__ == "__main__":
main()