Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/kmod/kmod.sh
26302 views
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 <[email protected]>
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
679
680