[PATCH net-next 2/2] selftests: netconsole: add a userdata torture test
Breno Leitao <[email protected]> Mon, 03 Aug 2026 04:30:04 -0700
| Newsgroups | org.kernel.vger.linux-kselftest,org.kernel.vger.linux-kernel,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
The userdata payload is rebuilt and republished on every configfs write, including while the target is enabled and messages are being sent. Add netcons_userdata.sh that runs random tests with userdata. Signed-off-by: Breno Leitao <[email protected]> --- .../selftests/drivers/net/netconsole/Makefile | 1 + .../drivers/net/netconsole/netcons_userdata.sh | 224 +++++++++++++++++++++ 2 files changed, 225 insertions(+) diff --git a/tools/testing/selftests/drivers/net/netconsole/Makefile b/tools/testing/selftests/drivers/net/netconsole/Makefile index b56c70b7e2742..f0674c0017fc4 100644 --- a/tools/testing/selftests/drivers/net/netconsole/Makefile +++ b/tools/testing/selftests/drivers/net/netconsole/Makefile @@ -13,6 +13,7 @@ TEST_PROGS := \ netcons_resume.sh \ netcons_sysdata.sh \ netcons_torture.sh \ + netcons_userdata.sh \ # end of TEST_PROGS include ../../../lib.mk diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_userdata.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_userdata.sh new file mode 100755 index 0000000000000..bf4c041d4d615 --- /dev/null +++ b/tools/testing/selftests/drivers/net/netconsole/netcons_userdata.sh @@ -0,0 +1,224 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-2.0 + +# Exercise the netconsole userdata payload. +# +# The first part checks that the payload the target transmits follows what +# configfs says: a value shows up in the next message, an update replaces the +# previous one, clearing the value drops the entry, and so does removing the +# key. +# +# The second part rewrites values, creates and deletes keys, and clears the +# payload entirely while messages are being sent, so the transmit path keeps +# picking up payloads that are being replaced underneath it. It runs twice, +# once with a payload small enough to fit in a single packet and once large +# enough to be fragmented. +# +# Author: Breno Leitao <[email protected]> + +set -euo pipefail + +SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")") + +source "${SCRIPTDIR}"/../lib/sh/lib_netcons.sh + +# Number of times each torture worker loops +ITERATIONS=${1:-200} + +# Keys owned by each torture worker. Workers do not share keys, so a failing +# configfs operation means a real problem and not a lost race. +CHURN_KEY="churnkey" +TRANSIENT_KEY="transientkey" +# Number of keys used to push a message past MAX_PRINT_CHUNK +BULK_KEYS=8 + +USERDATA_DIR="${NETCONS_PATH}/userdata" +# Values are capped at MAX_EXTRADATA_VALUE_LEN(200) bytes, so ${BULK_KEYS} +# entries of this size are enough to force fragmentation +LONG_VALUE=$(printf -- 'v%.0s' {1..190}) + +function write_key() { + local KEY="${1}" + local VALUE="${2}" + + mkdir -p "${USERDATA_DIR}/${KEY}" + echo "${VALUE}" > "${USERDATA_DIR}/${KEY}/value" +} + +# Send a single message and capture it on the destination interface +function send_and_capture() { + rm -f "${OUTPUT_FILE}" + + listen_port_and_save_to "${OUTPUT_FILE}" & + wait_for_port "${NAMESPACE}" "${PORT}" "${IP_VERSION}" + echo "${MSG}: ${TARGET}" > /dev/kmsg + busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true + pkill_socat + validate_msg "${OUTPUT_FILE}" +} + +function expect_in_msg() { + local WANTED="${1}" + + if ! grep -q -- "${WANTED}" "${OUTPUT_FILE}"; then + echo "FAIL: '${WANTED}' not found in ${OUTPUT_FILE}" >&2 + cat "${OUTPUT_FILE}" >&2 + exit "${ksft_fail}" + fi +} + +function expect_not_in_msg() { + local UNWANTED="${1}" + + if grep -q -- "${UNWANTED}" "${OUTPUT_FILE}"; then + echo "FAIL: '${UNWANTED}' found in ${OUTPUT_FILE}" >&2 + cat "${OUTPUT_FILE}" >&2 + exit "${ksft_fail}" + fi +} + +# Every write publishes a new payload and frees the previous one. An empty +# value is skipped when the payload is formatted, so this also drives the +# target through having no payload at all. +function churn_value() { + local i + + for i in $(seq "${ITERATIONS}") + do + echo "value${i}" > "${USERDATA_DIR}/${CHURN_KEY}/value" + echo > "${USERDATA_DIR}/${CHURN_KEY}/value" + done +} + +# Create and delete a key underneath the sender +function churn_key() { + local i + + for i in $(seq "${ITERATIONS}") + do + mkdir "${USERDATA_DIR}/${TRANSIENT_KEY}" + echo "transient${i}" > "${USERDATA_DIR}/${TRANSIENT_KEY}/value" + rmdir "${USERDATA_DIR}/${TRANSIENT_KEY}" + done +} + +# Keep the transmit path busy while the payload is being replaced +function send_messages() { + local i + + for i in $(seq "${ITERATIONS}") + do + echo "${MSG}: ${TARGET} ${i}" > /dev/kmsg + done +} + +# Run the workers concurrently and fail if any of them hits an error +function run_workers() { + local PIDS=() + local WORKER + local PID + + for WORKER in "$@" + do + "${WORKER}" & + PIDS+=("$!") + done + + for PID in "${PIDS[@]}" + do + if ! wait "${PID}" + then + echo "FAIL: userdata torture worker failed" >&2 + exit "${ksft_fail}" + fi + done +} + +function create_bulk_keys() { + local i + + for i in $(seq "${BULK_KEYS}") + do + write_key "bulk${i}" "${LONG_VALUE}" + done +} + +function delete_bulk_keys() { + local i + + for i in $(seq "${BULK_KEYS}") + do + rmdir "${USERDATA_DIR}/bulk${i}" + done +} + +# ========== # +# Start here # +# ========== # + +modprobe netdevsim 2> /dev/null || true +modprobe netconsole 2> /dev/null || true + +IP_VERSION="ipv4" +# The content of kmsg will be saved to the following file +OUTPUT_FILE="/tmp/${TARGET}" + +# Check for basic system dependency and exit if not found +check_for_dependencies +# Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5) +echo "6 5" > /proc/sys/kernel/printk +# Remove the namespace, interfaces and netconsole target on exit +trap cleanup EXIT +# Create one namespace and two interfaces +set_network "${IP_VERSION}" +# Create a dynamic target for netconsole +create_dynamic_target + +# =================================================== +# TEST #1 +# A value written to configfs reaches the destination +# =================================================== +write_key "${USERDATA_KEY}" "first" +send_and_capture +expect_in_msg "${USERDATA_KEY}=first" + +# =================================================== +# TEST #2 +# Updating the value replaces the previous payload +# =================================================== +write_key "${USERDATA_KEY}" "second" +send_and_capture +expect_in_msg "${USERDATA_KEY}=second" +expect_not_in_msg "${USERDATA_KEY}=first" + +# =================================================== +# TEST #3 +# Clearing the value drops the entry +# =================================================== +echo > "${USERDATA_DIR}/${USERDATA_KEY}/value" +send_and_capture +expect_not_in_msg "${USERDATA_KEY}=" + +# =================================================== +# TEST #4 +# Removing the key drops the entry +# =================================================== +write_key "${USERDATA_KEY}" "third" +rmdir "${USERDATA_DIR}/${USERDATA_KEY}" +send_and_capture +expect_not_in_msg "${USERDATA_KEY}=" +rm "${OUTPUT_FILE}" + +# =================================================== +# TEST #5 +# Torture the payload while messages are being sent, +# first unfragmented and then fragmented +# =================================================== +write_key "${CHURN_KEY}" "${USERDATA_VALUE}" +run_workers churn_value churn_key send_messages + +create_bulk_keys +run_workers churn_value churn_key send_messages +delete_bulk_keys + +exit "${ksft_pass}" -- 2.53.0-Meta