brintos

brintos / linux-shallow public Read only

0
0
Text · 15.0 KiB · 7189715 Raw
679 lines · bash
1#!/bin/bash2# SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.13# Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>4#5# This is a stress test script for kmod, the kernel module loader. It uses6# test_kmod which exposes a series of knobs for the API for us so we can7# tweak each test in userspace rather than in kernelspace.8#9# The way kmod works is it uses the kernel's usermode helper API to eventually10# call /sbin/modprobe. It has a limit of the number of concurrent calls11# possible. The kernel interface to load modules is request_module(), however12# mount uses get_fs_type(). Both behave slightly differently, but the13# differences are important enough to test each call separately. For this14# reason test_kmod starts by providing tests for both calls.15#16# The test driver test_kmod assumes a series of defaults which you can17# override by exporting to your environment prior running this script.18# For instance this script assumes you do not have xfs loaded upon boot.19# If this is false, export DEFAULT_KMOD_FS="ext4" prior to running this20# script if the filesystem module you don't have loaded upon bootup21# is ext4 instead. Refer to allow_user_defaults() for a list of user22# override variables possible.23#24# You'll want at least 4 GiB of RAM to expect to run these tests25# without running out of memory on them. For other requirements refer26# to test_reqs()27 28set -e29 30TEST_NAME="kmod"31TEST_DRIVER="test_${TEST_NAME}"32TEST_DIR=$(dirname $0)33 34# This represents35#36# TEST_ID:TEST_COUNT:ENABLED37#38# TEST_ID: is the test id number39# TEST_COUNT: number of times we should run the test40# ENABLED: 1 if enabled, 0 otherwise41#42# Once these are enabled please leave them as-is. Write your own test,43# we have tons of space.44ALL_TESTS="0001:3:1"45ALL_TESTS="$ALL_TESTS 0002:3:1"46ALL_TESTS="$ALL_TESTS 0003:1:1"47ALL_TESTS="$ALL_TESTS 0004:1:1"48ALL_TESTS="$ALL_TESTS 0005:10:1"49ALL_TESTS="$ALL_TESTS 0006:10:1"50ALL_TESTS="$ALL_TESTS 0007:5:1"51ALL_TESTS="$ALL_TESTS 0008:150:1"52ALL_TESTS="$ALL_TESTS 0009:150:1"53ALL_TESTS="$ALL_TESTS 0010:1:1"54ALL_TESTS="$ALL_TESTS 0011:1:1"55ALL_TESTS="$ALL_TESTS 0012:1:1"56ALL_TESTS="$ALL_TESTS 0013:1:1"57 58# Kselftest framework requirement - SKIP code is 4.59ksft_skip=460 61test_modprobe()62{63       if [ ! -d $DIR ]; then64               echo "$0: $DIR not present" >&265               echo "You must have the following enabled in your kernel:" >&266               cat $TEST_DIR/config >&267               exit $ksft_skip68       fi69}70 71function allow_user_defaults()72{73	if [ -z $DEFAULT_KMOD_DRIVER ]; then74		DEFAULT_KMOD_DRIVER="test_module"75	fi76 77	if [ -z $DEFAULT_KMOD_FS ]; then78		DEFAULT_KMOD_FS="xfs"79	fi80 81	if [ -z $PROC_DIR ]; then82		PROC_DIR="/proc/sys/kernel/"83	fi84 85	if [ -z $MODPROBE_LIMIT ]; then86		MODPROBE_LIMIT=5087	fi88 89	if [ -z $DIR ]; then90		DIR="/sys/devices/virtual/misc/${TEST_DRIVER}0/"91	fi92 93	if [ -z $DEFAULT_NUM_TESTS ]; then94		DEFAULT_NUM_TESTS=15095	fi96 97	MODPROBE_LIMIT_FILE="${PROC_DIR}/kmod-limit"98}99 100test_reqs()101{102	if ! which modprobe 2> /dev/null > /dev/null; then103		echo "$0: You need modprobe installed" >&2104		exit $ksft_skip105	fi106 107	if ! which kmod 2> /dev/null > /dev/null; then108		echo "$0: You need kmod installed" >&2109		exit $ksft_skip110	fi111 112	# kmod 19 has a bad bug where it returns 0 when modprobe113	# gets called *even* if the module was not loaded due to114	# some bad heuristics. For details see:115	#116	# A work around is possible in-kernel but its rather117	# complex.118	KMOD_VERSION=$(kmod --version | awk '{print $3}')119	if [[ $KMOD_VERSION  -le 19 ]]; then120		echo "$0: You need at least kmod 20" >&2121		echo "kmod <= 19 is buggy, for details see:" >&2122		echo "https://git.kernel.org/cgit/utils/kernel/kmod/kmod.git/commit/libkmod/libkmod-module.c?id=fd44a98ae2eb5eb32161088954ab21e58e19dfc4" >&2123		exit $ksft_skip124	fi125 126	uid=$(id -u)127	if [ $uid -ne 0 ]; then128		echo $msg must be run as root >&2129		exit $ksft_skip130	fi131}132 133function load_req_mod()134{135	trap "test_modprobe" EXIT136 137	if [ ! -d $DIR ]; then138		# Alanis: "Oh isn't it ironic?"139		modprobe $TEST_DRIVER140	fi141}142 143test_finish()144{145	echo "$MODPROBE" > /proc/sys/kernel/modprobe146	echo "Test completed"147}148 149errno_name_to_val()150{151	case "$1" in152	# kmod calls modprobe and upon of a module not found153	# modprobe returns just 1... However in the kernel we154	# *sometimes* see 256...155	MODULE_NOT_FOUND)156		echo 256;;157	SUCCESS)158		echo 0;;159	-EPERM)160		echo -1;;161	-ENOENT)162		echo -2;;163	-EINVAL)164		echo -22;;165	-ERR_ANY)166		echo -123456;;167	*)168		echo invalid;;169	esac170}171 172errno_val_to_name()173	case "$1" in174	256)175		echo MODULE_NOT_FOUND;;176	0)177		echo SUCCESS;;178	-1)179		echo -EPERM;;180	-2)181		echo -ENOENT;;182	-22)183		echo -EINVAL;;184	-123456)185		echo -ERR_ANY;;186	*)187		echo invalid;;188	esac189 190config_set_test_case_driver()191{192	if ! echo -n 1 >$DIR/config_test_case; then193		echo "$0: Unable to set to test case to driver" >&2194		exit 1195	fi196}197 198config_set_test_case_fs()199{200	if ! echo -n 2 >$DIR/config_test_case; then201		echo "$0: Unable to set to test case to fs" >&2202		exit 1203	fi204}205 206config_num_threads()207{208	if ! echo -n $1 >$DIR/config_num_threads; then209		echo "$0: Unable to set to number of threads" >&2210		exit 1211	fi212}213 214config_get_modprobe_limit()215{216	if [[ -f ${MODPROBE_LIMIT_FILE} ]] ; then217		MODPROBE_LIMIT=$(cat $MODPROBE_LIMIT_FILE)218	fi219	echo $MODPROBE_LIMIT220}221 222config_num_thread_limit_extra()223{224	MODPROBE_LIMIT=$(config_get_modprobe_limit)225	let EXTRA_LIMIT=$MODPROBE_LIMIT+$1226	config_num_threads $EXTRA_LIMIT227}228 229# For special characters use printf directly,230# refer to kmod_test_0001231config_set_driver()232{233	if ! echo -n $1 >$DIR/config_test_driver; then234		echo "$0: Unable to set driver" >&2235		exit 1236	fi237}238 239config_set_fs()240{241	if ! echo -n $1 >$DIR/config_test_fs; then242		echo "$0: Unable to set driver" >&2243		exit 1244	fi245}246 247config_get_driver()248{249	cat $DIR/config_test_driver250}251 252config_get_test_result()253{254	cat $DIR/test_result255}256 257config_reset()258{259	if ! echo -n "1" >"$DIR"/reset; then260		echo "$0: reset should have worked" >&2261		exit 1262	fi263}264 265config_show_config()266{267	echo "----------------------------------------------------"268	cat "$DIR"/config269	echo "----------------------------------------------------"270}271 272config_trigger()273{274	if ! echo -n "1" >"$DIR"/trigger_config 2>/dev/null; then275		echo "$1: FAIL - loading should have worked"276		config_show_config277		exit 1278	fi279	echo "$1: OK! - loading kmod test"280}281 282config_trigger_want_fail()283{284	if echo "1" > $DIR/trigger_config 2>/dev/null; then285		echo "$1: FAIL - test case was expected to fail"286		config_show_config287		exit 1288	fi289	echo "$1: OK! - kmod test case failed as expected"290}291 292config_expect_result()293{294	RC=$(config_get_test_result)295	RC_NAME=$(errno_val_to_name $RC)296 297	ERRNO_NAME=$2298	ERRNO=$(errno_name_to_val $ERRNO_NAME)299 300	if [[ $ERRNO_NAME = "-ERR_ANY" ]]; then301		if [[ $RC -ge 0 ]]; then302			echo "$1: FAIL, test expects $ERRNO_NAME - got $RC_NAME ($RC)" >&2303			config_show_config304			exit 1305		fi306	elif [[ $RC != $ERRNO ]]; then307		echo "$1: FAIL, test expects $ERRNO_NAME ($ERRNO) - got $RC_NAME ($RC)" >&2308		config_show_config309		exit 1310	fi311	echo "$1: OK! - Return value: $RC ($RC_NAME), expected $ERRNO_NAME"312}313 314kmod_defaults_driver()315{316	config_reset317	modprobe -r $DEFAULT_KMOD_DRIVER318	config_set_driver $DEFAULT_KMOD_DRIVER319}320 321kmod_defaults_fs()322{323	config_reset324	modprobe -r $DEFAULT_KMOD_FS325	config_set_fs $DEFAULT_KMOD_FS326	config_set_test_case_fs327}328 329kmod_test_0001_driver()330{331	NAME='\000'332 333	kmod_defaults_driver334	config_num_threads 1335	printf $NAME >"$DIR"/config_test_driver336	config_trigger ${FUNCNAME[0]}337	config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND338}339 340kmod_test_0001_fs()341{342	NAME='\000'343 344	kmod_defaults_fs345	config_num_threads 1346	printf $NAME >"$DIR"/config_test_fs347	config_trigger ${FUNCNAME[0]}348	config_expect_result ${FUNCNAME[0]} -EINVAL349}350 351kmod_test_0001()352{353	kmod_test_0001_driver354	kmod_test_0001_fs355}356 357kmod_test_0002_driver()358{359	NAME="nope-$DEFAULT_KMOD_DRIVER"360 361	kmod_defaults_driver362	config_set_driver $NAME363	config_num_threads 1364	config_trigger ${FUNCNAME[0]}365	config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND366}367 368kmod_test_0002_fs()369{370	NAME="nope-$DEFAULT_KMOD_FS"371 372	kmod_defaults_fs373	config_set_fs $NAME374	config_trigger ${FUNCNAME[0]}375	config_expect_result ${FUNCNAME[0]} -EINVAL376}377 378kmod_test_0002()379{380	kmod_test_0002_driver381	kmod_test_0002_fs382}383 384kmod_test_0003()385{386	kmod_defaults_fs387	config_num_threads 1388	config_trigger ${FUNCNAME[0]}389	config_expect_result ${FUNCNAME[0]} SUCCESS390}391 392kmod_test_0004()393{394	kmod_defaults_fs395	config_num_threads 2396	config_trigger ${FUNCNAME[0]}397	config_expect_result ${FUNCNAME[0]} SUCCESS398}399 400kmod_test_0005()401{402	kmod_defaults_driver403	config_trigger ${FUNCNAME[0]}404	config_expect_result ${FUNCNAME[0]} SUCCESS405}406 407kmod_test_0006()408{409	kmod_defaults_fs410	config_trigger ${FUNCNAME[0]}411	config_expect_result ${FUNCNAME[0]} SUCCESS412}413 414kmod_test_0007()415{416	kmod_test_0005417	kmod_test_0006418}419 420kmod_test_0008()421{422	kmod_defaults_driver423	MODPROBE_LIMIT=$(config_get_modprobe_limit)424	let EXTRA=$MODPROBE_LIMIT/6425	config_num_thread_limit_extra $EXTRA426	config_trigger ${FUNCNAME[0]}427	config_expect_result ${FUNCNAME[0]} SUCCESS428}429 430kmod_test_0009()431{432	kmod_defaults_fs433	MODPROBE_LIMIT=$(config_get_modprobe_limit)434	let EXTRA=$MODPROBE_LIMIT/4435	config_num_thread_limit_extra $EXTRA436	config_trigger ${FUNCNAME[0]}437	config_expect_result ${FUNCNAME[0]} SUCCESS438}439 440kmod_test_0010()441{442	kmod_defaults_driver443	config_num_threads 1444	echo "/KMOD_TEST_NONEXISTENT" > /proc/sys/kernel/modprobe445	config_trigger ${FUNCNAME[0]}446	config_expect_result ${FUNCNAME[0]} -ENOENT447	echo "$MODPROBE" > /proc/sys/kernel/modprobe448}449 450kmod_test_0011()451{452	kmod_defaults_driver453	config_num_threads 1454	# This causes the kernel to not even try executing modprobe.  The error455	# code is still -ENOENT like when modprobe doesn't exist, so we can't456	# easily test for the exact difference.  But this still is a useful test457	# since there was a bug where request_module() returned 0 in this case.458	echo > /proc/sys/kernel/modprobe459	config_trigger ${FUNCNAME[0]}460	config_expect_result ${FUNCNAME[0]} -ENOENT461	echo "$MODPROBE" > /proc/sys/kernel/modprobe462}463 464kmod_check_visibility()465{466	local name="$1"467	local cmd="$2"468 469	modprobe $DEFAULT_KMOD_DRIVER470 471	local priv=$(eval $cmd)472	local unpriv=$(capsh --drop=CAP_SYSLOG -- -c "$cmd")473 474	if [ "$priv" = "$unpriv" ] || \475	   [ "${priv:0:3}" = "0x0" ] || \476	   [ "${unpriv:0:3}" != "0x0" ] ; then477		echo "${FUNCNAME[0]}: FAIL, $name visible to unpriv: '$priv' vs '$unpriv'" >&2478		exit 1479	else480		echo "${FUNCNAME[0]}: OK!"481	fi482}483 484kmod_test_0012()485{486	kmod_check_visibility /proc/modules \487		"grep '^${DEFAULT_KMOD_DRIVER}\b' /proc/modules | awk '{print \$NF}'"488}489 490kmod_test_0013()491{492	kmod_check_visibility '/sys/module/*/sections/*' \493		"cat /sys/module/${DEFAULT_KMOD_DRIVER}/sections/.*text | head -n1"494}495 496list_tests()497{498	echo "Test ID list:"499	echo500	echo "TEST_ID x NUM_TEST"501	echo "TEST_ID:   Test ID"502	echo "NUM_TESTS: Number of recommended times to run the test"503	echo504	echo "0001 x $(get_test_count 0001) - Simple test - 1 thread  for empty string"505	echo "0002 x $(get_test_count 0002) - Simple test - 1 thread  for modules/filesystems that do not exist"506	echo "0003 x $(get_test_count 0003) - Simple test - 1 thread  for get_fs_type() only"507	echo "0004 x $(get_test_count 0004) - Simple test - 2 threads for get_fs_type() only"508	echo "0005 x $(get_test_count 0005) - multithreaded tests with default setup - request_module() only"509	echo "0006 x $(get_test_count 0006) - multithreaded tests with default setup - get_fs_type() only"510	echo "0007 x $(get_test_count 0007) - multithreaded tests with default setup test request_module() and get_fs_type()"511	echo "0008 x $(get_test_count 0008) - multithreaded - push kmod_concurrent over max_modprobes for request_module()"512	echo "0009 x $(get_test_count 0009) - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()"513	echo "0010 x $(get_test_count 0010) - test nonexistent modprobe path"514	echo "0011 x $(get_test_count 0011) - test completely disabling module autoloading"515	echo "0012 x $(get_test_count 0012) - test /proc/modules address visibility under CAP_SYSLOG"516	echo "0013 x $(get_test_count 0013) - test /sys/module/*/sections/* visibility under CAP_SYSLOG"517}518 519usage()520{521	NUM_TESTS=$(grep -o ' ' <<<"$ALL_TESTS" | grep -c .)522	let NUM_TESTS=$NUM_TESTS+1523	MAX_TEST=$(printf "%04d\n" $NUM_TESTS)524	echo "Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] |"525	echo "		 [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>"526	echo "           [ all ] [ -h | --help ] [ -l ]"527	echo ""528	echo "Valid tests: 0001-$MAX_TEST"529	echo ""530	echo "    all     Runs all tests (default)"531	echo "    -t      Run test ID the number amount of times is recommended"532	echo "    -w      Watch test ID run until it runs into an error"533	echo "    -s      Run test ID once"534	echo "    -c      Run test ID x test-count number of times"535	echo "    -l      List all test ID list"536	echo " -h|--help  Help"537	echo538	echo "If an error every occurs execution will immediately terminate."539	echo "If you are adding a new test try using -w <test-ID> first to"540	echo "make sure the test passes a series of tests."541	echo542	echo Example uses:543	echo544	echo "${TEST_NAME}.sh		-- executes all tests"545	echo "${TEST_NAME}.sh -t 0008	-- Executes test ID 0008 number of times is recommended"546	echo "${TEST_NAME}.sh -w 0008	-- Watch test ID 0008 run until an error occurs"547	echo "${TEST_NAME}.sh -s 0008	-- Run test ID 0008 once"548	echo "${TEST_NAME}.sh -c 0008 3	-- Run test ID 0008 three times"549	echo550	list_tests551	exit 1552}553 554function test_num()555{556	re='^[0-9]+$'557	if ! [[ $1 =~ $re ]]; then558		usage559	fi560}561 562function get_test_data()563{564	test_num $1565	local field_num=$(echo $1 | sed 's/^0*//')566	echo $ALL_TESTS | awk '{print $'$field_num'}'567}568 569function get_test_count()570{571	TEST_DATA=$(get_test_data $1)572	LAST_TWO=${TEST_DATA#*:*}573	echo ${LAST_TWO%:*}574}575 576function get_test_enabled()577{578	TEST_DATA=$(get_test_data $1)579	echo ${TEST_DATA#*:*:}580}581 582function run_all_tests()583{584	for i in $ALL_TESTS ; do585		TEST_ID=${i%:*:*}586		ENABLED=$(get_test_enabled $TEST_ID)587		TEST_COUNT=$(get_test_count $TEST_ID)588		if [[ $ENABLED -eq "1" ]]; then589			test_case $TEST_ID $TEST_COUNT590		fi591	done592}593 594function watch_log()595{596	if [ $# -ne 3 ]; then597		clear598	fi599	date600	echo "Running test: $2 - run #$1"601}602 603function watch_case()604{605	i=0606	while [ 1 ]; do607 608		if [ $# -eq 1 ]; then609			test_num $1610			watch_log $i ${TEST_NAME}_test_$1611			${TEST_NAME}_test_$1612		else613			watch_log $i all614			run_all_tests615		fi616		let i=$i+1617	done618}619 620function test_case()621{622	NUM_TESTS=$DEFAULT_NUM_TESTS623	if [ $# -eq 2 ]; then624		NUM_TESTS=$2625	fi626 627	i=0628	while [ $i -lt $NUM_TESTS ]; do629		test_num $1630		watch_log $i ${TEST_NAME}_test_$1 noclear631		RUN_TEST=${TEST_NAME}_test_$1632		$RUN_TEST633		let i=$i+1634	done635}636 637function parse_args()638{639	if [ $# -eq 0 ]; then640		run_all_tests641	else642		if [[ "$1" = "all" ]]; then643			run_all_tests644		elif [[ "$1" = "-w" ]]; then645			shift646			watch_case $@647		elif [[ "$1" = "-t" ]]; then648			shift649			test_num $1650			test_case $1 $(get_test_count $1)651		elif [[ "$1" = "-c" ]]; then652			shift653			test_num $1654			test_num $2655			test_case $1 $2656		elif [[ "$1" = "-s" ]]; then657			shift658			test_case $1 1659		elif [[ "$1" = "-l" ]]; then660			list_tests661		elif [[ "$1" = "-h" || "$1" = "--help" ]]; then662			usage663		else664			usage665		fi666	fi667}668 669test_reqs670allow_user_defaults671load_req_mod672 673MODPROBE=$(</proc/sys/kernel/modprobe)674trap "test_finish" EXIT675 676parse_args $@677 678exit 0679