kmod.sh 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. #!/bin/bash
  2. #
  3. # Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>
  4. #
  5. # This program is free software; you can redistribute it and/or modify it
  6. # under the terms of the GNU General Public License as published by the Free
  7. # Software Foundation; either version 2 of the License, or at your option any
  8. # later version; or, when distributed separately from the Linux kernel or
  9. # when incorporated into other software packages, subject to the following
  10. # license:
  11. #
  12. # This program is free software; you can redistribute it and/or modify it
  13. # under the terms of copyleft-next (version 0.3.1 or later) as published
  14. # at http://copyleft-next.org/.
  15. # This is a stress test script for kmod, the kernel module loader. It uses
  16. # test_kmod which exposes a series of knobs for the API for us so we can
  17. # tweak each test in userspace rather than in kernelspace.
  18. #
  19. # The way kmod works is it uses the kernel's usermode helper API to eventually
  20. # call /sbin/modprobe. It has a limit of the number of concurrent calls
  21. # possible. The kernel interface to load modules is request_module(), however
  22. # mount uses get_fs_type(). Both behave slightly differently, but the
  23. # differences are important enough to test each call separately. For this
  24. # reason test_kmod starts by providing tests for both calls.
  25. #
  26. # The test driver test_kmod assumes a series of defaults which you can
  27. # override by exporting to your environment prior running this script.
  28. # For instance this script assumes you do not have xfs loaded upon boot.
  29. # If this is false, export DEFAULT_KMOD_FS="ext4" prior to running this
  30. # script if the filesyste module you don't have loaded upon bootup
  31. # is ext4 instead. Refer to allow_user_defaults() for a list of user
  32. # override variables possible.
  33. #
  34. # You'll want at least 4 GiB of RAM to expect to run these tests
  35. # without running out of memory on them. For other requirements refer
  36. # to test_reqs()
  37. set -e
  38. TEST_NAME="kmod"
  39. TEST_DRIVER="test_${TEST_NAME}"
  40. TEST_DIR=$(dirname $0)
  41. # This represents
  42. #
  43. # TEST_ID:TEST_COUNT:ENABLED
  44. #
  45. # TEST_ID: is the test id number
  46. # TEST_COUNT: number of times we should run the test
  47. # ENABLED: 1 if enabled, 0 otherwise
  48. #
  49. # Once these are enabled please leave them as-is. Write your own test,
  50. # we have tons of space.
  51. ALL_TESTS="0001:3:1"
  52. ALL_TESTS="$ALL_TESTS 0002:3:1"
  53. ALL_TESTS="$ALL_TESTS 0003:1:1"
  54. ALL_TESTS="$ALL_TESTS 0004:1:1"
  55. ALL_TESTS="$ALL_TESTS 0005:10:1"
  56. ALL_TESTS="$ALL_TESTS 0006:10:1"
  57. ALL_TESTS="$ALL_TESTS 0007:5:1"
  58. ALL_TESTS="$ALL_TESTS 0008:150:1"
  59. ALL_TESTS="$ALL_TESTS 0009:150:1"
  60. # Kselftest framework requirement - SKIP code is 4.
  61. ksft_skip=4
  62. test_modprobe()
  63. {
  64. if [ ! -d $DIR ]; then
  65. echo "$0: $DIR not present" >&2
  66. echo "You must have the following enabled in your kernel:" >&2
  67. cat $TEST_DIR/config >&2
  68. exit $ksft_skip
  69. fi
  70. }
  71. function allow_user_defaults()
  72. {
  73. if [ -z $DEFAULT_KMOD_DRIVER ]; then
  74. DEFAULT_KMOD_DRIVER="test_module"
  75. fi
  76. if [ -z $DEFAULT_KMOD_FS ]; then
  77. DEFAULT_KMOD_FS="xfs"
  78. fi
  79. if [ -z $PROC_DIR ]; then
  80. PROC_DIR="/proc/sys/kernel/"
  81. fi
  82. if [ -z $MODPROBE_LIMIT ]; then
  83. MODPROBE_LIMIT=50
  84. fi
  85. if [ -z $DIR ]; then
  86. DIR="/sys/devices/virtual/misc/${TEST_DRIVER}0/"
  87. fi
  88. if [ -z $DEFAULT_NUM_TESTS ]; then
  89. DEFAULT_NUM_TESTS=150
  90. fi
  91. MODPROBE_LIMIT_FILE="${PROC_DIR}/kmod-limit"
  92. }
  93. test_reqs()
  94. {
  95. if ! which modprobe 2> /dev/null > /dev/null; then
  96. echo "$0: You need modprobe installed" >&2
  97. exit $ksft_skip
  98. fi
  99. if ! which kmod 2> /dev/null > /dev/null; then
  100. echo "$0: You need kmod installed" >&2
  101. exit $ksft_skip
  102. fi
  103. # kmod 19 has a bad bug where it returns 0 when modprobe
  104. # gets called *even* if the module was not loaded due to
  105. # some bad heuristics. For details see:
  106. #
  107. # A work around is possible in-kernel but its rather
  108. # complex.
  109. KMOD_VERSION=$(kmod --version | awk '{print $3}')
  110. if [[ $KMOD_VERSION -le 19 ]]; then
  111. echo "$0: You need at least kmod 20" >&2
  112. echo "kmod <= 19 is buggy, for details see:" >&2
  113. echo "http://git.kernel.org/cgit/utils/kernel/kmod/kmod.git/commit/libkmod/libkmod-module.c?id=fd44a98ae2eb5eb32161088954ab21e58e19dfc4" >&2
  114. exit $ksft_skip
  115. fi
  116. uid=$(id -u)
  117. if [ $uid -ne 0 ]; then
  118. echo $msg must be run as root >&2
  119. exit $ksft_skip
  120. fi
  121. }
  122. function load_req_mod()
  123. {
  124. trap "test_modprobe" EXIT
  125. if [ ! -d $DIR ]; then
  126. # Alanis: "Oh isn't it ironic?"
  127. modprobe $TEST_DRIVER
  128. fi
  129. }
  130. test_finish()
  131. {
  132. echo "Test completed"
  133. }
  134. errno_name_to_val()
  135. {
  136. case "$1" in
  137. # kmod calls modprobe and upon of a module not found
  138. # modprobe returns just 1... However in the kernel we
  139. # *sometimes* see 256...
  140. MODULE_NOT_FOUND)
  141. echo 256;;
  142. SUCCESS)
  143. echo 0;;
  144. -EPERM)
  145. echo -1;;
  146. -ENOENT)
  147. echo -2;;
  148. -EINVAL)
  149. echo -22;;
  150. -ERR_ANY)
  151. echo -123456;;
  152. *)
  153. echo invalid;;
  154. esac
  155. }
  156. errno_val_to_name()
  157. case "$1" in
  158. 256)
  159. echo MODULE_NOT_FOUND;;
  160. 0)
  161. echo SUCCESS;;
  162. -1)
  163. echo -EPERM;;
  164. -2)
  165. echo -ENOENT;;
  166. -22)
  167. echo -EINVAL;;
  168. -123456)
  169. echo -ERR_ANY;;
  170. *)
  171. echo invalid;;
  172. esac
  173. config_set_test_case_driver()
  174. {
  175. if ! echo -n 1 >$DIR/config_test_case; then
  176. echo "$0: Unable to set to test case to driver" >&2
  177. exit 1
  178. fi
  179. }
  180. config_set_test_case_fs()
  181. {
  182. if ! echo -n 2 >$DIR/config_test_case; then
  183. echo "$0: Unable to set to test case to fs" >&2
  184. exit 1
  185. fi
  186. }
  187. config_num_threads()
  188. {
  189. if ! echo -n $1 >$DIR/config_num_threads; then
  190. echo "$0: Unable to set to number of threads" >&2
  191. exit 1
  192. fi
  193. }
  194. config_get_modprobe_limit()
  195. {
  196. if [[ -f ${MODPROBE_LIMIT_FILE} ]] ; then
  197. MODPROBE_LIMIT=$(cat $MODPROBE_LIMIT_FILE)
  198. fi
  199. echo $MODPROBE_LIMIT
  200. }
  201. config_num_thread_limit_extra()
  202. {
  203. MODPROBE_LIMIT=$(config_get_modprobe_limit)
  204. let EXTRA_LIMIT=$MODPROBE_LIMIT+$1
  205. config_num_threads $EXTRA_LIMIT
  206. }
  207. # For special characters use printf directly,
  208. # refer to kmod_test_0001
  209. config_set_driver()
  210. {
  211. if ! echo -n $1 >$DIR/config_test_driver; then
  212. echo "$0: Unable to set driver" >&2
  213. exit 1
  214. fi
  215. }
  216. config_set_fs()
  217. {
  218. if ! echo -n $1 >$DIR/config_test_fs; then
  219. echo "$0: Unable to set driver" >&2
  220. exit 1
  221. fi
  222. }
  223. config_get_driver()
  224. {
  225. cat $DIR/config_test_driver
  226. }
  227. config_get_test_result()
  228. {
  229. cat $DIR/test_result
  230. }
  231. config_reset()
  232. {
  233. if ! echo -n "1" >"$DIR"/reset; then
  234. echo "$0: reset shuld have worked" >&2
  235. exit 1
  236. fi
  237. }
  238. config_show_config()
  239. {
  240. echo "----------------------------------------------------"
  241. cat "$DIR"/config
  242. echo "----------------------------------------------------"
  243. }
  244. config_trigger()
  245. {
  246. if ! echo -n "1" >"$DIR"/trigger_config 2>/dev/null; then
  247. echo "$1: FAIL - loading should have worked"
  248. config_show_config
  249. exit 1
  250. fi
  251. echo "$1: OK! - loading kmod test"
  252. }
  253. config_trigger_want_fail()
  254. {
  255. if echo "1" > $DIR/trigger_config 2>/dev/null; then
  256. echo "$1: FAIL - test case was expected to fail"
  257. config_show_config
  258. exit 1
  259. fi
  260. echo "$1: OK! - kmod test case failed as expected"
  261. }
  262. config_expect_result()
  263. {
  264. RC=$(config_get_test_result)
  265. RC_NAME=$(errno_val_to_name $RC)
  266. ERRNO_NAME=$2
  267. ERRNO=$(errno_name_to_val $ERRNO_NAME)
  268. if [[ $ERRNO_NAME = "-ERR_ANY" ]]; then
  269. if [[ $RC -ge 0 ]]; then
  270. echo "$1: FAIL, test expects $ERRNO_NAME - got $RC_NAME ($RC)" >&2
  271. config_show_config
  272. exit 1
  273. fi
  274. elif [[ $RC != $ERRNO ]]; then
  275. echo "$1: FAIL, test expects $ERRNO_NAME ($ERRNO) - got $RC_NAME ($RC)" >&2
  276. config_show_config
  277. exit 1
  278. fi
  279. echo "$1: OK! - Return value: $RC ($RC_NAME), expected $ERRNO_NAME"
  280. }
  281. kmod_defaults_driver()
  282. {
  283. config_reset
  284. modprobe -r $DEFAULT_KMOD_DRIVER
  285. config_set_driver $DEFAULT_KMOD_DRIVER
  286. }
  287. kmod_defaults_fs()
  288. {
  289. config_reset
  290. modprobe -r $DEFAULT_KMOD_FS
  291. config_set_fs $DEFAULT_KMOD_FS
  292. config_set_test_case_fs
  293. }
  294. kmod_test_0001_driver()
  295. {
  296. NAME='\000'
  297. kmod_defaults_driver
  298. config_num_threads 1
  299. printf '\000' >"$DIR"/config_test_driver
  300. config_trigger ${FUNCNAME[0]}
  301. config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND
  302. }
  303. kmod_test_0001_fs()
  304. {
  305. NAME='\000'
  306. kmod_defaults_fs
  307. config_num_threads 1
  308. printf '\000' >"$DIR"/config_test_fs
  309. config_trigger ${FUNCNAME[0]}
  310. config_expect_result ${FUNCNAME[0]} -EINVAL
  311. }
  312. kmod_test_0001()
  313. {
  314. kmod_test_0001_driver
  315. kmod_test_0001_fs
  316. }
  317. kmod_test_0002_driver()
  318. {
  319. NAME="nope-$DEFAULT_KMOD_DRIVER"
  320. kmod_defaults_driver
  321. config_set_driver $NAME
  322. config_num_threads 1
  323. config_trigger ${FUNCNAME[0]}
  324. config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND
  325. }
  326. kmod_test_0002_fs()
  327. {
  328. NAME="nope-$DEFAULT_KMOD_FS"
  329. kmod_defaults_fs
  330. config_set_fs $NAME
  331. config_trigger ${FUNCNAME[0]}
  332. config_expect_result ${FUNCNAME[0]} -EINVAL
  333. }
  334. kmod_test_0002()
  335. {
  336. kmod_test_0002_driver
  337. kmod_test_0002_fs
  338. }
  339. kmod_test_0003()
  340. {
  341. kmod_defaults_fs
  342. config_num_threads 1
  343. config_trigger ${FUNCNAME[0]}
  344. config_expect_result ${FUNCNAME[0]} SUCCESS
  345. }
  346. kmod_test_0004()
  347. {
  348. kmod_defaults_fs
  349. config_num_threads 2
  350. config_trigger ${FUNCNAME[0]}
  351. config_expect_result ${FUNCNAME[0]} SUCCESS
  352. }
  353. kmod_test_0005()
  354. {
  355. kmod_defaults_driver
  356. config_trigger ${FUNCNAME[0]}
  357. config_expect_result ${FUNCNAME[0]} SUCCESS
  358. }
  359. kmod_test_0006()
  360. {
  361. kmod_defaults_fs
  362. config_trigger ${FUNCNAME[0]}
  363. config_expect_result ${FUNCNAME[0]} SUCCESS
  364. }
  365. kmod_test_0007()
  366. {
  367. kmod_test_0005
  368. kmod_test_0006
  369. }
  370. kmod_test_0008()
  371. {
  372. kmod_defaults_driver
  373. MODPROBE_LIMIT=$(config_get_modprobe_limit)
  374. let EXTRA=$MODPROBE_LIMIT/6
  375. config_num_thread_limit_extra $EXTRA
  376. config_trigger ${FUNCNAME[0]}
  377. config_expect_result ${FUNCNAME[0]} SUCCESS
  378. }
  379. kmod_test_0009()
  380. {
  381. kmod_defaults_fs
  382. MODPROBE_LIMIT=$(config_get_modprobe_limit)
  383. let EXTRA=$MODPROBE_LIMIT/4
  384. config_num_thread_limit_extra $EXTRA
  385. config_trigger ${FUNCNAME[0]}
  386. config_expect_result ${FUNCNAME[0]} SUCCESS
  387. }
  388. list_tests()
  389. {
  390. echo "Test ID list:"
  391. echo
  392. echo "TEST_ID x NUM_TEST"
  393. echo "TEST_ID: Test ID"
  394. echo "NUM_TESTS: Number of recommended times to run the test"
  395. echo
  396. echo "0001 x $(get_test_count 0001) - Simple test - 1 thread for empty string"
  397. echo "0002 x $(get_test_count 0002) - Simple test - 1 thread for modules/filesystems that do not exist"
  398. echo "0003 x $(get_test_count 0003) - Simple test - 1 thread for get_fs_type() only"
  399. echo "0004 x $(get_test_count 0004) - Simple test - 2 threads for get_fs_type() only"
  400. echo "0005 x $(get_test_count 0005) - multithreaded tests with default setup - request_module() only"
  401. echo "0006 x $(get_test_count 0006) - multithreaded tests with default setup - get_fs_type() only"
  402. echo "0007 x $(get_test_count 0007) - multithreaded tests with default setup test request_module() and get_fs_type()"
  403. echo "0008 x $(get_test_count 0008) - multithreaded - push kmod_concurrent over max_modprobes for request_module()"
  404. echo "0009 x $(get_test_count 0009) - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()"
  405. }
  406. usage()
  407. {
  408. NUM_TESTS=$(grep -o ' ' <<<"$ALL_TESTS" | grep -c .)
  409. let NUM_TESTS=$NUM_TESTS+1
  410. MAX_TEST=$(printf "%04d\n" $NUM_TESTS)
  411. echo "Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] |"
  412. echo " [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>"
  413. echo " [ all ] [ -h | --help ] [ -l ]"
  414. echo ""
  415. echo "Valid tests: 0001-$MAX_TEST"
  416. echo ""
  417. echo " all Runs all tests (default)"
  418. echo " -t Run test ID the number amount of times is recommended"
  419. echo " -w Watch test ID run until it runs into an error"
  420. echo " -s Run test ID once"
  421. echo " -c Run test ID x test-count number of times"
  422. echo " -l List all test ID list"
  423. echo " -h|--help Help"
  424. echo
  425. echo "If an error every occurs execution will immediately terminate."
  426. echo "If you are adding a new test try using -w <test-ID> first to"
  427. echo "make sure the test passes a series of tests."
  428. echo
  429. echo Example uses:
  430. echo
  431. echo "${TEST_NAME}.sh -- executes all tests"
  432. echo "${TEST_NAME}.sh -t 0008 -- Executes test ID 0008 number of times is recomended"
  433. echo "${TEST_NAME}.sh -w 0008 -- Watch test ID 0008 run until an error occurs"
  434. echo "${TEST_NAME}.sh -s 0008 -- Run test ID 0008 once"
  435. echo "${TEST_NAME}.sh -c 0008 3 -- Run test ID 0008 three times"
  436. echo
  437. list_tests
  438. exit 1
  439. }
  440. function test_num()
  441. {
  442. re='^[0-9]+$'
  443. if ! [[ $1 =~ $re ]]; then
  444. usage
  445. fi
  446. }
  447. function get_test_data()
  448. {
  449. test_num $1
  450. local field_num=$(echo $1 | sed 's/^0*//')
  451. echo $ALL_TESTS | awk '{print $'$field_num'}'
  452. }
  453. function get_test_count()
  454. {
  455. TEST_DATA=$(get_test_data $1)
  456. LAST_TWO=${TEST_DATA#*:*}
  457. echo ${LAST_TWO%:*}
  458. }
  459. function get_test_enabled()
  460. {
  461. TEST_DATA=$(get_test_data $1)
  462. echo ${TEST_DATA#*:*:}
  463. }
  464. function run_all_tests()
  465. {
  466. for i in $ALL_TESTS ; do
  467. TEST_ID=${i%:*:*}
  468. ENABLED=$(get_test_enabled $TEST_ID)
  469. TEST_COUNT=$(get_test_count $TEST_ID)
  470. if [[ $ENABLED -eq "1" ]]; then
  471. test_case $TEST_ID $TEST_COUNT
  472. fi
  473. done
  474. }
  475. function watch_log()
  476. {
  477. if [ $# -ne 3 ]; then
  478. clear
  479. fi
  480. date
  481. echo "Running test: $2 - run #$1"
  482. }
  483. function watch_case()
  484. {
  485. i=0
  486. while [ 1 ]; do
  487. if [ $# -eq 1 ]; then
  488. test_num $1
  489. watch_log $i ${TEST_NAME}_test_$1
  490. ${TEST_NAME}_test_$1
  491. else
  492. watch_log $i all
  493. run_all_tests
  494. fi
  495. let i=$i+1
  496. done
  497. }
  498. function test_case()
  499. {
  500. NUM_TESTS=$DEFAULT_NUM_TESTS
  501. if [ $# -eq 2 ]; then
  502. NUM_TESTS=$2
  503. fi
  504. i=0
  505. while [ $i -lt $NUM_TESTS ]; do
  506. test_num $1
  507. watch_log $i ${TEST_NAME}_test_$1 noclear
  508. RUN_TEST=${TEST_NAME}_test_$1
  509. $RUN_TEST
  510. let i=$i+1
  511. done
  512. }
  513. function parse_args()
  514. {
  515. if [ $# -eq 0 ]; then
  516. run_all_tests
  517. else
  518. if [[ "$1" = "all" ]]; then
  519. run_all_tests
  520. elif [[ "$1" = "-w" ]]; then
  521. shift
  522. watch_case $@
  523. elif [[ "$1" = "-t" ]]; then
  524. shift
  525. test_num $1
  526. test_case $1 $(get_test_count $1)
  527. elif [[ "$1" = "-c" ]]; then
  528. shift
  529. test_num $1
  530. test_num $2
  531. test_case $1 $2
  532. elif [[ "$1" = "-s" ]]; then
  533. shift
  534. test_case $1 1
  535. elif [[ "$1" = "-l" ]]; then
  536. list_tests
  537. elif [[ "$1" = "-h" || "$1" = "--help" ]]; then
  538. usage
  539. else
  540. usage
  541. fi
  542. fi
  543. }
  544. test_reqs
  545. allow_user_defaults
  546. load_req_mod
  547. trap "test_finish" EXIT
  548. parse_args $@
  549. exit 0