selftests/net: GRO coalesce test
Implement a GRO testsuite that expects Linux kernel GRO behavior. All tests pass with the kernel software GRO stack. Run against a device with hardware GRO to verify that it matches the software stack. gro.c generates packets and sends them out through a packet socket. The receiver in gro.c (run separately) receives the packets on a packet socket, filters them by destination ports using BPF and checks the packet geometry to see whether GRO was applied. gro.sh provides a wrapper to run the gro.c in NIC loopback mode. It is not included in continuous testing because it modifies network configuration around a physical NIC: gro.sh sets the NIC in loopback mode, creates macvlan devices on the physical device in separate namespaces, and sends traffic generated by gro.c between the two namespaces to observe coalescing behavior. GRO coalescing is time sensitive. Some tests may prove flaky on some hardware. Note that this test suite tests for software GRO unless hardware GRO is enabled (ethtool -K $DEV rx-gro-hw on). To test, run ./gro.sh. The wrapper will output success or failed test names, and generate log.txt and stderr. Sample log.txt result: ... pure data packet of same size: Test succeeded large data packets followed by a smaller one: Test succeeded small data packets followed by a larger one: Test succeeded ... Sample stderr result: ... carrier ready running test ipv4 data Expected {200 }, Total 1 packets Received {200 }, Total 1 packets. ... Signed-off-by: Coco Li <lixiaoyan@google.com> Reviewed-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
ab996c4205
commit
7d1575014a
|
@ -38,6 +38,7 @@ 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
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,128 @@
|
|||
#!/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;
|
|
@ -0,0 +1,82 @@
|
|||
#!/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
|
||||
}
|
Loading…
Reference in New Issue