From 4799ff80c68a76e408b8dadd4aeaa5311962033c Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 30 Mar 2026 18:29:25 +0300 Subject: [PATCH 1/9] selftests: forwarding: extend ethtool_std_stats_get with pause statistics Even though pause frame statistics are not exported through the same ethtool command, there is no point in adding another helper just for them. Extent the ethtool_std_stats_get() function so that we are able to interrogate using the same helper all the standard statistics. And since we are touching the function, convert the initial ethtool call as well to the jq --arg form in order to be easier to read. Signed-off-by: Ioana Ciornei Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260330152933.2195885-2-ioana.ciornei@nxp.com Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/forwarding/lib.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index a9034f0bb58b..3009ce00c5dc 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -831,8 +831,14 @@ ethtool_std_stats_get() local name=$1; shift local src=$1; shift - ethtool --json -S $dev --groups $grp -- --src $src | \ - jq '.[]."'"$grp"'"."'$name'"' + if [[ "$grp" == "pause" ]]; then + ethtool -I --json -a "$dev" --src "$src" | \ + jq --arg name "$name" '.[].statistics[$name]' + return + fi + + ethtool --json -S "$dev" --groups "$grp" -- --src "$src" | \ + jq --arg grp "$grp" --arg name "$name" '.[][$grp][$name]' } qdisc_stats_get() From 2de16ebe78f0509d5fa0b41ed6e841603058b6a6 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 30 Mar 2026 18:29:26 +0300 Subject: [PATCH 2/9] selftests: net: extend lib.sh to parse drivers/net/net.config Extend lib.sh so that it's able to parse driver/net/net.config and environment variables such as NETIF, REMOTE_TYPE, LOCAL_V4 etc described in drivers/net/README.rst. In order to make the transition towards running with a single local interface smoother for the bash networking driver tests, beside sourcing the net.config file also translate the new env variables into the old style based on the NETIFS array. Since the NETIFS array only holds the network interface names, also add a new array - TARGETS - which keeps track of the target on which a specific interfaces resides - local, netns or accesible through an ssh command. For example, a net.config which looks like below: NETIF=eth0 LOCAL_V4=192.168.1.1 REMOTE_V4=192.168.1.2 REMOTE_TYPE=ssh REMOTE_ARGS=root@192.168.1.2 will generate the NETIFS and TARGETS arrays with the following data. NETIFS[p1]="eth0" NETIFS[p2]="eth2" TARGETS[eth0]="local:" TARGETS[eth2]="ssh:root@192.168.1.2" The above will be true if on the remote target, the interface which has the 192.168.1.2 address is named eth2. Since the TARGETS array is indexed by the network interface name, document a new restriction README.rst which states that the remote interface cannot have the same name as the local one. Keep the old way of populating the NETIFS variable based on the command line arguments. This will be invoked in case DRIVER_TEST_CONFORMANT = "no". Also add a couple of helpers which can be used by tests which need to run a specific bash command on a different target than the local system, be it either another netns or a remote system accessible through ssh. The __run_on() function is passed through $1 the target on which the command should be executed while run_on() is passed the name of the interface that is then used to retrieve the target from the TARGETS array. Also add a stub run_on() function in net/lib.sh so that users of the net/lib.sh are going through the stub only since neither NETIFS nor TARGETS are valid in that circumstance. Signed-off-by: Ioana Ciornei Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260330152933.2195885-3-ioana.ciornei@nxp.com Signed-off-by: Paolo Abeni --- .../testing/selftests/drivers/net/README.rst | 4 + tools/testing/selftests/net/forwarding/lib.sh | 148 ++++++++++++++++-- tools/testing/selftests/net/lib.sh | 5 + 3 files changed, 147 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/drivers/net/README.rst b/tools/testing/selftests/drivers/net/README.rst index b26b364be534..c8588436c224 100644 --- a/tools/testing/selftests/drivers/net/README.rst +++ b/tools/testing/selftests/drivers/net/README.rst @@ -26,6 +26,10 @@ The netdevice against which tests will be run must exist, be running Refer to list of :ref:`Variables` later in this file to set up running the tests against a real device. +The current support for bash tests restricts the use of the same interface name +on the local system and the remote one and will bail if this case is +encountered. + Both modes required ~~~~~~~~~~~~~~~~~~~ diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 3009ce00c5dc..83249aafa669 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -1,5 +1,6 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +#shellcheck disable=SC2034 # SC doesn't see our uses of global variables ############################################################################## # Topology description. p1 looped back to p2, p3 to p4 and so on. @@ -340,17 +341,144 @@ fi ############################################################################## # Command line options handling -count=0 - -while [[ $# -gt 0 ]]; do - if [[ "$count" -eq "0" ]]; then - unset NETIFS - declare -A NETIFS +check_env() { + if [[ ! (( -n "$LOCAL_V4" && -n "$REMOTE_V4") || + ( -n "$LOCAL_V6" && -n "$REMOTE_V6" )) ]]; then + echo "SKIP: Invalid environment, missing or inconsistent LOCAL_V4/REMOTE_V4/LOCAL_V6/REMOTE_V6" + echo "Please see tools/testing/selftests/drivers/net/README.rst" + exit "$ksft_skip" fi - count=$((count + 1)) - NETIFS[p$count]="$1" - shift -done + + if [[ -z "$REMOTE_TYPE" ]]; then + echo "SKIP: Invalid environment, missing REMOTE_TYPE" + exit "$ksft_skip" + fi + + if [[ -z "$REMOTE_ARGS" ]]; then + echo "SKIP: Invalid environment, missing REMOTE_ARGS" + exit "$ksft_skip" + fi +} + +__run_on() +{ + local target=$1; shift + local type args + + IFS=':' read -r type args <<< "$target" + + case "$type" in + netns) + # Execute command in network namespace + # args contains the namespace name + ip netns exec "$args" "$@" + ;; + ssh) + # Execute command via SSH args contains user@host + ssh -n "$args" "$@" + ;; + local|*) + # Execute command locally. This is also the fallback + # case for when the interface's target is not found in + # the TARGETS array. + "$@" + ;; + esac +} + +run_on() +{ + local iface=$1; shift + local target="local:" + + if [ "${DRIVER_TEST_CONFORMANT}" = "yes" ]; then + target="${TARGETS[$iface]}" + fi + + __run_on "$target" "$@" +} + +get_ifname_by_ip() +{ + local target=$1; shift + local ip_addr=$1; shift + + __run_on "$target" ip -j addr show to "$ip_addr" | jq -r '.[].ifname' +} + +# Whether the test is conforming to the requirements and usage described in +# drivers/net/README.rst. +: "${DRIVER_TEST_CONFORMANT:=no}" + +declare -A TARGETS + +# Based on DRIVER_TEST_CONFORMANT, decide if to source drivers/net/net.config +# or not. In the "yes" case, the test expects to pass the arguments through the +# variables specified in drivers/net/README.rst file. If not, fallback on +# parsing the script arguments for interface names. +if [ "${DRIVER_TEST_CONFORMANT}" = "yes" ]; then + if [[ -f $net_forwarding_dir/../../drivers/net/net.config ]]; then + source "$net_forwarding_dir/../../drivers/net/net.config" + fi + + if (( NUM_NETIFS > 2)); then + echo "SKIP: DRIVER_TEST_CONFORMANT=yes and NUM_NETIFS is bigger than 2" + exit "$ksft_skip" + fi + + check_env + + # Populate the NETIFS and TARGETS arrays automatically based on the + # environment variables. The TARGETS array is indexed by the network + # interface name keeping track of the target on which the interface + # resides. Values will be strings of the following format - + # :. + # + # TARGETS[eth0]="local:" - meaning that the eth0 interface is + # accessible locally + # TARGETS[eth1]="netns:foo" - eth1 is in the foo netns + # TARGETS[eth2]="ssh:root@10.0.0.2" - eth2 is accessible through + # running the 'ssh root@10.0.0.2' command. + + unset NETIFS + declare -A NETIFS + + NETIFS[p1]="$NETIF" + TARGETS[$NETIF]="local:" + + # Locate the name of the remote interface + remote_target="$REMOTE_TYPE:$REMOTE_ARGS" + if [[ -v REMOTE_V4 ]]; then + remote_netif=$(get_ifname_by_ip "$remote_target" "$REMOTE_V4") + else + remote_netif=$(get_ifname_by_ip "$remote_target" "$REMOTE_V6") + fi + if [[ ! -n "$remote_netif" ]]; then + echo "SKIP: cannot find remote interface" + exit "$ksft_skip" + fi + + if [[ "$NETIF" == "$remote_netif" ]]; then + echo "SKIP: local and remote interfaces cannot have the same name" + exit "$ksft_skip" + fi + + NETIFS[p2]="$remote_netif" + TARGETS[$remote_netif]="$REMOTE_TYPE:$REMOTE_ARGS" +else + count=0 + + while [[ $# -gt 0 ]]; do + if [[ "$count" -eq "0" ]]; then + unset NETIFS + declare -A NETIFS + fi + count=$((count + 1)) + NETIFS[p$count]="$1" + TARGETS[$1]="local:" + shift + done +fi ############################################################################## # Network interfaces configuration diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index b40694573f4c..3f79bf1ec977 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -670,3 +670,8 @@ cmd_jq() # return success only in case of non-empty output [ ! -z "$output" ] } + +run_on() +{ + shift; "$@" +} From 7db9da4c67c7781c2c917657d5d9bdc171836d5f Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 30 Mar 2026 18:29:27 +0300 Subject: [PATCH 3/9] selftests: net: update some helpers to use run_on Update some helpers so that they are capable to run commands on different targets than the local one. This patch makes the necesasy modification for those helpers / sections of code which are needed for the ethtool_rmon.sh test that will be converted in the next patches. For example, mac_addr_prepare() and mac_addr_restore() used when STABLE_MAC_ADDRS=yes need to ensure stable MAC addresses on interfaces located even in other namespaces. In order to do that, append the 'ip link' commands with a 'run_on $dev' tag. The same run_on is necessary also when verifying if all the interfaces listed in NETIFS are indeed available. Signed-off-by: Ioana Ciornei Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260330152933.2195885-4-ioana.ciornei@nxp.com Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/forwarding/lib.sh | 19 ++++++++++++------- tools/testing/selftests/net/lib.sh | 3 ++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 83249aafa669..d8cc4c64148d 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -546,10 +546,11 @@ mac_addr_prepare() dev=${NETIFS[p$i]} new_addr=$(printf "00:01:02:03:04:%02x" $i) - MAC_ADDR_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].address') + MAC_ADDR_ORIG["$dev"]=$(run_on "$dev" \ + ip -j link show dev "$dev" | jq -e '.[].address') # Strip quotes MAC_ADDR_ORIG["$dev"]=${MAC_ADDR_ORIG["$dev"]//\"/} - ip link set dev $dev address $new_addr + run_on "$dev" ip link set dev "$dev" address $new_addr done } @@ -559,7 +560,8 @@ mac_addr_restore() for ((i = 1; i <= NUM_NETIFS; ++i)); do dev=${NETIFS[p$i]} - ip link set dev $dev address ${MAC_ADDR_ORIG["$dev"]} + run_on "$dev" \ + ip link set dev "$dev" address ${MAC_ADDR_ORIG["$dev"]} done } @@ -572,7 +574,9 @@ if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then fi for ((i = 1; i <= NUM_NETIFS; ++i)); do - ip link show dev ${NETIFS[p$i]} &> /dev/null + int="${NETIFS[p$i]}" + + run_on "$int" ip link show dev "$int" &> /dev/null if [[ $? -ne 0 ]]; then echo "SKIP: could not find all required interfaces" exit $ksft_skip @@ -655,7 +659,7 @@ setup_wait_dev_with_timeout() local i for ((i = 1; i <= $max_iterations; ++i)); do - ip link show dev $dev up \ + run_on "$dev" ip link show dev "$dev" up \ | grep 'state UP' &> /dev/null if [[ $? -ne 0 ]]; then sleep 1 @@ -960,12 +964,13 @@ ethtool_std_stats_get() local src=$1; shift if [[ "$grp" == "pause" ]]; then - ethtool -I --json -a "$dev" --src "$src" | \ + run_on "$dev" ethtool -I --json -a "$dev" --src "$src" | \ jq --arg name "$name" '.[].statistics[$name]' return fi - ethtool --json -S "$dev" --groups "$grp" -- --src "$src" | \ + run_on "$dev" \ + ethtool --json -S "$dev" --groups "$grp" -- --src "$src" | \ jq --arg grp "$grp" --arg name "$name" '.[][$grp][$name]' } diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index 3f79bf1ec977..e915386daf1b 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -514,7 +514,8 @@ mac_get() { local if_name=$1 - ip -j link show dev $if_name | jq -r '.[]["address"]' + run_on "$if_name" \ + ip -j link show dev "$if_name" | jq -r '.[]["address"]' } kill_process() From afd1b9bf7529d853284205f618e314134a5ad9ad Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 30 Mar 2026 18:29:28 +0300 Subject: [PATCH 4/9] selftests: drivers: hw: cleanup shellcheck warnings in the rmon test If run on the ethtool_rmon.sh script, shellcheck generates a bunch of false positive errors. Suppress those checks that generate them. Also cleanup the remaining warnings by using double quoting around the used variables. Signed-off-by: Ioana Ciornei Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260330152933.2195885-5-ioana.ciornei@nxp.com Signed-off-by: Paolo Abeni --- .../selftests/drivers/net/hw/ethtool_rmon.sh | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh b/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh index 8f60c1685ad4..13b3760e3a40 100755 --- a/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh +++ b/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh @@ -1,5 +1,7 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +#shellcheck disable=SC2034 # SC does not see the global variables +#shellcheck disable=SC2317,SC2329 # unused functions ALL_TESTS=" rmon_rx_histogram @@ -19,11 +21,12 @@ ensure_mtu() { local iface=$1; shift local len=$1; shift - local current=$(ip -j link show dev $iface | jq -r '.[0].mtu') local required=$((len - ETH_HLEN - ETH_FCS_LEN)) + local current - if [ $current -lt $required ]; then - ip link set dev $iface mtu $required || return 1 + current=$(ip -j link show dev "$iface" | jq -r '.[0].mtu') + if [ "$current" -lt "$required" ]; then + ip link set dev "$iface" mtu "$required" || return 1 fi } @@ -46,23 +49,23 @@ bucket_test() len=$((len - ETH_FCS_LEN)) len=$((len > 0 ? len : 0)) - before=$(ethtool --json -S $iface --groups rmon | \ + before=$(ethtool --json -S "$iface" --groups rmon | \ jq -r ".[0].rmon[\"${set}-pktsNtoM\"][$bucket].val") # Send 10k one way and 20k in the other, to detect counters # mapped to the wrong direction - $MZ $neigh -q -c $num_rx -p $len -a own -b bcast -d 10us - $MZ $iface -q -c $num_tx -p $len -a own -b bcast -d 10us + "$MZ" "$neigh" -q -c "$num_rx" -p "$len" -a own -b bcast -d 10us + "$MZ" "$iface" -q -c "$num_tx" -p "$len" -a own -b bcast -d 10us - after=$(ethtool --json -S $iface --groups rmon | \ + after=$(ethtool --json -S "$iface" --groups rmon | \ jq -r ".[0].rmon[\"${set}-pktsNtoM\"][$bucket].val") delta=$((after - before)) - expected=$([ $set = rx ] && echo $num_rx || echo $num_tx) + expected=$([ "$set" = rx ] && echo "$num_rx" || echo "$num_tx") # Allow some extra tolerance for other packets sent by the stack - [ $delta -ge $expected ] && [ $delta -le $((expected + 100)) ] + [ "$delta" -ge "$expected" ] && [ "$delta" -le $((expected + 100)) ] } rmon_histogram() @@ -78,23 +81,23 @@ rmon_histogram() while read -r -a bucket; do step="$set-pkts${bucket[0]}to${bucket[1]} on $iface" - for if in $iface $neigh; do - if ! ensure_mtu $if ${bucket[0]}; then + for if in "$iface" "$neigh"; do + if ! ensure_mtu "$if" "${bucket[0]}"; then log_test_xfail "$if does not support the required MTU for $step" return fi done - if ! bucket_test $iface $neigh $set $nbuckets ${bucket[0]}; then + if ! bucket_test "$iface" "$neigh" "$set" "$nbuckets" "${bucket[0]}"; then check_err 1 "$step failed" return 1 fi log_test "$step" nbuckets=$((nbuckets + 1)) - done < <(ethtool --json -S $iface --groups rmon | \ + done < <(ethtool --json -S "$iface" --groups rmon | \ jq -r ".[0].rmon[\"${set}-pktsNtoM\"][]|[.low, .high]|@tsv" 2>/dev/null) - if [ $nbuckets -eq 0 ]; then + if [ "$nbuckets" -eq 0 ]; then log_test_xfail "$iface does not support $set histogram counters" return fi @@ -102,14 +105,14 @@ rmon_histogram() rmon_rx_histogram() { - rmon_histogram $h1 $h2 rx - rmon_histogram $h2 $h1 rx + rmon_histogram "$h1" "$h2" rx + rmon_histogram "$h2" "$h1" rx } rmon_tx_histogram() { - rmon_histogram $h1 $h2 tx - rmon_histogram $h2 $h1 tx + rmon_histogram "$h1" "$h2" tx + rmon_histogram "$h2" "$h1" tx } setup_prepare() @@ -117,9 +120,10 @@ setup_prepare() h1=${NETIFS[p1]} h2=${NETIFS[p2]} - for iface in $h1 $h2; do - netif_mtu[$iface]=$(ip -j link show dev $iface | jq -r '.[0].mtu') - ip link set dev $iface up + for iface in "$h1" "$h2"; do + netif_mtu["$iface"]=$(ip -j link show dev "$iface" | \ + jq -r '.[0].mtu') + ip link set dev "$iface" up done } @@ -127,9 +131,9 @@ cleanup() { pre_cleanup - for iface in $h2 $h1; do - ip link set dev $iface \ - mtu ${netif_mtu[$iface]} \ + for iface in "$h2" "$h1"; do + ip link set dev "$iface" \ + mtu "${netif_mtu[$iface]}" \ down done } @@ -142,4 +146,4 @@ setup_wait tests_run -exit $EXIT_STATUS +exit "$EXIT_STATUS" From e76c71483a5600eb9ef8f1741c92fe1282bfd461 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 30 Mar 2026 18:29:29 +0300 Subject: [PATCH 5/9] selftests: drivers: hw: test rmon counters only on first interface The selftests in drivers/net are slowly transitioning to being able to be used on systems with a single network interface. The first step for the ethtool_rmon.sh test is to only validate that the rmon counters are properly exported on the first interface supplied as an argument. Remove the rmon_histogram calls which intend to test also the rmon counters on the 2nd interface. This also removes the need for the remote system, which should be used only to inject traffic, to also support rmon counters. Signed-off-by: Ioana Ciornei Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260330152933.2195885-6-ioana.ciornei@nxp.com Signed-off-by: Paolo Abeni --- tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh b/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh index 13b3760e3a40..80e75b9b40fd 100755 --- a/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh +++ b/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh @@ -106,13 +106,11 @@ rmon_histogram() rmon_rx_histogram() { rmon_histogram "$h1" "$h2" rx - rmon_histogram "$h2" "$h1" rx } rmon_tx_histogram() { rmon_histogram "$h1" "$h2" tx - rmon_histogram "$h2" "$h1" tx } setup_prepare() From 557c067c0001b2e1538633d921790a1094e675ff Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 30 Mar 2026 18:29:30 +0300 Subject: [PATCH 6/9] selftests: drivers: hw: replace counter upper limit with UINT32_MAX in rmon test The ethtool_rmon.sh script checks that the number of packets sent / received during a test matches the expected value with a 1% tolerance. Since in the next patches this test will gain the capability to also be run on systems with a single interface where the traffic generator is accesible through ssh, use the UINT32_MAX as the upper limit. This is necessary since the same interface will be used also for control traffic (the ssh commands) as well as the mausezahn generated one. Signed-off-by: Ioana Ciornei Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260330152933.2195885-7-ioana.ciornei@nxp.com Signed-off-by: Paolo Abeni --- tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh b/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh index 80e75b9b40fd..f290ce1832f1 100755 --- a/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh +++ b/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh @@ -12,6 +12,7 @@ NUM_NETIFS=2 lib_dir=$(dirname "$0") source "$lib_dir"/../../../net/forwarding/lib.sh +UINT32_MAX=$((2**32 - 1)) ETH_FCS_LEN=4 ETH_HLEN=$((6+6+2)) @@ -64,8 +65,7 @@ bucket_test() expected=$([ "$set" = rx ] && echo "$num_rx" || echo "$num_tx") - # Allow some extra tolerance for other packets sent by the stack - [ "$delta" -ge "$expected" ] && [ "$delta" -le $((expected + 100)) ] + [ "$delta" -ge "$expected" ] && [ "$delta" -le "$UINT32_MAX" ] } rmon_histogram() From eec1b9057c24d3f7c37d98208bb0c9be7130d3c6 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 30 Mar 2026 18:29:31 +0300 Subject: [PATCH 7/9] selftests: drivers: hw: move to KTAP output Update the ethtool_rmon.sh test so that it uses the KTAP format for its output. This is achieved by using the helpers found in ktap_helpers.sh. An example output can be found below. $ ./ethtool_rmon.sh endpmac3 endpmac4 TAP version 13 1..14 ok 1 ethtool_rmon.rx-pkts64to64 ok 2 ethtool_rmon.rx-pkts65to127 ok 3 ethtool_rmon.rx-pkts128to255 ok 4 ethtool_rmon.rx-pkts256to511 ok 5 ethtool_rmon.rx-pkts512to1023 ok 6 ethtool_rmon.rx-pkts1024to1518 ok 7 ethtool_rmon.rx-pkts1519to10240 ok 8 ethtool_rmon.tx-pkts64to64 ok 9 ethtool_rmon.tx-pkts65to127 ok 10 ethtool_rmon.tx-pkts128to255 ok 11 ethtool_rmon.tx-pkts256to511 ok 12 ethtool_rmon.tx-pkts512to1023 ok 13 ethtool_rmon.tx-pkts1024to1518 ok 14 ethtool_rmon.tx-pkts1519to10240 # Totals: pass:14 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Ioana Ciornei Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260330152933.2195885-8-ioana.ciornei@nxp.com Signed-off-by: Paolo Abeni --- .../selftests/drivers/net/hw/ethtool_rmon.sh | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh b/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh index f290ce1832f1..ed81bdc33536 100755 --- a/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh +++ b/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh @@ -11,10 +11,12 @@ ALL_TESTS=" NUM_NETIFS=2 lib_dir=$(dirname "$0") source "$lib_dir"/../../../net/forwarding/lib.sh +source "$lib_dir"/../../../kselftest/ktap_helpers.sh UINT32_MAX=$((2**32 - 1)) ETH_FCS_LEN=4 ETH_HLEN=$((6+6+2)) +TEST_NAME=$(basename "$0" .sh) declare -A netif_mtu @@ -76,29 +78,28 @@ rmon_histogram() local nbuckets=0 local step= - RET=0 - while read -r -a bucket; do - step="$set-pkts${bucket[0]}to${bucket[1]} on $iface" + step="$set-pkts${bucket[0]}to${bucket[1]}" for if in "$iface" "$neigh"; do if ! ensure_mtu "$if" "${bucket[0]}"; then - log_test_xfail "$if does not support the required MTU for $step" + ktap_print_msg "$if does not support the required MTU for $step" + ktap_test_xfail "$TEST_NAME.$step" return fi done if ! bucket_test "$iface" "$neigh" "$set" "$nbuckets" "${bucket[0]}"; then - check_err 1 "$step failed" + ktap_test_fail "$TEST_NAME.$step" return 1 fi - log_test "$step" + ktap_test_pass "$TEST_NAME.$step" nbuckets=$((nbuckets + 1)) done < <(ethtool --json -S "$iface" --groups rmon | \ jq -r ".[0].rmon[\"${set}-pktsNtoM\"][]|[.low, .high]|@tsv" 2>/dev/null) if [ "$nbuckets" -eq 0 ]; then - log_test_xfail "$iface does not support $set histogram counters" + ktap_print_msg "$iface does not support $set histogram counters" return fi } @@ -139,9 +140,16 @@ cleanup() check_ethtool_counter_group_support trap cleanup EXIT +bucket_count=$(ethtool --json -S "${NETIFS[p1]}" --groups rmon | \ + jq -r '.[0].rmon | + "\((."rx-pktsNtoM" | length) + + (."tx-pktsNtoM" | length))"') +ktap_print_header +ktap_set_plan "$bucket_count" + setup_prepare setup_wait tests_run -exit "$EXIT_STATUS" +ktap_finished From abe4929bc7d0e29c5dda8b0bb0f6beeec76fd7b6 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 30 Mar 2026 18:29:32 +0300 Subject: [PATCH 8/9] selftests: drivers: hw: update ethtool_rmon to work with a single local interface This patch finalizes the transition to work with a single local interface for the ethtool_rmon.sh test. Each 'ip link' and 'ethtool' command used by the test is annotated with the necessary run_on in order to be executed on the necessary target system, be it local, in another network namespace or through ssh. Since we need NETIF up and running also for control traffic, we now expect that the interfaces are up and running and do not touch bring them up or down at the end of the test. This is also documented in the drivers/net/README.rst. The ethtool_rmon.sh script can still be used in the older fashion by passing two interfaces as command line arguments, the only restriction is that those interfaces need to be already up. $ DRIVER_TEST_CONFORMANT=no ./ethtool_rmon.sh eth0 eth1 As part of the kselftest infrastructure, this test can be run in the following manner: $ make -C tools/testing/selftests/ TARGETS="drivers/net drivers/net/hw" \ install INSTALL_PATH=/tmp/ksft-net-drv $ cd /tmp/ksft-net-drv/ $ cat > ./drivers/net/net.config < Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260330152933.2195885-9-ioana.ciornei@nxp.com Signed-off-by: Paolo Abeni --- .../selftests/drivers/net/hw/ethtool_rmon.sh | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh b/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh index ed81bdc33536..2ec19edddfaa 100755 --- a/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh +++ b/tools/testing/selftests/drivers/net/hw/ethtool_rmon.sh @@ -8,6 +8,7 @@ ALL_TESTS=" rmon_tx_histogram " +: "${DRIVER_TEST_CONFORMANT:=yes}" NUM_NETIFS=2 lib_dir=$(dirname "$0") source "$lib_dir"/../../../net/forwarding/lib.sh @@ -27,9 +28,11 @@ ensure_mtu() local required=$((len - ETH_HLEN - ETH_FCS_LEN)) local current - current=$(ip -j link show dev "$iface" | jq -r '.[0].mtu') + current=$(run_on "$iface" \ + ip -j link show dev "$iface" | jq -r '.[0].mtu') if [ "$current" -lt "$required" ]; then - ip link set dev "$iface" mtu "$required" || return 1 + run_on "$iface" ip link set dev "$iface" mtu "$required" \ + || return 1 fi } @@ -52,15 +55,17 @@ bucket_test() len=$((len - ETH_FCS_LEN)) len=$((len > 0 ? len : 0)) - before=$(ethtool --json -S "$iface" --groups rmon | \ + before=$(run_on "$iface" ethtool --json -S "$iface" --groups rmon | \ jq -r ".[0].rmon[\"${set}-pktsNtoM\"][$bucket].val") # Send 10k one way and 20k in the other, to detect counters # mapped to the wrong direction - "$MZ" "$neigh" -q -c "$num_rx" -p "$len" -a own -b bcast -d 10us - "$MZ" "$iface" -q -c "$num_tx" -p "$len" -a own -b bcast -d 10us + run_on "$neigh" \ + "$MZ" "$neigh" -q -c "$num_rx" -p "$len" -a own -b bcast -d 10us + run_on "$iface" \ + "$MZ" "$iface" -q -c "$num_tx" -p "$len" -a own -b bcast -d 10us - after=$(ethtool --json -S "$iface" --groups rmon | \ + after=$(run_on "$iface" ethtool --json -S "$iface" --groups rmon | \ jq -r ".[0].rmon[\"${set}-pktsNtoM\"][$bucket].val") delta=$((after - before)) @@ -95,7 +100,7 @@ rmon_histogram() fi ktap_test_pass "$TEST_NAME.$step" nbuckets=$((nbuckets + 1)) - done < <(ethtool --json -S "$iface" --groups rmon | \ + done < <(run_on "$iface" ethtool --json -S "$iface" --groups rmon | \ jq -r ".[0].rmon[\"${set}-pktsNtoM\"][]|[.low, .high]|@tsv" 2>/dev/null) if [ "$nbuckets" -eq 0 ]; then @@ -120,9 +125,8 @@ setup_prepare() h2=${NETIFS[p2]} for iface in "$h1" "$h2"; do - netif_mtu["$iface"]=$(ip -j link show dev "$iface" | \ - jq -r '.[0].mtu') - ip link set dev "$iface" up + netif_mtu["$iface"]=$(run_on "$iface" \ + ip -j link show dev "$iface" | jq -r '.[0].mtu') done } @@ -130,10 +134,10 @@ cleanup() { pre_cleanup + # Do not bring down the interfaces, just configure the initial MTU for iface in "$h2" "$h1"; do - ip link set dev "$iface" \ - mtu "${netif_mtu[$iface]}" \ - down + run_on "$iface" ip link set dev "$iface" \ + mtu "${netif_mtu[$iface]}" done } From 3016574ea2f8a8cff69286574ac027fa5aed5a81 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 30 Mar 2026 18:29:33 +0300 Subject: [PATCH 9/9] selftests: drivers: hw: add test for the ethtool standard counters Add a new selftest - ethtool_std_stats.sh - which validates the eth-ctrl, eth-mac and pause standard statistics exported by an interface. Collision related eth-mac counters as well as the error ones will be checked against zero since that is the most likely correct scenario. The central part of this patch is the traffic_test() function which gathers the 'before' counter values, sends a batch of traffic and then interrogates again the same counters in order to determine if the delta is on target. The function receives an array through which the caller can request what counters to be interrogated and, for each of them, what is their target delta value. The output from this selftest looks as follows on a LX2160ARDB board: $ ./run_kselftest.sh -t drivers/net/hw:ethtool_std_stats.sh TAP version 13 1..1 # timeout set to 0 # selftests: drivers/net/hw: ethtool_std_stats.sh # TAP version 13 # 1..26 # ok 1 ethtool_std_stats.eth-ctrl-MACControlFramesTransmitted # ok 2 ethtool_std_stats.eth-ctrl-MACControlFramesReceived # ok 3 ethtool_std_stats.eth-mac-FrameCheckSequenceErrors # ok 4 ethtool_std_stats.eth-mac-AlignmentErrors # ok 5 ethtool_std_stats.eth-mac-FramesLostDueToIntMACXmitError # ok 6 ethtool_std_stats.eth-mac-CarrierSenseErrors # SKIP # ok 7 ethtool_std_stats.eth-mac-FramesLostDueToIntMACRcvError # ok 8 ethtool_std_stats.eth-mac-InRangeLengthErrors # SKIP # ok 9 ethtool_std_stats.eth-mac-OutOfRangeLengthField # SKIP # ok 10 ethtool_std_stats.eth-mac-FrameTooLongErrors # SKIP # ok 11 ethtool_std_stats.eth-mac-FramesAbortedDueToXSColls # SKIP # ok 12 ethtool_std_stats.eth-mac-SingleCollisionFrames # SKIP # ok 13 ethtool_std_stats.eth-mac-MultipleCollisionFrames # SKIP # ok 14 ethtool_std_stats.eth-mac-FramesWithDeferredXmissions # SKIP # ok 15 ethtool_std_stats.eth-mac-LateCollisions # SKIP # ok 16 ethtool_std_stats.eth-mac-FramesWithExcessiveDeferral # SKIP # ok 17 ethtool_std_stats.eth-mac-BroadcastFramesXmittedOK # ok 18 ethtool_std_stats.eth-mac-OctetsTransmittedOK # ok 19 ethtool_std_stats.eth-mac-BroadcastFramesReceivedOK # ok 20 ethtool_std_stats.eth-mac-OctetsReceivedOK # ok 21 ethtool_std_stats.eth-mac-FramesTransmittedOK # ok 22 ethtool_std_stats.eth-mac-MulticastFramesXmittedOK # ok 23 ethtool_std_stats.eth-mac-FramesReceivedOK # ok 24 ethtool_std_stats.eth-mac-MulticastFramesReceivedOK # ok 25 ethtool_std_stats.pause-tx_pause_frames # ok 26 ethtool_std_stats.pause-rx_pause_frames # # 10 skipped test(s) detected. Consider enabling relevant config options to improve coverage. # # Totals: pass:16 fail:0 xfail:0 xpass:0 skip:10 error:0 ok 1 selftests: drivers/net/hw: ethtool_std_stats.sh Please note that not all MACs are counting the software injected pause frames as real Tx pause. For example, on a LS1028ARDB the selftest output will reflect the fact that neither the ENETC MAC, nor the Felix switch MAC are able to detect Tx pause frames injected by software. $ ./run_kselftest.sh -t drivers/net/hw:ethtool_std_stats.sh (...) # # software sent pause frames not detected # ok 25 ethtool_std_stats.pause-tx_pause_frames # XFAIL # ok 26 ethtool_std_stats.pause-rx_pause_frames Signed-off-by: Ioana Ciornei Acked-by: Petr Machata Link: https://patch.msgid.link/20260330152933.2195885-10-ioana.ciornei@nxp.com Signed-off-by: Paolo Abeni --- .../testing/selftests/drivers/net/hw/Makefile | 1 + .../drivers/net/hw/ethtool_std_stats.sh | 206 ++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100755 tools/testing/selftests/drivers/net/hw/ethtool_std_stats.sh diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile index 884cc77daeaa..deeca3f8d080 100644 --- a/tools/testing/selftests/drivers/net/hw/Makefile +++ b/tools/testing/selftests/drivers/net/hw/Makefile @@ -26,6 +26,7 @@ TEST_PROGS = \ ethtool_extended_state.sh \ ethtool_mm.sh \ ethtool_rmon.sh \ + ethtool_std_stats.sh \ gro_hw.py \ hw_stats_l3.sh \ hw_stats_l3_gre.sh \ diff --git a/tools/testing/selftests/drivers/net/hw/ethtool_std_stats.sh b/tools/testing/selftests/drivers/net/hw/ethtool_std_stats.sh new file mode 100755 index 000000000000..c085d2a4c989 --- /dev/null +++ b/tools/testing/selftests/drivers/net/hw/ethtool_std_stats.sh @@ -0,0 +1,206 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +#shellcheck disable=SC2034 # SC does not see the global variables +#shellcheck disable=SC2317,SC2329 # unused functions + +ALL_TESTS=" + test_eth_ctrl_stats + test_eth_mac_stats + test_pause_stats +" +: "${DRIVER_TEST_CONFORMANT:=yes}" +STABLE_MAC_ADDRS=yes +NUM_NETIFS=2 +lib_dir=$(dirname "$0") +# shellcheck source=./../../../net/forwarding/lib.sh +source "$lib_dir"/../../../net/forwarding/lib.sh +# shellcheck source=./../../../kselftest/ktap_helpers.sh +source "$lib_dir"/../../../kselftest/ktap_helpers.sh + +UINT32_MAX=$((2**32 - 1)) +SUBTESTS=0 +TEST_NAME=$(basename "$0" .sh) + +traffic_test() +{ + local iface=$1; shift + local neigh=$1; shift + local num_tx=$1; shift + local pkt_format="$1"; shift + local -a counters=("$@") + local int grp cnt target exact_check + local before after delta + local num_rx=$((num_tx * 2)) + local xfail_message + local src="aggregate" + local i + + for i in "${!counters[@]}"; do + read -r int grp cnt target exact_check xfail_message \ + <<< "${counters[$i]}" + + before[i]=$(ethtool_std_stats_get "$int" "$grp" "$cnt" "$src") + done + + # shellcheck disable=SC2086 # needs split options + run_on "$iface" "$MZ" "$iface" -q -c "$num_tx" $pkt_format + + # shellcheck disable=SC2086 # needs split options + run_on "$neigh" "$MZ" "$neigh" -q -c "$num_rx" $pkt_format + + for i in "${!counters[@]}"; do + read -r int grp cnt target exact_check xfail_message \ + <<< "${counters[$i]}" + + after[i]=$(ethtool_std_stats_get "$int" "$grp" "$cnt" "$src") + if [[ "${after[$i]}" == "null" ]]; then + ktap_test_skip "$TEST_NAME.$grp-$cnt" + continue; + fi + + delta=$((after[i] - before[i])) + + if [ "$exact_check" -ne 0 ]; then + [ "$delta" -eq "$target" ] + else + [ "$delta" -ge "$target" ] && \ + [ "$delta" -le "$UINT32_MAX" ] + fi + err="$?" + + if [[ $err != 0 ]] && [[ -n $xfail_message ]]; then + ktap_print_msg "$xfail_message" + ktap_test_xfail "$TEST_NAME.$grp-$cnt" + continue; + fi + + if [[ $err != 0 ]]; then + ktap_print_msg "$grp-$cnt is not valid on $int (expected $target, got $delta)" + ktap_test_fail "$TEST_NAME.$grp-$cnt" + else + ktap_test_pass "$TEST_NAME.$grp-$cnt" + fi + done +} + +test_eth_ctrl_stats() +{ + local pkt_format="-a own -b bcast 88:08 -p 64" + local num_pkts=1000 + local -a counters + + counters=("$h1 eth-ctrl MACControlFramesTransmitted $num_pkts 0") + traffic_test "$h1" "$h2" "$num_pkts" "$pkt_format" \ + "${counters[@]}" + + counters=("$h1 eth-ctrl MACControlFramesReceived $num_pkts 0") + traffic_test "$h2" "$h1" "$num_pkts" "$pkt_format" \ + "${counters[@]}" +} +SUBTESTS=$((SUBTESTS + 2)) + +test_eth_mac_stats() +{ + local pkt_size=100 + local pkt_size_fcs=$((pkt_size + 4)) + local bcast_pkt_format="-a own -b bcast -p $pkt_size" + local mcast_pkt_format="-a own -b 01:00:5E:00:00:01 -p $pkt_size" + local num_pkts=2000 + local octets=$((pkt_size_fcs * num_pkts)) + local -a counters error_cnt collision_cnt + + # Error counters should be exactly zero + counters=("$h1 eth-mac FrameCheckSequenceErrors 0 1" + "$h1 eth-mac AlignmentErrors 0 1" + "$h1 eth-mac FramesLostDueToIntMACXmitError 0 1" + "$h1 eth-mac CarrierSenseErrors 0 1" + "$h1 eth-mac FramesLostDueToIntMACRcvError 0 1" + "$h1 eth-mac InRangeLengthErrors 0 1" + "$h1 eth-mac OutOfRangeLengthField 0 1" + "$h1 eth-mac FrameTooLongErrors 0 1" + "$h1 eth-mac FramesAbortedDueToXSColls 0 1") + traffic_test "$h1" "$h2" "$num_pkts" "$bcast_pkt_format" \ + "${counters[@]}" + + # Collision related counters should also be zero + counters=("$h1 eth-mac SingleCollisionFrames 0 1" + "$h1 eth-mac MultipleCollisionFrames 0 1" + "$h1 eth-mac FramesWithDeferredXmissions 0 1" + "$h1 eth-mac LateCollisions 0 1" + "$h1 eth-mac FramesWithExcessiveDeferral 0 1") + traffic_test "$h1" "$h2" "$num_pkts" "$bcast_pkt_format" \ + "${counters[@]}" + + counters=("$h1 eth-mac BroadcastFramesXmittedOK $num_pkts 0" + "$h1 eth-mac OctetsTransmittedOK $octets 0") + traffic_test "$h1" "$h2" "$num_pkts" "$bcast_pkt_format" \ + "${counters[@]}" + + counters=("$h1 eth-mac BroadcastFramesReceivedOK $num_pkts 0" + "$h1 eth-mac OctetsReceivedOK $octets 0") + traffic_test "$h2" "$h1" "$num_pkts" "$bcast_pkt_format" \ + "${counters[@]}" + + counters=("$h1 eth-mac FramesTransmittedOK $num_pkts 0" + "$h1 eth-mac MulticastFramesXmittedOK $num_pkts 0") + traffic_test "$h1" "$h2" "$num_pkts" "$mcast_pkt_format" \ + "${counters[@]}" + + counters=("$h1 eth-mac FramesReceivedOK $num_pkts 0" + "$h1 eth-mac MulticastFramesReceivedOK $num_pkts 0") + traffic_test "$h2" "$h1" "$num_pkts" "$mcast_pkt_format" \ + "${counters[@]}" +} +SUBTESTS=$((SUBTESTS + 22)) + +test_pause_stats() +{ + local pkt_format="-a own -b 01:80:c2:00:00:01 88:08:00:01:00:01" + local xfail_message="software sent pause frames not detected" + local num_pkts=2000 + local -a counters + local int + local i + + # Check that there is pause frame support + for ((i = 1; i <= NUM_NETIFS; ++i)); do + int="${NETIFS[p$i]}" + if ! run_on "$int" ethtool -I --json -a "$int" > /dev/null 2>&1; then + ktap_test_skip "$TEST_NAME.tx_pause_frames" + ktap_test_skip "$TEST_NAME.rx_pause_frames" + return + fi + done + + counters=("$h1 pause tx_pause_frames $num_pkts 0 $xfail_message") + traffic_test "$h1" "$h2" "$num_pkts" "$pkt_format" \ + "${counters[@]}" + + counters=("$h1 pause rx_pause_frames $num_pkts 0") + traffic_test "$h2" "$h1" "$num_pkts" "$pkt_format" \ + "${counters[@]}" +} +SUBTESTS=$((SUBTESTS + 2)) + +setup_prepare() +{ + local iface + + h1=${NETIFS[p1]} + h2=${NETIFS[p2]} + + h2_mac=$(mac_get "$h2") +} + +ktap_print_header +ktap_set_plan $SUBTESTS + +check_ethtool_counter_group_support +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +ktap_finished