xref: /linux/tools/testing/selftests/drivers/net/netconsole/netcons_userdata.sh (revision cf85f810f911234a06a4ef2439e8694b93b717fc)
1#!/usr/bin/env bash
2# SPDX-License-Identifier: GPL-2.0
3
4# Exercise the netconsole userdata payload.
5#
6# The first part checks that the payload the target transmits follows what
7# configfs says: a value shows up in the next message, an update replaces the
8# previous one, clearing the value drops the entry, and so does removing the
9# key.
10#
11# The second part rewrites values, creates and deletes keys, and clears the
12# payload entirely while messages are being sent, so the transmit path keeps
13# picking up payloads that are being replaced underneath it. It runs twice,
14# once with a payload small enough to fit in a single packet and once large
15# enough to be fragmented.
16#
17# Author: Breno Leitao <leitao@debian.org>
18
19set -euo pipefail
20
21SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
22
23source "${SCRIPTDIR}"/../lib/sh/lib_netcons.sh
24
25# Number of times each torture worker loops
26ITERATIONS=${1:-200}
27
28# Keys owned by each torture worker. Workers do not share keys, so a failing
29# configfs operation means a real problem and not a lost race.
30CHURN_KEY="churnkey"
31TRANSIENT_KEY="transientkey"
32# Number of keys used to push a message past MAX_PRINT_CHUNK
33BULK_KEYS=8
34
35USERDATA_DIR="${NETCONS_PATH}/userdata"
36# Values are capped at MAX_EXTRADATA_VALUE_LEN(200) bytes, so ${BULK_KEYS}
37# entries of this size are enough to force fragmentation
38LONG_VALUE=$(printf -- 'v%.0s' {1..190})
39
40function write_key() {
41	local KEY="${1}"
42	local VALUE="${2}"
43
44	mkdir -p "${USERDATA_DIR}/${KEY}"
45	echo "${VALUE}" > "${USERDATA_DIR}/${KEY}/value"
46}
47
48# Send a single message and capture it on the destination interface
49function send_and_capture() {
50	rm -f "${OUTPUT_FILE}"
51
52	listen_port_and_save_to "${OUTPUT_FILE}" &
53	wait_for_port "${NAMESPACE}" "${PORT}" "${IP_VERSION}"
54	echo "${MSG}: ${TARGET}" > /dev/kmsg
55	busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true
56	pkill_socat
57	validate_msg "${OUTPUT_FILE}"
58}
59
60function expect_in_msg() {
61	local WANTED="${1}"
62
63	if ! grep -q -- "${WANTED}" "${OUTPUT_FILE}"; then
64		echo "FAIL: '${WANTED}' not found in ${OUTPUT_FILE}" >&2
65		cat "${OUTPUT_FILE}" >&2
66		exit "${ksft_fail}"
67	fi
68}
69
70function expect_not_in_msg() {
71	local UNWANTED="${1}"
72
73	if grep -q -- "${UNWANTED}" "${OUTPUT_FILE}"; then
74		echo "FAIL: '${UNWANTED}' found in ${OUTPUT_FILE}" >&2
75		cat "${OUTPUT_FILE}" >&2
76		exit "${ksft_fail}"
77	fi
78}
79
80# Every write publishes a new payload and frees the previous one. An empty
81# value is skipped when the payload is formatted, so this also drives the
82# target through having no payload at all.
83function churn_value() {
84	local i
85
86	for i in $(seq "${ITERATIONS}")
87	do
88		echo "value${i}" > "${USERDATA_DIR}/${CHURN_KEY}/value"
89		echo > "${USERDATA_DIR}/${CHURN_KEY}/value"
90	done
91}
92
93# Create and delete a key underneath the sender
94function churn_key() {
95	local i
96
97	for i in $(seq "${ITERATIONS}")
98	do
99		mkdir "${USERDATA_DIR}/${TRANSIENT_KEY}"
100		echo "transient${i}" > "${USERDATA_DIR}/${TRANSIENT_KEY}/value"
101		rmdir "${USERDATA_DIR}/${TRANSIENT_KEY}"
102	done
103}
104
105# Keep the transmit path busy while the payload is being replaced
106function send_messages() {
107	local i
108
109	for i in $(seq "${ITERATIONS}")
110	do
111		echo "${MSG}: ${TARGET} ${i}" > /dev/kmsg
112	done
113}
114
115# Run the workers concurrently and fail if any of them hits an error
116function run_workers() {
117	local PIDS=()
118	local WORKER
119	local RET=0
120	local PID
121
122	for WORKER in "$@"
123	do
124		"${WORKER}" &
125		PIDS+=("$!")
126	done
127
128	# Reap every worker before reporting a failure, otherwise a surviving
129	# worker keeps writing to configfs while the exit trap cleans it up.
130	for PID in "${PIDS[@]}"
131	do
132		wait "${PID}" || RET=1
133	done
134
135	if [[ "${RET}" -ne 0 ]]
136	then
137		echo "FAIL: userdata torture worker failed" >&2
138		exit "${ksft_fail}"
139	fi
140}
141
142function create_bulk_keys() {
143	local i
144
145	for i in $(seq "${BULK_KEYS}")
146	do
147		write_key "bulk${i}" "${LONG_VALUE}"
148	done
149}
150
151function delete_bulk_keys() {
152	local i
153
154	for i in $(seq "${BULK_KEYS}")
155	do
156		rmdir "${USERDATA_DIR}/bulk${i}"
157	done
158}
159
160# ========== #
161# Start here #
162# ========== #
163
164modprobe netdevsim 2> /dev/null || true
165modprobe netconsole 2> /dev/null || true
166
167IP_VERSION="ipv4"
168# The content of kmsg will be saved to the following file
169OUTPUT_FILE="/tmp/${TARGET}"
170
171# Check for basic system dependency and exit if not found
172check_for_dependencies
173# Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5)
174echo "6 5" > /proc/sys/kernel/printk
175# Remove the namespace, interfaces and netconsole target on exit
176trap cleanup EXIT
177# Create one namespace and two interfaces
178set_network "${IP_VERSION}"
179# Create a dynamic target for netconsole
180create_dynamic_target
181
182# ===================================================
183# TEST #1
184# A value written to configfs reaches the destination
185# ===================================================
186write_key "${USERDATA_KEY}" "first"
187send_and_capture
188expect_in_msg "${USERDATA_KEY}=first"
189
190# ===================================================
191# TEST #2
192# Updating the value replaces the previous payload
193# ===================================================
194write_key "${USERDATA_KEY}" "second"
195send_and_capture
196expect_in_msg "${USERDATA_KEY}=second"
197expect_not_in_msg "${USERDATA_KEY}=first"
198
199# ===================================================
200# TEST #3
201# Clearing the value drops the entry
202# ===================================================
203echo > "${USERDATA_DIR}/${USERDATA_KEY}/value"
204send_and_capture
205expect_not_in_msg "${USERDATA_KEY}="
206
207# ===================================================
208# TEST #4
209# Removing the key drops the entry
210# ===================================================
211write_key "${USERDATA_KEY}" "third"
212rmdir "${USERDATA_DIR}/${USERDATA_KEY}"
213send_and_capture
214expect_not_in_msg "${USERDATA_KEY}="
215rm "${OUTPUT_FILE}"
216
217# ===================================================
218# TEST #5
219# Torture the payload while messages are being sent,
220# first unfragmented and then fragmented
221# ===================================================
222write_key "${CHURN_KEY}" "${USERDATA_VALUE}"
223run_workers churn_value churn_key send_messages
224
225create_bulk_keys
226run_workers churn_value churn_key send_messages
227delete_bulk_keys
228
229exit "${ksft_pass}"
230