#! /bin/bash # SPDX-License-Identifier: GPL-2.0 set -e set -u unset KBUILD_OUTPUT current_dir="$(realpath "$(dirname "$0")")" build_dir="$current_dir" build_include="$current_dir/include.sh" if test -f "$build_include"; then # this include will define "$mk_build_dir" as the location the test was # built. We will need this if the tests are installed in a location # other than the kernel source source "$build_include" build_dir="$mk_build_dir" fi # Source settings for timeout value (also used by ksft runner) source "$current_dir"/settings # This test requires kernel source and the *.gcda data therein # Locate the top level of the kernel source, and the net/rds # subfolder with the appropriate *.gcno object files ksrc_dir="$(realpath "$build_dir"/../../../../../)" kconfig="$ksrc_dir/.config" obj_dir="$ksrc_dir/net/rds" GCOV_CMD=gcov #check to see if the host has the required packages to generate a gcov report check_gcov_env() { if ! which "$GCOV_CMD" > /dev/null 2>&1; then echo "# Warning: Could not find gcov. " GENERATE_GCOV_REPORT=0 return fi # the gcov version must match the gcc version GCC_VER=$(gcc -dumpfullversion) GCOV_VER=$($GCOV_CMD -v | grep gcov | awk '{print $3}'| awk 'BEGIN {FS="-"}{print $1}') if [ "$GCOV_VER" != "$GCC_VER" ]; then #attempt to find a matching gcov version GCOV_CMD=gcov-$(gcc -dumpversion) if ! which "$GCOV_CMD" > /dev/null 2>&1; then echo "# Warning: Could not find an appropriate gcov installation. \ gcov version must match gcc version" GENERATE_GCOV_REPORT=0 return fi #recheck version number of found gcov executable GCOV_VER=$($GCOV_CMD -v | grep gcov | awk '{print $3}'| \ awk 'BEGIN {FS="-"}{print $1}') if [ "$GCOV_VER" != "$GCC_VER" ]; then echo "# Warning: Could not find an appropriate gcov installation. \ gcov version must match gcc version" GENERATE_GCOV_REPORT=0 else echo "# Warning: Mismatched gcc and gcov detected. Using $GCOV_CMD" fi fi } # Check to see if the kconfig has the required configs to generate a coverage report check_gcov_conf() { if ! grep -x "CONFIG_GCOV_PROFILE_RDS=y" "$kconfig" > /dev/null 2>&1; then echo "# INFO: CONFIG_GCOV_PROFILE_RDS should be enabled for coverage reports" GENERATE_GCOV_REPORT=0 fi if ! grep -x "CONFIG_GCOV_KERNEL=y" "$kconfig" > /dev/null 2>&1; then echo "# INFO: CONFIG_GCOV_KERNEL should be enabled for coverage reports" GENERATE_GCOV_REPORT=0 fi if grep -x "CONFIG_GCOV_PROFILE_ALL=y" "$kconfig" > /dev/null 2>&1; then echo "# INFO: CONFIG_GCOV_PROFILE_ALL should be disabled for coverage reports" GENERATE_GCOV_REPORT=0 fi if [ "$GENERATE_GCOV_REPORT" -eq 0 ]; then echo "# To enable gcov reports, please run "\ "\"tools/testing/selftests/net/rds/config.sh -g\" and rebuild the kernel" else # if we have the required kernel configs, proceed to check the environment to # ensure we have the required gcov packages check_gcov_env fi } # Checks if a kconfig is enabled (set to =y or =m) # $1: kconfig symbol to check # $2: (optional) module name backing $1 # Ex: check_conf_enabled CONFIG_RDS_TCP rds_tcp # Modules for configs set to =m will be probed # If omitted, only a built-in (=y) config is accepted. # Returns on success. exits 4 on failure # Kselftest framework requirement - SKIP code is 4. check_conf_enabled() { if grep -x "$1=y" "$kconfig" > /dev/null 2>&1; then return fi if [ -n "${2:-}" ] && grep -x "$1=m" "$kconfig" > /dev/null 2>&1; then probe_module "$2" return fi echo "selftests: [SKIP] This test requires $1 enabled" echo "Please run" \ "tools/testing/selftests/net/rds/config.sh and rebuild the kernel" exit 4 } check_rdma_conf_enabled() { if grep -x "$1=y" "$kconfig" > /dev/null 2>&1; then return fi if [ -n "${2:-}" ] && grep -x "$1=m" "$kconfig" > /dev/null 2>&1; then probe_module "$2" return fi echo "selftests: [XFAIL] rdma transport requires $1 enabled" echo "To enable, run" \ "tools/testing/selftests/net/rds/config.sh -r and rebuild" exit 2 } # Load the module backing a config that is built as a loadable module # (=m). Built-in (=y) configs are already available and don't reach # here. Exits with the SKIP code if a required module cannot be loaded. probe_module() { if ! modprobe -q "$1"; then echo "selftests: [SKIP] could not load required module $1" exit 4 fi } check_conf() { check_conf_enabled CONFIG_NET_SCH_NETEM sch_netem check_conf_enabled CONFIG_VETH veth check_conf_enabled CONFIG_NET_NS check_conf_enabled CONFIG_RDS_TCP rds_tcp check_conf_enabled CONFIG_RDS rds } # Check kernel config and host environment for RDS-RDMA support. # Exits with XFAIL (2) if the user requested rdma but prerequisites # are not met. check_rdma_conf() { case "$TRANSPORT" in *rdma*) ;; *) return ;; esac # Kconfig will enforce CONFIG_INFINIBAND_* as dependencies # of CONFIG_RDMA_RXE check_rdma_conf_enabled CONFIG_RDMA_RXE rdma_rxe check_rdma_conf_enabled CONFIG_RDS_RDMA rds_rdma if ! which rdma > /dev/null 2>&1; then echo "selftests: [XFAIL] rdma transport requires the 'rdma'" \ "tool (iproute2)" exit 2 fi } check_env() { if ! test -d "$obj_dir"; then echo "selftests: [SKIP] This test requires a kernel source tree" exit 4 fi if ! test -e "$kconfig"; then echo "selftests: [SKIP] This test requires a configured kernel source tree" exit 4 fi if ! which strace > /dev/null 2>&1; then echo "selftests: [SKIP] Could not run test without strace" exit 4 fi if ! which tcpdump > /dev/null 2>&1; then echo "selftests: [SKIP] Could not run test without tcpdump" exit 4 fi if ! which python3 > /dev/null 2>&1; then echo "selftests: [SKIP] Could not run test without python3" exit 4 fi python_major=$(python3 -c "import sys; print(sys.version_info[0])") python_minor=$(python3 -c "import sys; print(sys.version_info[1])") if [[ python_major -lt 3 || ( python_major -eq 3 && python_minor -lt 9 ) ]] ; then echo "selftests: [SKIP] Could not run test without at least python3.9" python3 -V exit 4 fi } LOG_DIR="${RDS_LOG_DIR:-}" TIMEOUT=$timeout GENERATE_GCOV_REPORT=1 TRANSPORT=tcp FLAGS=() while getopts "d:l:c:u:t:T:" opt; do case ${opt} in d) LOG_DIR=${OPTARG} ;; l) FLAGS+=("-l" "${OPTARG}") ;; c) FLAGS+=("-c" "${OPTARG}") ;; t) TIMEOUT=${OPTARG} ;; u) FLAGS+=("-u" "${OPTARG}") ;; T) TRANSPORT=${OPTARG} ;; :) echo "USAGE: rds_run.sh [-d logdir] [-l packet_loss]" \ "[-c packet_corruption] [-u packet_duplicate] [-t timeout]" \ "[-T tcp|rdma|tcp,rdma]" exit 1 ;; ?) echo "Invalid option: -${OPTARG}." exit 1 ;; esac done # Validate transport tokens IFS=',' read -ra transports <<< "$TRANSPORT" for t in "${transports[@]}"; do if [ "$t" != "tcp" ] && [ "$t" != "rdma" ]; then echo "rds_run.sh: unknown transport '$t' (expected tcp or rdma)" exit 1 fi done FLAGS+=("--transport" "${TRANSPORT}") check_env check_conf check_gcov_conf check_rdma_conf TRACE_CMD=() if [[ -n "$LOG_DIR" ]]; then FLAGS+=("-d" "$LOG_DIR") TRACE_FILE="${LOG_DIR}/rds-strace.txt" COVR_DIR="${LOG_DIR}/coverage/" DMESG_FILE="${LOG_DIR}/rds-dmesg.out" mkdir -p "$LOG_DIR" mkdir -p "$COVR_DIR" rm -f "$TRACE_FILE" rm -f "$DMESG_FILE" rm -f "$LOG_DIR"/rds-*.pcap rm -f "$COVR_DIR"/gcovr* echo "# Traces will be logged to ${TRACE_FILE}" TRACE_CMD=(strace -T -tt -o "${TRACE_FILE}") fi set +e echo "# running RDS tests..." "${TRACE_CMD[@]}" python3 "$(dirname "$0")/test.py" "${FLAGS[@]}" -t "$TIMEOUT" test_rc=$? if [[ -n "$LOG_DIR" ]]; then dmesg > "${DMESG_FILE}" fi if [[ -n "$LOG_DIR" ]] && [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then echo "# saving coverage data..." # Ensure debugfs is mounted before reading gcov data. if ! mountpoint -q /sys/kernel/debug 2>/dev/null; then mount -t debugfs debugfs /sys/kernel/debug 2>/dev/null || true fi (set +x; cd /sys/kernel/debug/gcov; find ./* -name '*.gcda' | \ while read -r f do cat < "/sys/kernel/debug/gcov/$f" > "/$f" done) echo "# running gcovr..." gcovr -s --html-details --gcov-executable "$GCOV_CMD" --gcov-ignore-parse-errors \ --root "${ksrc_dir}" -o "${COVR_DIR}/gcovr" "${ksrc_dir}/net/rds/" \ > "${LOG_DIR}/gcovr.log" 2>&1 echo "# gcovr log: ${LOG_DIR}/gcovr.log" else echo "# Coverage report will be skipped" fi if [ "$test_rc" -eq 0 ]; then echo "# PASS: Test completed successfully" else echo "# FAIL: Test failed" fi exit "$test_rc"