Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1#!/bin/bash
  2# SPDX-License-Identifier: GPL-2.0
  3# Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
  4
  5# Shell functions for the rest of the scripts.
  6
  7MAX_RETRIES=600
  8RETRY_INTERVAL=".1"	# seconds
  9KLP_SYSFS_DIR="/sys/kernel/livepatch"
 10
 11# Kselftest framework requirement - SKIP code is 4
 12ksft_skip=4
 13
 14# log(msg) - write message to kernel log
 15#	msg - insightful words
 16function log() {
 17	echo "$1" > /dev/kmsg
 18}
 19
 20# skip(msg) - testing can't proceed
 21#	msg - explanation
 22function skip() {
 23	log "SKIP: $1"
 24	echo "SKIP: $1" >&2
 25	exit $ksft_skip
 26}
 27
 28# root test
 29function is_root() {
 30	uid=$(id -u)
 31	if [ $uid -ne 0 ]; then
 32		echo "skip all tests: must be run as root" >&2
 33		exit $ksft_skip
 34	fi
 35}
 36
 37# die(msg) - game over, man
 38#	msg - dying words
 39function die() {
 40	log "ERROR: $1"
 41	echo "ERROR: $1" >&2
 42	exit 1
 43}
 44
 45# save existing dmesg so we can detect new content
 46function save_dmesg() {
 47	SAVED_DMESG=$(mktemp --tmpdir -t klp-dmesg-XXXXXX)
 48	dmesg > "$SAVED_DMESG"
 49}
 50
 51# cleanup temporary dmesg file from save_dmesg()
 52function cleanup_dmesg_file() {
 53	rm -f "$SAVED_DMESG"
 54}
 55
 56function push_config() {
 57	DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \
 58			awk -F'[: ]' '{print "file " $1 " line " $2 " " $4}')
 59	FTRACE_ENABLED=$(sysctl --values kernel.ftrace_enabled)
 60}
 61
 62function pop_config() {
 63	if [[ -n "$DYNAMIC_DEBUG" ]]; then
 64		echo -n "$DYNAMIC_DEBUG" > /sys/kernel/debug/dynamic_debug/control
 65	fi
 66	if [[ -n "$FTRACE_ENABLED" ]]; then
 67		sysctl kernel.ftrace_enabled="$FTRACE_ENABLED" &> /dev/null
 68	fi
 69}
 70
 71function set_dynamic_debug() {
 72        cat <<-EOF > /sys/kernel/debug/dynamic_debug/control
 73		file kernel/livepatch/* +p
 74		func klp_try_switch_task -p
 75		EOF
 76}
 77
 78function set_ftrace_enabled() {
 79	local can_fail=0
 80	if [[ "$1" == "--fail" ]] ; then
 81		can_fail=1
 82		shift
 83	fi
 84
 85	local err=$(sysctl -q kernel.ftrace_enabled="$1" 2>&1)
 86	local result=$(sysctl --values kernel.ftrace_enabled)
 87
 88	if [[ "$result" != "$1" ]] ; then
 89		if [[ $can_fail -eq 1 ]] ; then
 90			echo "livepatch: $err" | sed 's#/proc/sys/kernel/#kernel.#' > /dev/kmsg
 91			return
 92		fi
 93
 94		skip "failed to set kernel.ftrace_enabled = $1"
 95	fi
 96
 97	echo "livepatch: kernel.ftrace_enabled = $result" > /dev/kmsg
 98}
 99
100function cleanup() {
101	pop_config
102	cleanup_dmesg_file
103}
104
105# setup_config - save the current config and set a script exit trap that
106#		 restores the original config.  Setup the dynamic debug
107#		 for verbose livepatching output and turn on
108#		 the ftrace_enabled sysctl.
109function setup_config() {
110	is_root
111	push_config
112	set_dynamic_debug
113	set_ftrace_enabled 1
114	trap cleanup EXIT INT TERM HUP
115}
116
117# loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES,
118#		    sleep $RETRY_INTERVAL between attempts
119#	cmd - command and its arguments to run
120function loop_until() {
121	local cmd="$*"
122	local i=0
123	while true; do
124		eval "$cmd" && return 0
125		[[ $((i++)) -eq $MAX_RETRIES ]] && return 1
126		sleep $RETRY_INTERVAL
127	done
128}
129
130function assert_mod() {
131	local mod="$1"
132
133	modprobe --dry-run "$mod" &>/dev/null
134}
135
136function is_livepatch_mod() {
137	local mod="$1"
138
139	if [[ $(modinfo "$mod" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then
140		return 0
141	fi
142
143	return 1
144}
145
146function __load_mod() {
147	local mod="$1"; shift
148
149	local msg="% modprobe $mod $*"
150	log "${msg%% }"
151	ret=$(modprobe "$mod" "$@" 2>&1)
152	if [[ "$ret" != "" ]]; then
153		die "$ret"
154	fi
155
156	# Wait for module in sysfs ...
157	loop_until '[[ -e "/sys/module/$mod" ]]' ||
158		die "failed to load module $mod"
159}
160
161
162# load_mod(modname, params) - load a kernel module
163#	modname - module name to load
164#	params  - module parameters to pass to modprobe
165function load_mod() {
166	local mod="$1"; shift
167
168	assert_mod "$mod" ||
169		skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root"
170
171	is_livepatch_mod "$mod" &&
172		die "use load_lp() to load the livepatch module $mod"
173
174	__load_mod "$mod" "$@"
175}
176
177# load_lp_nowait(modname, params) - load a kernel module with a livepatch
178#			but do not wait on until the transition finishes
179#	modname - module name to load
180#	params  - module parameters to pass to modprobe
181function load_lp_nowait() {
182	local mod="$1"; shift
183
184	assert_mod "$mod" ||
185		skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root"
186
187	is_livepatch_mod "$mod" ||
188		die "module $mod is not a livepatch"
189
190	__load_mod "$mod" "$@"
191
192	# Wait for livepatch in sysfs ...
193	loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' ||
194		die "failed to load module $mod (sysfs)"
195}
196
197# load_lp(modname, params) - load a kernel module with a livepatch
198#	modname - module name to load
199#	params  - module parameters to pass to modprobe
200function load_lp() {
201	local mod="$1"; shift
202
203	load_lp_nowait "$mod" "$@"
204
205	# Wait until the transition finishes ...
206	loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' ||
207		die "failed to complete transition"
208}
209
210# load_failing_mod(modname, params) - load a kernel module, expect to fail
211#	modname - module name to load
212#	params  - module parameters to pass to modprobe
213function load_failing_mod() {
214	local mod="$1"; shift
215
216	local msg="% modprobe $mod $*"
217	log "${msg%% }"
218	ret=$(modprobe "$mod" "$@" 2>&1)
219	if [[ "$ret" == "" ]]; then
220		die "$mod unexpectedly loaded"
221	fi
222	log "$ret"
223}
224
225# unload_mod(modname) - unload a kernel module
226#	modname - module name to unload
227function unload_mod() {
228	local mod="$1"
229
230	# Wait for module reference count to clear ...
231	loop_until '[[ $(cat "/sys/module/$mod/refcnt") == "0" ]]' ||
232		die "failed to unload module $mod (refcnt)"
233
234	log "% rmmod $mod"
235	ret=$(rmmod "$mod" 2>&1)
236	if [[ "$ret" != "" ]]; then
237		die "$ret"
238	fi
239
240	# Wait for module in sysfs ...
241	loop_until '[[ ! -e "/sys/module/$mod" ]]' ||
242		die "failed to unload module $mod (/sys/module)"
243}
244
245# unload_lp(modname) - unload a kernel module with a livepatch
246#	modname - module name to unload
247function unload_lp() {
248	unload_mod "$1"
249}
250
251# disable_lp(modname) - disable a livepatch
252#	modname - module name to unload
253function disable_lp() {
254	local mod="$1"
255
256	log "% echo 0 > /sys/kernel/livepatch/$mod/enabled"
257	echo 0 > /sys/kernel/livepatch/"$mod"/enabled
258
259	# Wait until the transition finishes and the livepatch gets
260	# removed from sysfs...
261	loop_until '[[ ! -e "/sys/kernel/livepatch/$mod" ]]' ||
262		die "failed to disable livepatch $mod"
263}
264
265# set_pre_patch_ret(modname, pre_patch_ret)
266#	modname - module name to set
267#	pre_patch_ret - new pre_patch_ret value
268function set_pre_patch_ret {
269	local mod="$1"; shift
270	local ret="$1"
271
272	log "% echo $ret > /sys/module/$mod/parameters/pre_patch_ret"
273	echo "$ret" > /sys/module/"$mod"/parameters/pre_patch_ret
274
275	# Wait for sysfs value to hold ...
276	loop_until '[[ $(cat "/sys/module/$mod/parameters/pre_patch_ret") == "$ret" ]]' ||
277		die "failed to set pre_patch_ret parameter for $mod module"
278}
279
280function start_test {
281	local test="$1"
282
283	save_dmesg
284	echo -n "TEST: $test ... "
285	log "===== TEST: $test ====="
286}
287
288# check_result() - verify dmesg output
289#	TODO - better filter, out of order msgs, etc?
290function check_result {
291	local expect="$*"
292	local result
293
294	# Note: when comparing dmesg output, the kernel log timestamps
295	# help differentiate repeated testing runs.  Remove them with a
296	# post-comparison sed filter.
297
298	result=$(dmesg | comm --nocheck-order -13 "$SAVED_DMESG" - | \
299		 grep -e 'livepatch:' -e 'test_klp' | \
300		 grep -v '\(tainting\|taints\) kernel' | \
301		 sed 's/^\[[ 0-9.]*\] //')
302
303	if [[ "$expect" == "$result" ]] ; then
304		echo "ok"
305	else
306		echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n"
307		die "livepatch kselftest(s) failed"
308	fi
309
310	cleanup_dmesg_file
311}
312
313# check_sysfs_rights(modname, rel_path, expected_rights) - check sysfs
314# path permissions
315#	modname - livepatch module creating the sysfs interface
316#	rel_path - relative path of the sysfs interface
317#	expected_rights - expected access rights
318function check_sysfs_rights() {
319	local mod="$1"; shift
320	local rel_path="$1"; shift
321	local expected_rights="$1"; shift
322
323	local path="$KLP_SYSFS_DIR/$mod/$rel_path"
324	local rights=$(/bin/stat --format '%A' "$path")
325	if test "$rights" != "$expected_rights" ; then
326		die "Unexpected access rights of $path: $expected_rights vs. $rights"
327	fi
328}
329
330# check_sysfs_value(modname, rel_path, expected_value) - check sysfs value
331#	modname - livepatch module creating the sysfs interface
332#	rel_path - relative path of the sysfs interface
333#	expected_value - expected value read from the file
334function check_sysfs_value() {
335	local mod="$1"; shift
336	local rel_path="$1"; shift
337	local expected_value="$1"; shift
338
339	local path="$KLP_SYSFS_DIR/$mod/$rel_path"
340	local value=`cat $path`
341	if test "$value" != "$expected_value" ; then
342		die "Unexpected value in $path: $expected_value vs. $value"
343	fi
344}