Commit 7d157501 authored by Coco Li's avatar Coco Li Committed by David S. Miller
Browse files

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: default avatarCoco Li <lixiaoyan@google.com>
Reviewed-by: default avatarWillem de Bruijn <willemb@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ab996c42
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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

+1095 −0

File added.

Preview size limit exceeded, changes collapsed.

+128 −0
Original line number Diff line number Diff line
#!/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;
+82 −0
Original line number Diff line number Diff line
#!/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
}