1 #!/bin/bash 2 # SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 3 # Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org> 4 # 5 # This is a stress test script for kmod, the kernel module loader. It uses 6 # test_kmod which exposes a series of knobs for the API for us so we can 7 # 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 eventually 10 # call /sbin/modprobe. It has a limit of the number of concurrent calls 11 # possible. The kernel interface to load modules is request_module(), however 12 # mount uses get_fs_type(). Both behave slightly differently, but the 13 # differences are important enough to test each call separately. For this 14 # reason test_kmod starts by providing tests for both calls. 15 # 16 # The test driver test_kmod assumes a series of defaults which you can 17 # 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 this 20 # script if the filesystem module you don't have loaded upon bootup 21 # is ext4 instead. Refer to allow_user_defaults() for a list of user 22 # override variables possible. 23 # 24 # You'll want at least 4 GiB of RAM to expect to run these tests 25 # without running out of memory on them. For other requirements refer 26 # to test_reqs() 27 28 set -e 29 30 TEST_NAME="kmod" 31 TEST_DRIVER="test_${TEST_NAME}" 32 TEST_DIR=$(dirname $0) 33 34 # This represents 35 # 36 # TEST_ID:TEST_COUNT:ENABLED 37 # 38 # TEST_ID: is the test id number 39 # TEST_COUNT: number of times we should run the test 40 # ENABLED: 1 if enabled, 0 otherwise 41 # 42 # Once these are enabled please leave them as-is. Write your own test, 43 # we have tons of space. 44 ALL_TESTS="0001:3:1" 45 ALL_TESTS="$ALL_TESTS 0002:3:1" 46 ALL_TESTS="$ALL_TESTS 0003:1:1" 47 ALL_TESTS="$ALL_TESTS 0004:1:1" 48 ALL_TESTS="$ALL_TESTS 0005:10:1" 49 ALL_TESTS="$ALL_TESTS 0006:10:1" 50 ALL_TESTS="$ALL_TESTS 0007:5:1" 51 ALL_TESTS="$ALL_TESTS 0008:150:1" 52 ALL_TESTS="$ALL_TESTS 0009:150:1" 53 ALL_TESTS="$ALL_TESTS 0010:1:1" 54 ALL_TESTS="$ALL_TESTS 0011:1:1" 55 ALL_TESTS="$ALL_TESTS 0012:1:1" 56 ALL_TESTS="$ALL_TESTS 0013:1:1" 57 58 # Kselftest framework requirement - SKIP code is 4. 59 ksft_skip=4 60 61 test_modprobe() 62 { 63 if [ ! -d $DIR ]; then 64 echo "$0: $DIR not present" >&2 65 echo "You must have the following enabled in your kernel:" >&2 66 cat $TEST_DIR/config >&2 67 exit $ksft_skip 68 fi 69 } 70 71 function allow_user_defaults() 72 { 73 if [ -z $DEFAULT_KMOD_DRIVER ]; then 74 DEFAULT_KMOD_DRIVER="test_module" 75 fi 76 77 if [ -z $DEFAULT_KMOD_FS ]; then 78 DEFAULT_KMOD_FS="xfs" 79 fi 80 81 if [ -z $PROC_DIR ]; then 82 PROC_DIR="/proc/sys/kernel/" 83 fi 84 85 if [ -z $MODPROBE_LIMIT ]; then 86 MODPROBE_LIMIT=50 87 fi 88 89 if [ -z $DIR ]; then 90 DIR="/sys/devices/virtual/misc/${TEST_DRIVER}0/" 91 fi 92 93 if [ -z $DEFAULT_NUM_TESTS ]; then 94 DEFAULT_NUM_TESTS=150 95 fi 96 97 MODPROBE_LIMIT_FILE="${PROC_DIR}/kmod-limit" 98 } 99 100 test_reqs() 101 { 102 if ! which modprobe 2> /dev/null > /dev/null; then 103 echo "$0: You need modprobe installed" >&2 104 exit $ksft_skip 105 fi 106 107 if ! which kmod 2> /dev/null > /dev/null; then 108 echo "$0: You need kmod installed" >&2 109 exit $ksft_skip 110 fi 111 112 # kmod 19 has a bad bug where it returns 0 when modprobe 113 # gets called *even* if the module was not loaded due to 114 # some bad heuristics. For details see: 115 # 116 # A work around is possible in-kernel but its rather 117 # complex. 118 KMOD_VERSION=$(kmod --version | awk '{print $3}') 119 if [[ $KMOD_VERSION -le 19 ]]; then 120 echo "$0: You need at least kmod 20" >&2 121 echo "kmod <= 19 is buggy, for details see:" >&2 122 echo "https://git.kernel.org/cgit/utils/kernel/kmod/kmod.git/commit/libkmod/libkmod-module.c?id=fd44a98ae2eb5eb32161088954ab21e58e19dfc4" >&2 123 exit $ksft_skip 124 fi 125 126 uid=$(id -u) 127 if [ $uid -ne 0 ]; then 128 echo $msg must be run as root >&2 129 exit $ksft_skip 130 fi 131 } 132 133 function load_req_mod() 134 { 135 trap "test_modprobe" EXIT 136 137 if [ ! -d $DIR ]; then 138 # Alanis: "Oh isn't it ironic?" 139 modprobe $TEST_DRIVER 140 fi 141 } 142 143 test_finish() 144 { 145 echo "$MODPROBE" > /proc/sys/kernel/modprobe 146 echo "Test completed" 147 } 148 149 errno_name_to_val() 150 { 151 case "$1" in 152 # kmod calls modprobe and upon of a module not found 153 # modprobe returns just 1... However in the kernel we 154 # *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 esac 170 } 171 172 errno_val_to_name() 173 case "$1" in 174 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 esac 189 190 config_set_test_case_driver() 191 { 192 if ! echo -n 1 >$DIR/config_test_case; then 193 echo "$0: Unable to set to test case to driver" >&2 194 exit 1 195 fi 196 } 197 198 config_set_test_case_fs() 199 { 200 if ! echo -n 2 >$DIR/config_test_case; then 201 echo "$0: Unable to set to test case to fs" >&2 202 exit 1 203 fi 204 } 205 206 config_num_threads() 207 { 208 if ! echo -n $1 >$DIR/config_num_threads; then 209 echo "$0: Unable to set to number of threads" >&2 210 exit 1 211 fi 212 } 213 214 config_get_modprobe_limit() 215 { 216 if [[ -f ${MODPROBE_LIMIT_FILE} ]] ; then 217 MODPROBE_LIMIT=$(cat $MODPROBE_LIMIT_FILE) 218 fi 219 echo $MODPROBE_LIMIT 220 } 221 222 config_num_thread_limit_extra() 223 { 224 MODPROBE_LIMIT=$(config_get_modprobe_limit) 225 let EXTRA_LIMIT=$MODPROBE_LIMIT+$1 226 config_num_threads $EXTRA_LIMIT 227 } 228 229 # For special characters use printf directly, 230 # refer to kmod_test_0001 231 config_set_driver() 232 { 233 if ! echo -n $1 >$DIR/config_test_driver; then 234 echo "$0: Unable to set driver" >&2 235 exit 1 236 fi 237 } 238 239 config_set_fs() 240 { 241 if ! echo -n $1 >$DIR/config_test_fs; then 242 echo "$0: Unable to set driver" >&2 243 exit 1 244 fi 245 } 246 247 config_get_driver() 248 { 249 cat $DIR/config_test_driver 250 } 251 252 config_get_test_result() 253 { 254 cat $DIR/test_result 255 } 256 257 config_reset() 258 { 259 if ! echo -n "1" >"$DIR"/reset; then 260 echo "$0: reset should have worked" >&2 261 exit 1 262 fi 263 } 264 265 config_show_config() 266 { 267 echo "----------------------------------------------------" 268 cat "$DIR"/config 269 echo "----------------------------------------------------" 270 } 271 272 config_trigger() 273 { 274 if ! echo -n "1" >"$DIR"/trigger_config 2>/dev/null; then 275 echo "$1: FAIL - loading should have worked" 276 config_show_config 277 exit 1 278 fi 279 echo "$1: OK! - loading kmod test" 280 } 281 282 config_trigger_want_fail() 283 { 284 if echo "1" > $DIR/trigger_config 2>/dev/null; then 285 echo "$1: FAIL - test case was expected to fail" 286 config_show_config 287 exit 1 288 fi 289 echo "$1: OK! - kmod test case failed as expected" 290 } 291 292 config_expect_result() 293 { 294 RC=$(config_get_test_result) 295 RC_NAME=$(errno_val_to_name $RC) 296 297 ERRNO_NAME=$2 298 ERRNO=$(errno_name_to_val $ERRNO_NAME) 299 300 if [[ $ERRNO_NAME = "-ERR_ANY" ]]; then 301 if [[ $RC -ge 0 ]]; then 302 echo "$1: FAIL, test expects $ERRNO_NAME - got $RC_NAME ($RC)" >&2 303 config_show_config 304 exit 1 305 fi 306 elif [[ $RC != $ERRNO ]]; then 307 echo "$1: FAIL, test expects $ERRNO_NAME ($ERRNO) - got $RC_NAME ($RC)" >&2 308 config_show_config 309 exit 1 310 fi 311 echo "$1: OK! - Return value: $RC ($RC_NAME), expected $ERRNO_NAME" 312 } 313 314 kmod_defaults_driver() 315 { 316 config_reset 317 modprobe -r $DEFAULT_KMOD_DRIVER 318 config_set_driver $DEFAULT_KMOD_DRIVER 319 } 320 321 kmod_defaults_fs() 322 { 323 config_reset 324 modprobe -r $DEFAULT_KMOD_FS 325 config_set_fs $DEFAULT_KMOD_FS 326 config_set_test_case_fs 327 } 328 329 kmod_test_0001_driver() 330 { 331 NAME='\000' 332 333 kmod_defaults_driver 334 config_num_threads 1 335 printf $NAME >"$DIR"/config_test_driver 336 config_trigger ${FUNCNAME[0]} 337 config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND 338 } 339 340 kmod_test_0001_fs() 341 { 342 NAME='\000' 343 344 kmod_defaults_fs 345 config_num_threads 1 346 printf $NAME >"$DIR"/config_test_fs 347 config_trigger ${FUNCNAME[0]} 348 config_expect_result ${FUNCNAME[0]} -EINVAL 349 } 350 351 kmod_test_0001() 352 { 353 kmod_test_0001_driver 354 kmod_test_0001_fs 355 } 356 357 kmod_test_0002_driver() 358 { 359 NAME="nope-$DEFAULT_KMOD_DRIVER" 360 361 kmod_defaults_driver 362 config_set_driver $NAME 363 config_num_threads 1 364 config_trigger ${FUNCNAME[0]} 365 config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND 366 } 367 368 kmod_test_0002_fs() 369 { 370 NAME="nope-$DEFAULT_KMOD_FS" 371 372 kmod_defaults_fs 373 config_set_fs $NAME 374 config_trigger ${FUNCNAME[0]} 375 config_expect_result ${FUNCNAME[0]} -EINVAL 376 } 377 378 kmod_test_0002() 379 { 380 kmod_test_0002_driver 381 kmod_test_0002_fs 382 } 383 384 kmod_test_0003() 385 { 386 kmod_defaults_fs 387 config_num_threads 1 388 config_trigger ${FUNCNAME[0]} 389 config_expect_result ${FUNCNAME[0]} SUCCESS 390 } 391 392 kmod_test_0004() 393 { 394 kmod_defaults_fs 395 config_num_threads 2 396 config_trigger ${FUNCNAME[0]} 397 config_expect_result ${FUNCNAME[0]} SUCCESS 398 } 399 400 kmod_test_0005() 401 { 402 kmod_defaults_driver 403 config_trigger ${FUNCNAME[0]} 404 config_expect_result ${FUNCNAME[0]} SUCCESS 405 } 406 407 kmod_test_0006() 408 { 409 kmod_defaults_fs 410 config_trigger ${FUNCNAME[0]} 411 config_expect_result ${FUNCNAME[0]} SUCCESS 412 } 413 414 kmod_test_0007() 415 { 416 kmod_test_0005 417 kmod_test_0006 418 } 419 420 kmod_test_0008() 421 { 422 kmod_defaults_driver 423 MODPROBE_LIMIT=$(config_get_modprobe_limit) 424 let EXTRA=$MODPROBE_LIMIT/6 425 config_num_thread_limit_extra $EXTRA 426 config_trigger ${FUNCNAME[0]} 427 config_expect_result ${FUNCNAME[0]} SUCCESS 428 } 429 430 kmod_test_0009() 431 { 432 kmod_defaults_fs 433 MODPROBE_LIMIT=$(config_get_modprobe_limit) 434 let EXTRA=$MODPROBE_LIMIT/4 435 config_num_thread_limit_extra $EXTRA 436 config_trigger ${FUNCNAME[0]} 437 config_expect_result ${FUNCNAME[0]} SUCCESS 438 } 439 440 kmod_test_0010() 441 { 442 kmod_defaults_driver 443 config_num_threads 1 444 echo "/KMOD_TEST_NONEXISTENT" > /proc/sys/kernel/modprobe 445 config_trigger ${FUNCNAME[0]} 446 config_expect_result ${FUNCNAME[0]} -ENOENT 447 echo "$MODPROBE" > /proc/sys/kernel/modprobe 448 } 449 450 kmod_test_0011() 451 { 452 kmod_defaults_driver 453 config_num_threads 1 454 # This causes the kernel to not even try executing modprobe. The error 455 # code is still -ENOENT like when modprobe doesn't exist, so we can't 456 # easily test for the exact difference. But this still is a useful test 457 # since there was a bug where request_module() returned 0 in this case. 458 echo > /proc/sys/kernel/modprobe 459 config_trigger ${FUNCNAME[0]} 460 config_expect_result ${FUNCNAME[0]} -ENOENT 461 echo "$MODPROBE" > /proc/sys/kernel/modprobe 462 } 463 464 kmod_check_visibility() 465 { 466 local name="$1" 467 local cmd="$2" 468 469 modprobe $DEFAULT_KMOD_DRIVER 470 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" ] ; then 477 echo "${FUNCNAME[0]}: FAIL, $name visible to unpriv: '$priv' vs '$unpriv'" >&2 478 exit 1 479 else 480 echo "${FUNCNAME[0]}: OK!" 481 fi 482 } 483 484 kmod_test_0012() 485 { 486 kmod_check_visibility /proc/modules \ 487 "grep '^${DEFAULT_KMOD_DRIVER}\b' /proc/modules | awk '{print \$NF}'" 488 } 489 490 kmod_test_0013() 491 { 492 kmod_check_visibility '/sys/module/*/sections/*' \ 493 "cat /sys/module/${DEFAULT_KMOD_DRIVER}/sections/.*text | head -n1" 494 } 495 496 list_tests() 497 { 498 echo "Test ID list:" 499 echo 500 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 echo 504 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 519 usage() 520 { 521 NUM_TESTS=$(grep -o ' ' <<<"$ALL_TESTS" | grep -c .) 522 let NUM_TESTS=$NUM_TESTS+1 523 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 echo 538 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 echo 542 echo Example uses: 543 echo 544 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 echo 550 list_tests 551 exit 1 552 } 553 554 function test_num() 555 { 556 re='^[0-9]+$' 557 if ! [[ $1 =~ $re ]]; then 558 usage 559 fi 560 } 561 562 function get_test_data() 563 { 564 test_num $1 565 local field_num=$(echo $1 | sed 's/^0*//') 566 echo $ALL_TESTS | awk '{print $'$field_num'}' 567 } 568 569 function get_test_count() 570 { 571 TEST_DATA=$(get_test_data $1) 572 LAST_TWO=${TEST_DATA#*:*} 573 echo ${LAST_TWO%:*} 574 } 575 576 function get_test_enabled() 577 { 578 TEST_DATA=$(get_test_data $1) 579 echo ${TEST_DATA#*:*:} 580 } 581 582 function run_all_tests() 583 { 584 for i in $ALL_TESTS ; do 585 TEST_ID=${i%:*:*} 586 ENABLED=$(get_test_enabled $TEST_ID) 587 TEST_COUNT=$(get_test_count $TEST_ID) 588 if [[ $ENABLED -eq "1" ]]; then 589 test_case $TEST_ID $TEST_COUNT 590 fi 591 done 592 } 593 594 function watch_log() 595 { 596 if [ $# -ne 3 ]; then 597 clear 598 fi 599 date 600 echo "Running test: $2 - run #$1" 601 } 602 603 function watch_case() 604 { 605 i=0 606 while [ 1 ]; do 607 608 if [ $# -eq 1 ]; then 609 test_num $1 610 watch_log $i ${TEST_NAME}_test_$1 611 ${TEST_NAME}_test_$1 612 else 613 watch_log $i all 614 run_all_tests 615 fi 616 let i=$i+1 617 done 618 } 619 620 function test_case() 621 { 622 NUM_TESTS=$DEFAULT_NUM_TESTS 623 if [ $# -eq 2 ]; then 624 NUM_TESTS=$2 625 fi 626 627 i=0 628 while [ $i -lt $NUM_TESTS ]; do 629 test_num $1 630 watch_log $i ${TEST_NAME}_test_$1 noclear 631 RUN_TEST=${TEST_NAME}_test_$1 632 $RUN_TEST 633 let i=$i+1 634 done 635 } 636 637 function parse_args() 638 { 639 if [ $# -eq 0 ]; then 640 run_all_tests 641 else 642 if [[ "$1" = "all" ]]; then 643 run_all_tests 644 elif [[ "$1" = "-w" ]]; then 645 shift 646 watch_case $@ 647 elif [[ "$1" = "-t" ]]; then 648 shift 649 test_num $1 650 test_case $1 $(get_test_count $1) 651 elif [[ "$1" = "-c" ]]; then 652 shift 653 test_num $1 654 test_num $2 655 test_case $1 $2 656 elif [[ "$1" = "-s" ]]; then 657 shift 658 test_case $1 1 659 elif [[ "$1" = "-l" ]]; then 660 list_tests 661 elif [[ "$1" = "-h" || "$1" = "--help" ]]; then 662 usage 663 else 664 usage 665 fi 666 fi 667 } 668 669 test_reqs 670 allow_user_defaults 671 load_req_mod 672 673 MODPROBE=$(</proc/sys/kernel/modprobe) 674 trap "test_finish" EXIT 675 676 parse_args $@ 677 678 exit 0
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.