mirror of
https://github.com/torvalds/linux.git
synced 2026-10-16 12:50:02 +02:00
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 <leitao@debian.org> Reviewed-by: Gustavo Luiz Duarte <gustavold@gmail.com> Link: https://patch.msgid.link/20260810-netcons-userdata-rcu-v3-2-f65557f769ce@debian.org Signed-off-by: Paolo Abeni <pabeni@redhat.com>
230 lines
6.0 KiB
Bash
Executable File
230 lines
6.0 KiB
Bash
Executable File
#!/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 <leitao@debian.org>
|
|
|
|
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 RET=0
|
|
local PID
|
|
|
|
for WORKER in "$@"
|
|
do
|
|
"${WORKER}" &
|
|
PIDS+=("$!")
|
|
done
|
|
|
|
# Reap every worker before reporting a failure, otherwise a surviving
|
|
# worker keeps writing to configfs while the exit trap cleans it up.
|
|
for PID in "${PIDS[@]}"
|
|
do
|
|
wait "${PID}" || RET=1
|
|
done
|
|
|
|
if [[ "${RET}" -ne 0 ]]
|
|
then
|
|
echo "FAIL: userdata torture worker failed" >&2
|
|
exit "${ksft_fail}"
|
|
fi
|
|
}
|
|
|
|
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}"
|