Skip to content

Commit 84b4a51

Browse files
committed
selftests: add new kallsyms selftests
We lack find_symbol() selftests, so add one. This let's us stress test improvements easily on find_symbol() or optimizations. It also inherently allows us to test the limits of kallsyms on Linux today. We test a pathalogical use case for kallsyms by introducing modules which are automatically written for us with a larger number of symbols. We have 4 kallsyms test modules: A: has KALLSYSMS_NUMSYMS exported symbols B: uses one of A's symbols C: adds KALLSYMS_SCALE_FACTOR * KALLSYSMS_NUMSYMS exported D: adds 2 * the symbols than C By using anything much larger than KALLSYSMS_NUMSYMS as 10,000 and KALLSYMS_SCALE_FACTOR of 8 we segfault today. So we're capped at around 160000 symbols somehow today. We can inpsect that issue at our leasure later, but for now the real value to this test is that this will easily allow us to test improvements on find_symbol(). We want to enable this test on allyesmodconfig builds so we can't use this combination, so instead just use a safe value for now and be informative on the Kconfig symbol documentation about where our thresholds are for testers. We default then to KALLSYSMS_NUMSYMS of just 100 and KALLSYMS_SCALE_FACTOR of 8. On x86_64 we can use perf, for other architectures we just use 'time' and allow for customizations. For example a future enhancements could be done for parisc to check for unaligned accesses which triggers a special special exception handler assembler code inside the kernel. The negative impact on performance is so large on parisc that it keeps track of its accesses on /proc/cpuinfo as UAH: IRQ: CPU0 CPU1 3: 1332 0 SuperIO ttyS0 7: 1270013 0 SuperIO pata_ns87415 64: 320023012 320021431 CPU timer 65: 17080507 20624423 CPU IPI UAH: 10948640 58104 Unaligned access handler traps While at it, this tidies up lib/ test modules to allow us to have a new directory for them. The amount of test modules under lib/ is insane. This should also hopefully showcase how to start doing basic self module writing code, which may be more useful for more complex cases later in the future. Signed-off-by: Luis Chamberlain <[email protected]>
1 parent 2295cf8 commit 84b4a51

File tree

9 files changed

+350
-0
lines changed

9 files changed

+350
-0
lines changed

lib/Kconfig.debug

+105
Original file line numberDiff line numberDiff line change
@@ -2903,6 +2903,111 @@ config TEST_KMOD
29032903

29042904
If unsure, say N.
29052905

2906+
config TEST_RUNTIME
2907+
bool
2908+
2909+
config TEST_RUNTIME_MODULE
2910+
bool
2911+
2912+
config TEST_KALLSYMS
2913+
tristate "module kallsyms find_symbol() test"
2914+
depends on m
2915+
select TEST_RUNTIME
2916+
select TEST_RUNTIME_MODULE
2917+
select TEST_KALLSYMS_A
2918+
select TEST_KALLSYMS_B
2919+
select TEST_KALLSYMS_C
2920+
select TEST_KALLSYMS_D
2921+
help
2922+
This allows us to stress test find_symbol() through the kallsyms
2923+
used to place symbols on the kernel ELF kallsyms and modules kallsyms
2924+
where we place kernel symbols such as exported symbols.
2925+
2926+
We have four test modules:
2927+
2928+
A: has KALLSYSMS_NUMSYMS exported symbols
2929+
B: uses one of A's symbols
2930+
C: adds KALLSYMS_SCALE_FACTOR * KALLSYSMS_NUMSYMS exported
2931+
D: adds 2 * the symbols than C
2932+
2933+
We stress test find_symbol() through two means:
2934+
2935+
1) Upon load of B it will trigger simplify_symbols() to look for the
2936+
one symbol it uses from the module A with tons of symbols. This is an
2937+
indirect way for us to have B call resolve_symbol_wait() upon module
2938+
load. This will eventually call find_symbol() which will eventually
2939+
try to find the symbols used with find_exported_symbol_in_section().
2940+
find_exported_symbol_in_section() uses bsearch() so a binary search
2941+
for each symbol. Binary search will at worst be O(log(n)) so the
2942+
larger TEST_MODULE_KALLSYSMS the worse the search.
2943+
2944+
2) The selftests should load C first, before B. Upon B's load towards
2945+
the end right before we call module B's init routine we get
2946+
complete_formation() called on the module. That will first check
2947+
for duplicate symbols with the call to verify_exported_symbols().
2948+
That is when we'll force iteration on module C's insane symbol list.
2949+
Since it has 10 * KALLSYMS_NUMSYMS it means we can first test
2950+
just loading B without C. The amount of time it takes to load C Vs
2951+
B can give us an idea of the impact growth of the symbol space and
2952+
give us projection. Module A only uses one symbol from B so to allow
2953+
this scaling in module C to be proportional, if it used more symbols
2954+
then the first test would be doing more and increasing just the
2955+
search space would be slightly different. The last module, module D
2956+
will just increase the search space by twice the number of symbols in
2957+
C so to allow for full projects.
2958+
2959+
tools/testing/selftests/module/find_symbol.sh
2960+
2961+
The current defaults will incur a build delay of about 7 minutes
2962+
on an x86_64 with only 8 cores. Enable this only if you want to
2963+
stress test find_symbol() with thousands of symbols. At the same
2964+
time this is also useful to test building modules with thousands of
2965+
symbols, and if BTF is enabled this also stress tests adding BTF
2966+
information for each module. Currently enabling many more symbols
2967+
will segfault the build system.
2968+
2969+
If unsure, say N.
2970+
2971+
if TEST_KALLSYMS
2972+
2973+
config TEST_KALLSYMS_A
2974+
tristate
2975+
depends on m
2976+
2977+
config TEST_KALLSYMS_B
2978+
tristate
2979+
depends on m
2980+
2981+
config TEST_KALLSYMS_C
2982+
tristate
2983+
depends on m
2984+
2985+
config TEST_KALLSYMS_D
2986+
tristate
2987+
depends on m
2988+
2989+
config TEST_KALLSYMS_NUMSYMS
2990+
int "test kallsyms number of symbols"
2991+
default 100
2992+
help
2993+
The number of symbols to create on TEST_KALLSYMS_A, only one of which
2994+
module TEST_KALLSYMS_B will use. This also will be used
2995+
for how many symbols TEST_KALLSYMS_C will have, scaled up by
2996+
TEST_KALLSYMS_SCALE_FACTOR. Note that setting this to 10,000 will
2997+
trigger a segfault today, don't use anything close to it unless
2998+
you are aware that this should not be used for automated build tests.
2999+
3000+
config TEST_KALLSYMS_SCALE_FACTOR
3001+
int "test kallsyms scale factor"
3002+
default 8
3003+
help
3004+
How many more unusued symbols will TEST_KALLSYSMS_C have than
3005+
TEST_KALLSYMS_A. If 8, then module C will have 8 * syms
3006+
than module A. Then TEST_KALLSYMS_D will have double the amount
3007+
of symbols than C so to allow projections.
3008+
3009+
endif # TEST_KALLSYMS
3010+
29063011
config TEST_DEBUG_VIRTUAL
29073012
tristate "Test CONFIG_DEBUG_VIRTUAL feature"
29083013
depends on DEBUG_VIRTUAL

lib/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ obj-$(CONFIG_TEST_XARRAY) += test_xarray.o
9696
obj-$(CONFIG_TEST_MAPLE_TREE) += test_maple_tree.o
9797
obj-$(CONFIG_TEST_PARMAN) += test_parman.o
9898
obj-$(CONFIG_TEST_KMOD) += test_kmod.o
99+
obj-$(CONFIG_TEST_RUNTIME) += tests/
99100
obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o
100101
obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o
101102
obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o

lib/tests/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
obj-$(CONFIG_TEST_RUNTIME_MODULE) += module/

lib/tests/module/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
test_kallsyms_a.c
2+
test_kallsyms_b.c
3+
test_kallsyms_c.c
4+
test_kallsyms_d.c

lib/tests/module/Makefile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
obj-$(CONFIG_TEST_KALLSYMS_A) += test_kallsyms_a.o
2+
obj-$(CONFIG_TEST_KALLSYMS_B) += test_kallsyms_b.o
3+
obj-$(CONFIG_TEST_KALLSYMS_C) += test_kallsyms_c.o
4+
obj-$(CONFIG_TEST_KALLSYMS_D) += test_kallsyms_d.o
5+
6+
$(obj)/%.c: FORCE
7+
@$(kecho) " GEN $@"
8+
$(Q)$(srctree)/lib/tests/module/gen_test_kallsyms.sh $@\
9+
$(CONFIG_TEST_KALLSYMS_NUMSYMS) \
10+
$(CONFIG_TEST_KALLSYMS_SCALE_FACTOR)
11+
12+
clean-files += test_kallsyms_a.c
13+
clean-files += test_kallsyms_b.c
14+
clean-files += test_kallsyms_c.c
15+
clean-files += test_kallsyms_d.c

lib/tests/module/gen_test_kallsyms.sh

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/bin/bash
2+
3+
TARGET=$(basename $1)
4+
DIR=lib/tests/module
5+
TARGET="$DIR/$TARGET"
6+
NUM_SYMS=$2
7+
SCALE_FACTOR=$3
8+
TEST_TYPE=$(echo $TARGET | sed -e 's|lib/tests/module/test_kallsyms_||g')
9+
TEST_TYPE=$(echo $TEST_TYPE | sed -e 's|.c||g')
10+
11+
gen_template_module_header()
12+
{
13+
cat <<____END_MODULE
14+
// SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1
15+
/*
16+
* Copyright (C) 2023 Luis Chamberlain <[email protected]>
17+
*
18+
* Automatically generated code for testing, do not edit manually.
19+
*/
20+
21+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22+
23+
#include <linux/init.h>
24+
#include <linux/module.h>
25+
#include <linux/printk.h>
26+
27+
____END_MODULE
28+
}
29+
30+
gen_num_syms()
31+
{
32+
PREFIX=$1
33+
NUM=$2
34+
for i in $(seq 1 $NUM); do
35+
printf "int auto_test_%s_%010d = 0xff;\n" $PREFIX $i
36+
printf "EXPORT_SYMBOL_GPL(auto_test_%s_%010d);\n" $PREFIX $i
37+
done
38+
echo
39+
}
40+
41+
gen_template_module_data_a()
42+
{
43+
gen_num_syms a $1
44+
cat <<____END_MODULE
45+
static int auto_runtime_test(void)
46+
{
47+
return 0;
48+
}
49+
50+
____END_MODULE
51+
}
52+
53+
gen_template_module_data_b()
54+
{
55+
printf "\nextern int auto_test_a_%010d;\n\n" 28
56+
echo "static int auto_runtime_test(void)"
57+
echo "{"
58+
printf "\nreturn auto_test_a_%010d;\n" 28
59+
echo "}"
60+
}
61+
62+
gen_template_module_data_c()
63+
{
64+
gen_num_syms c $1
65+
cat <<____END_MODULE
66+
static int auto_runtime_test(void)
67+
{
68+
return 0;
69+
}
70+
71+
____END_MODULE
72+
}
73+
74+
gen_template_module_data_d()
75+
{
76+
gen_num_syms d $1
77+
cat <<____END_MODULE
78+
static int auto_runtime_test(void)
79+
{
80+
return 0;
81+
}
82+
83+
____END_MODULE
84+
}
85+
86+
gen_template_module_exit()
87+
{
88+
cat <<____END_MODULE
89+
static int __init auto_test_module_init(void)
90+
{
91+
return auto_runtime_test();
92+
}
93+
module_init(auto_test_module_init);
94+
95+
static void __exit auto_test_module_exit(void)
96+
{
97+
}
98+
module_exit(auto_test_module_exit);
99+
100+
MODULE_AUTHOR("Luis Chamberlain <[email protected]>");
101+
MODULE_LICENSE("GPL");
102+
____END_MODULE
103+
}
104+
105+
case $TEST_TYPE in
106+
a)
107+
gen_template_module_header > $TARGET
108+
gen_template_module_data_a $NUM_SYMS >> $TARGET
109+
gen_template_module_exit >> $TARGET
110+
;;
111+
b)
112+
gen_template_module_header > $TARGET
113+
gen_template_module_data_b >> $TARGET
114+
gen_template_module_exit >> $TARGET
115+
;;
116+
c)
117+
gen_template_module_header > $TARGET
118+
gen_template_module_data_c $((NUM_SYMS * SCALE_FACTOR)) >> $TARGET
119+
gen_template_module_exit >> $TARGET
120+
;;
121+
d)
122+
gen_template_module_header > $TARGET
123+
gen_template_module_data_d $((NUM_SYMS * SCALE_FACTOR * 2)) >> $TARGET
124+
gen_template_module_exit >> $TARGET
125+
;;
126+
*)
127+
;;
128+
esac
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
# Makefile for module loading selftests
3+
4+
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
5+
all:
6+
7+
TEST_PROGS := find_symbol.sh
8+
9+
include ../lib.mk
10+
11+
# Nothing to clean up.
12+
clean:

tools/testing/selftests/module/config

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_TEST_RUNTIME=y
2+
CONFIG_TEST_RUNTIME_MODULE=y
3+
CONFIG_TEST_KALLSYMS=m
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1
3+
# Copyright (C) 2023 Luis Chamberlain <[email protected]>
4+
#
5+
# This is a stress test script for kallsyms through find_symbol()
6+
7+
set -e
8+
9+
# Kselftest framework requirement - SKIP code is 4.
10+
ksft_skip=4
11+
12+
test_reqs()
13+
{
14+
if ! which modprobe 2> /dev/null > /dev/null; then
15+
echo "$0: You need modprobe installed" >&2
16+
exit $ksft_skip
17+
fi
18+
19+
if ! which kmod 2> /dev/null > /dev/null; then
20+
echo "$0: You need kmod installed" >&2
21+
exit $ksft_skip
22+
fi
23+
24+
if ! which perf 2> /dev/null > /dev/null; then
25+
echo "$0: You need perf installed" >&2
26+
exit $ksft_skip
27+
fi
28+
29+
uid=$(id -u)
30+
if [ $uid -ne 0 ]; then
31+
echo $msg must be run as root >&2
32+
exit $ksft_skip
33+
fi
34+
}
35+
36+
load_mod()
37+
{
38+
local STATS="-e duration_time"
39+
STATS="$STATS -e user_time"
40+
STATS="$STATS -e system_time"
41+
STATS="$STATS -e page-faults"
42+
local MOD=$1
43+
44+
local ARCH="$(uname -m)"
45+
case "${ARCH}" in
46+
x86_64)
47+
perf stat $STATS $MODPROBE test_kallsyms_b
48+
;;
49+
*)
50+
time $MODPROBE test_kallsyms_b
51+
exit 1
52+
;;
53+
esac
54+
}
55+
56+
remove_all()
57+
{
58+
$MODPROBE -r test_kallsyms_b
59+
for i in a b c d; do
60+
$MODPROBE -r test_kallsyms_$i
61+
done
62+
}
63+
test_reqs
64+
65+
MODPROBE=$(</proc/sys/kernel/modprobe)
66+
67+
remove_all
68+
load_mod test_kallsyms_b
69+
remove_all
70+
71+
# Now pollute the namespace
72+
$MODPROBE test_kallsyms_c
73+
load_mod test_kallsyms_b
74+
75+
# Now pollute the namespace with twice the number of symbols than the last time
76+
remove_all
77+
$MODPROBE test_kallsyms_c
78+
$MODPROBE test_kallsyms_d
79+
load_mod test_kallsyms_b
80+
81+
exit 0

0 commit comments

Comments
 (0)