kmod.sh 13 KB

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