Commit 6234219d authored by David S. Miller's avatar David S. Miller

Merge branch 'GRO-Toeplitz-selftests'

Coco Li says:

====================
GRO and Toeplitz hash selftests

This patch contains two selftests in net, as well as respective
scripts to run the tests on a single machine in loopback mode.
GRO: tests the Linux kernel GRO behavior
Toeplitz: tests the toeplitz hash implementation
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ab996c42 5ebfb4cc
......@@ -38,8 +38,10 @@ TEST_GEN_FILES += reuseaddr_ports_exhausted
TEST_GEN_FILES += hwtstamp_config rxtimestamp timestamping txtimestamp
TEST_GEN_FILES += ipsec
TEST_GEN_FILES += ioam6_parser
TEST_GEN_FILES += gro
TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls
TEST_GEN_FILES += toeplitz
TEST_FILES := settings
......
This diff is collapsed.
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
source setup_loopback.sh
readonly SERVER_MAC="aa:00:00:00:00:02"
readonly CLIENT_MAC="aa:00:00:00:00:01"
readonly TESTS=("data" "ack" "flags" "tcp" "ip" "large")
readonly PROTOS=("ipv4" "ipv6")
dev="eth0"
test="all"
proto="ipv4"
setup_interrupt() {
# Use timer on host to trigger the network stack
# Also disable device interrupt to not depend on NIC interrupt
# Reduce test flakiness caused by unexpected interrupts
echo 100000 >"${FLUSH_PATH}"
echo 50 >"${IRQ_PATH}"
}
setup_ns() {
# Set up server_ns namespace and client_ns namespace
setup_macvlan_ns "${dev}" server_ns server "${SERVER_MAC}"
setup_macvlan_ns "${dev}" client_ns client "${CLIENT_MAC}"
}
cleanup_ns() {
cleanup_macvlan_ns server_ns server client_ns client
}
setup() {
setup_loopback_environment "${dev}"
setup_interrupt
}
cleanup() {
cleanup_loopback "${dev}"
echo "${FLUSH_TIMEOUT}" >"${FLUSH_PATH}"
echo "${HARD_IRQS}" >"${IRQ_PATH}"
}
run_test() {
local server_pid=0
local exit_code=0
local protocol=$1
local test=$2
local ARGS=( "--${protocol}" "--dmac" "${SERVER_MAC}" \
"--smac" "${CLIENT_MAC}" "--test" "${test}" "--verbose" )
setup_ns
# Each test is run 3 times to deflake, because given the receive timing,
# not all packets that should coalesce will be considered in the same flow
# on every try.
for tries in {1..3}; do
# Actual test starts here
ip netns exec server_ns ./gro "${ARGS[@]}" "--rx" "--iface" "server" \
1>>log.txt &
server_pid=$!
sleep 0.5 # to allow for socket init
ip netns exec client_ns ./gro "${ARGS[@]}" "--iface" "client" \
1>>log.txt
wait "${server_pid}"
exit_code=$?
if [[ "${exit_code}" -eq 0 ]]; then
break;
fi
done
cleanup_ns
echo ${exit_code}
}
run_all_tests() {
local failed_tests=()
for proto in "${PROTOS[@]}"; do
for test in "${TESTS[@]}"; do
echo "running test ${proto} ${test}" >&2
exit_code=$(run_test $proto $test)
if [[ "${exit_code}" -ne 0 ]]; then
failed_tests+=("${proto}_${test}")
fi;
done;
done
if [[ ${#failed_tests[@]} -ne 0 ]]; then
echo "failed tests: ${failed_tests[*]}. \
Please see log.txt for more logs"
exit 1
else
echo "All Tests Succeeded!"
fi;
}
usage() {
echo "Usage: $0 \
[-i <DEV>] \
[-t data|ack|flags|tcp|ip|large] \
[-p <ipv4|ipv6>]" 1>&2;
exit 1;
}
while getopts "i:t:p:" opt; do
case "${opt}" in
i)
dev="${OPTARG}"
;;
t)
test="${OPTARG}"
;;
p)
proto="${OPTARG}"
;;
*)
usage
;;
esac
done
readonly FLUSH_PATH="/sys/class/net/${dev}/gro_flush_timeout"
readonly IRQ_PATH="/sys/class/net/${dev}/napi_defer_hard_irqs"
readonly FLUSH_TIMEOUT="$(< ${FLUSH_PATH})"
readonly HARD_IRQS="$(< ${IRQ_PATH})"
setup
trap cleanup EXIT
if [[ "${test}" == "all" ]]; then
run_all_tests
else
run_test "${proto}" "${test}"
fi;
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
netdev_check_for_carrier() {
local -r dev="$1"
for i in {1..5}; do
carrier="$(cat /sys/class/net/${dev}/carrier)"
if [[ "${carrier}" -ne 1 ]] ; then
echo "carrier not ready yet..." >&2
sleep 1
else
echo "carrier ready" >&2
break
fi
done
echo "${carrier}"
}
# Assumes that there is no existing ipvlan device on the physical device
setup_loopback_environment() {
local dev="$1"
# Fail hard if cannot turn on loopback mode for current NIC
ethtool -K "${dev}" loopback on || exit 1
sleep 1
# Check for the carrier
carrier=$(netdev_check_for_carrier ${dev})
if [[ "${carrier}" -ne 1 ]] ; then
echo "setup_loopback_environment failed"
exit 1
fi
}
setup_macvlan_ns(){
local -r link_dev="$1"
local -r ns_name="$2"
local -r ns_dev="$3"
local -r ns_mac="$4"
local -r addr="$5"
ip link add link "${link_dev}" dev "${ns_dev}" \
address "${ns_mac}" type macvlan
exit_code=$?
if [[ "${exit_code}" -ne 0 ]]; then
echo "setup_macvlan_ns failed"
exit $exit_code
fi
[[ -e /var/run/netns/"${ns_name}" ]] || ip netns add "${ns_name}"
ip link set dev "${ns_dev}" netns "${ns_name}"
ip -netns "${ns_name}" link set dev "${ns_dev}" up
if [[ -n "${addr}" ]]; then
ip -netns "${ns_name}" addr add dev "${ns_dev}" "${addr}"
fi
sleep 1
}
cleanup_macvlan_ns(){
while (( $# >= 2 )); do
ns_name="$1"
ns_dev="$2"
ip -netns "${ns_name}" link del dev "${ns_dev}"
ip netns del "${ns_name}"
shift 2
done
}
cleanup_loopback(){
local -r dev="$1"
ethtool -K "${dev}" loopback off
sleep 1
# Check for the carrier
carrier=$(netdev_check_for_carrier ${dev})
if [[ "${carrier}" -ne 1 ]] ; then
echo "setup_loopback_environment failed"
exit 1
fi
}
This diff is collapsed.
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# extended toeplitz test: test rxhash plus, optionally, either (1) rss mapping
# from rxhash to rx queue ('-rss') or (2) rps mapping from rxhash to cpu
# ('-rps <rps_map>')
#
# irq-pattern-prefix can be derived from /sys/kernel/irq/*/action,
# which is a driver-specific encoding.
#
# invoke as ./toeplitz.sh (-i <iface>) -u|-t -4|-6 \
# [(-rss -irq_prefix <irq-pattern-prefix>)|(-rps <rps_map>)]
source setup_loopback.sh
readonly SERVER_IP4="192.168.1.200/24"
readonly SERVER_IP6="fda8::1/64"
readonly SERVER_MAC="aa:00:00:00:00:02"
readonly CLIENT_IP4="192.168.1.100/24"
readonly CLIENT_IP6="fda8::2/64"
readonly CLIENT_MAC="aa:00:00:00:00:01"
PORT=8000
KEY="$(</proc/sys/net/core/netdev_rss_key)"
TEST_RSS=false
RPS_MAP=""
PROTO_FLAG=""
IP_FLAG=""
DEV="eth0"
# Return the number of rxqs among which RSS is configured to spread packets.
# This is determined by reading the RSS indirection table using ethtool.
get_rss_cfg_num_rxqs() {
echo $(ethtool -x "${DEV}" |
egrep [[:space:]]+[0-9]+:[[:space:]]+ |
cut -d: -f2- |
awk '{$1=$1};1' |
tr ' ' '\n' |
sort -u |
wc -l)
}
# Return a list of the receive irq handler cpus.
# The list is ordered by the irqs, so first rxq-0 cpu, then rxq-1 cpu, etc.
# Reads /sys/kernel/irq/ in order, so algorithm depends on
# irq_{rxq-0} < irq_{rxq-1}, etc.
get_rx_irq_cpus() {
CPUS=""
# sort so that irq 2 is read before irq 10
SORTED_IRQS=$(for i in /sys/kernel/irq/*; do echo $i; done | sort -V)
# Consider only as many queues as RSS actually uses. We assume that
# if RSS_CFG_NUM_RXQS=N, then RSS uses rxqs 0-(N-1).
RSS_CFG_NUM_RXQS=$(get_rss_cfg_num_rxqs)
RXQ_COUNT=0
for i in ${SORTED_IRQS}
do
[[ "${RXQ_COUNT}" -lt "${RSS_CFG_NUM_RXQS}" ]] || break
# lookup relevant IRQs by action name
[[ -e "$i/actions" ]] || continue
cat "$i/actions" | grep -q "${IRQ_PATTERN}" || continue
irqname=$(<"$i/actions")
# does the IRQ get called
irqcount=$(cat "$i/per_cpu_count" | tr -d '0,')
[[ -n "${irqcount}" ]] || continue
# lookup CPU
irq=$(basename "$i")
cpu=$(cat "/proc/irq/$irq/smp_affinity_list")
if [[ -z "${CPUS}" ]]; then
CPUS="${cpu}"
else
CPUS="${CPUS},${cpu}"
fi
RXQ_COUNT=$((RXQ_COUNT+1))
done
echo "${CPUS}"
}
get_disable_rfs_cmd() {
echo "echo 0 > /proc/sys/net/core/rps_sock_flow_entries;"
}
get_set_rps_bitmaps_cmd() {
CMD=""
for i in /sys/class/net/${DEV}/queues/rx-*/rps_cpus
do
CMD="${CMD} echo $1 > ${i};"
done
echo "${CMD}"
}
get_disable_rps_cmd() {
echo "$(get_set_rps_bitmaps_cmd 0)"
}
die() {
echo "$1"
exit 1
}
check_nic_rxhash_enabled() {
local -r pattern="receive-hashing:\ on"
ethtool -k "${DEV}" | grep -q "${pattern}" || die "rxhash must be enabled"
}
parse_opts() {
local prog=$0
shift 1
while [[ "$1" =~ "-" ]]; do
if [[ "$1" = "-irq_prefix" ]]; then
shift
IRQ_PATTERN="^$1-[0-9]*$"
elif [[ "$1" = "-u" || "$1" = "-t" ]]; then
PROTO_FLAG="$1"
elif [[ "$1" = "-4" ]]; then
IP_FLAG="$1"
SERVER_IP="${SERVER_IP4}"
CLIENT_IP="${CLIENT_IP4}"
elif [[ "$1" = "-6" ]]; then
IP_FLAG="$1"
SERVER_IP="${SERVER_IP6}"
CLIENT_IP="${CLIENT_IP6}"
elif [[ "$1" = "-rss" ]]; then
TEST_RSS=true
elif [[ "$1" = "-rps" ]]; then
shift
RPS_MAP="$1"
elif [[ "$1" = "-i" ]]; then
shift
DEV="$1"
else
die "Usage: ${prog} (-i <iface>) -u|-t -4|-6 \
[(-rss -irq_prefix <irq-pattern-prefix>)|(-rps <rps_map>)]"
fi
shift
done
}
setup() {
setup_loopback_environment "${DEV}"
# Set up server_ns namespace and client_ns namespace
setup_macvlan_ns "${DEV}" server_ns server \
"${SERVER_MAC}" "${SERVER_IP}"
setup_macvlan_ns "${DEV}" client_ns client \
"${CLIENT_MAC}" "${CLIENT_IP}"
}
cleanup() {
cleanup_macvlan_ns server_ns server client_ns client
cleanup_loopback "${DEV}"
}
parse_opts $0 $@
setup
trap cleanup EXIT
check_nic_rxhash_enabled
# Actual test starts here
if [[ "${TEST_RSS}" = true ]]; then
# RPS/RFS must be disabled because they move packets between cpus,
# which breaks the PACKET_FANOUT_CPU identification of RSS decisions.
eval "$(get_disable_rfs_cmd) $(get_disable_rps_cmd)" \
ip netns exec server_ns ./toeplitz "${IP_FLAG}" "${PROTO_FLAG}" \
-d "${PORT}" -i "${DEV}" -k "${KEY}" -T 1000 \
-C "$(get_rx_irq_cpus)" -s -v &
elif [[ ! -z "${RPS_MAP}" ]]; then
eval "$(get_disable_rfs_cmd) $(get_set_rps_bitmaps_cmd ${RPS_MAP})" \
ip netns exec server_ns ./toeplitz "${IP_FLAG}" "${PROTO_FLAG}" \
-d "${PORT}" -i "${DEV}" -k "${KEY}" -T 1000 \
-r "0x${RPS_MAP}" -s -v &
else
ip netns exec server_ns ./toeplitz "${IP_FLAG}" "${PROTO_FLAG}" \
-d "${PORT}" -i "${DEV}" -k "${KEY}" -T 1000 -s -v &
fi
server_pid=$!
ip netns exec client_ns ./toeplitz_client.sh "${PROTO_FLAG}" \
"${IP_FLAG}" "${SERVER_IP%%/*}" "${PORT}" &
client_pid=$!
wait "${server_pid}"
exit_code=$?
kill -9 "${client_pid}"
if [[ "${exit_code}" -eq 0 ]]; then
echo "Test Succeeded!"
fi
exit "${exit_code}"
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# A simple program for generating traffic for the toeplitz test.
#
# This program sends packets periodically for, conservatively, 20 seconds. The
# intent is for the calling program to kill this program once it is no longer
# needed, rather than waiting for the 20 second expiration.
send_traffic() {
expiration=$((SECONDS+20))
while [[ "${SECONDS}" -lt "${expiration}" ]]
do
if [[ "${PROTO}" == "-u" ]]; then
echo "msg $i" | nc "${IPVER}" -u -w 0 "${ADDR}" "${PORT}"
else
echo "msg $i" | nc "${IPVER}" -w 0 "${ADDR}" "${PORT}"
fi
sleep 0.001
done
}
PROTO=$1
IPVER=$2
ADDR=$3
PORT=$4
send_traffic
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment