blob: 2ae63b734e59608eb0e0127ef67554287d670393 [file] [log] [blame]
Anders Roxelle3f51b82024-07-04 12:30:01 +02001#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0-only
3# Copyright (C) 2024 Linaro Ltd.
4
5# shellcheck disable=SC1091
6. ../../lib/sh-test-lib
7OUTPUT="$(pwd)/output"
8RESULT_FILE="${OUTPUT}/result.txt"
9export RESULT_FILE
10
11MODULES_LIST=""
12MODULES_SUBDIRS=""
13MODULE_MODPROBE_NUMBER="1"
Anders Roxell8aa8dbf2024-07-11 14:42:19 +020014SKIPLIST=""
Anders Roxelle3f51b82024-07-04 12:30:01 +020015SHARD_NUMBER=1
16SHARD_INDEX=1
Anders Roxell261c1ee2025-07-02 09:17:39 +020017MEMORY_TOLERANCE=512
Anders Roxelle3f51b82024-07-04 12:30:01 +020018
19usage() {
20 echo "Usage: $0 [-d <subdir of the modules directory> ]
21 [-l <space separated module list> ]
22 [-c <Number of load/unload of a module> ]
23 [-i <sharding bucket to run> ]
24 [-n <number of shard buckets to create> ]
Anders Roxell8aa8dbf2024-07-11 14:42:19 +020025 [-s <skiplist modules to skip> ]
Anders Roxell261c1ee2025-07-02 09:17:39 +020026 [-t <memory tolerance in KB for leak detection> ]
Anders Roxelle3f51b82024-07-04 12:30:01 +020027 [-h ]" 1>&2
28 exit 0
29}
30
Anders Roxell261c1ee2025-07-02 09:17:39 +020031while getopts "c:d:i:l:n:s:t:h" o; do
Anders Roxelle3f51b82024-07-04 12:30:01 +020032 case "$o" in
33 d) MODULES_SUBDIRS="${OPTARG}" ;;
34 l) MODULES_LIST="${OPTARG}" ;;
35 c) MODULE_MODPROBE_NUMBER="${OPTARG}" ;;
36 i) SHARD_INDEX="${OPTARG}" ;;
37 n) SHARD_NUMBER="${OPTARG}" ;;
Anders Roxell8aa8dbf2024-07-11 14:42:19 +020038 s) SKIPLIST="${OPTARG}" ;;
Anders Roxell261c1ee2025-07-02 09:17:39 +020039 t) MEMORY_TOLERANCE="${OPTARG}" ;;
Anders Roxelle3f51b82024-07-04 12:30:01 +020040 h|*) usage ;;
41 esac
42done
43
Anders Roxellcb19cb92025-06-06 00:25:27 +020044get_mem_usage_kb() {
45 grep -i "MemAvailable:" /proc/meminfo | awk '{ print $2 }'
46}
47
Anders Roxellb8562562025-07-02 09:06:40 +020048check_module_memory_leaks_cumulative() {
Anders Roxell9d498ad2025-07-02 09:07:09 +020049 local module=$1
50 local mem_start=$2
51 local mem_end=$3
Anders Roxellf6792ff2025-07-02 09:08:13 +020052
53 if [ -e /sys/kernel/debug/kmemleak ]; then
54 # Trigger scan after all iterations complete
55 echo scan > /sys/kernel/debug/kmemleak
56 sleep 3
57
58 # Check for cumulative leaks from all iterations
59 if grep -q "." /sys/kernel/debug/kmemleak; then
60 echo "Cumulative memory leak detected for module ${module}:"
61 cat /sys/kernel/debug/kmemleak
62 report_fail "kmemleak_${module}"
63 else
64 report_pass "kmemleak_${module}"
65 fi
66
67 # Clear leaks for next module
68 echo clear > /sys/kernel/debug/kmemleak
Anders Roxellcb19cb92025-06-06 00:25:27 +020069 else
Anders Roxellf6792ff2025-07-02 09:08:13 +020070 # Fallback: aggregate memory check (not per-iteration)
71 local diff_kb
72 diff_kb=$((mem_start - mem_end))
73 echo "memcheck cumulative: start ${mem_start}, end ${mem_end}, diff ${diff_kb}"
74 if [ "$diff_kb" -lt "-${MEMORY_TOLERANCE}" ]; then
75 report_fail "memcheck_${module}"
76 else
77 report_pass "memcheck_${module}"
78 fi
Anders Roxellcb19cb92025-06-06 00:25:27 +020079 fi
80}
81
Anders Roxelle3f51b82024-07-04 12:30:01 +020082get_modules_list() {
83 if [ -z "${MODULES_LIST}" ]; then
Anders Roxell25fd9512025-04-28 13:50:31 +020084 if [ -n "${MODULES_SUBDIRS}" ]; then
85 subdir=$(echo "${MODULES_SUBDIRS}" | tr ' ' '|')
86 grep -E "kernel/(${subdir})" /lib/modules/"$(uname -r)"/modules.order > /tmp/find_modules.txt
87 else
88 # No subdir given, default to all modules
89 cat /lib/modules/"$(uname -r)"/modules.order > /tmp/find_modules.txt
90 fi
91
92 if [ -n "${SKIPLIST}" ]; then
93 skiplist=$(echo "${SKIPLIST}" | tr ' ' '|')
94 grep -E -v "(${skiplist})" /tmp/find_modules.txt > /tmp/modules_to_run.txt
95 else
96 cp /tmp/find_modules.txt /tmp/modules_to_run.txt
97 fi
98
Anders Roxell8aa8dbf2024-07-11 14:42:19 +020099 split --verbose --numeric-suffixes=1 -n l/"${SHARD_INDEX}"/"${SHARD_NUMBER}" /tmp/modules_to_run.txt > /tmp/shardfile
Anders Roxellbde4f2e2024-07-09 19:29:06 +0200100 echo "============== Tests to run ==============="
101 cat /tmp/shardfile
102 echo "===========End Tests to run ==============="
103 if [ -s /tmp/shardfile ]; then
104 report_pass "shardfile"
105 else
106 report_fail "shardfile"
107 fi
Anders Roxelle3f51b82024-07-04 12:30:01 +0200108 while IFS= read -r line
109 do
110 module_basename=$(basename "${line}")
111 module_name=${module_basename%.*}
112 MODULES_LIST="${MODULES_LIST} ${module_name}"
113 done < /tmp/shardfile
114 fi
115}
116
117report() {
118 local _modprop_flag="${1}"
119 local _module="${2}"
120 local _text="${3}"
121 local _num="${4}"
122 echo
123 echo "${_text} module: ${_module}"
124 if ! modprobe "${_module}" "${_modprop_flag}"; then
125 report_fail "${_text}_module_${_num}_${_module}"
126 else
127 report_pass "${_text}_module_${_num}_${_module}"
128 fi
129}
130
Anders Roxell164d52c2025-07-02 12:40:45 +0200131report_skip() {
132 local test_name="$1"
133 echo "<LAVA_SIGNAL_TESTCASE TEST_CASE_ID=${test_name} RESULT=skip>"
134}
135
Anders Roxella7b173b2025-04-28 12:24:19 +0200136scan_dmesg_for_errors() {
137 echo "=== Scanning dmesg for errors ==="
138 dmesg -l 0,1,2,3,4,5 | grep -Ei "BUG:|WARNING:|Oops:|Call Trace:" && report_fail "dmesg_error_scan" || report_pass "dmesg_error_scan"
139}
140
Anders Roxelle6199bd2025-04-28 12:25:03 +0200141check_module_unloaded() {
142 local _module="$1"
143 if lsmod | grep "^${_module} " > /dev/null; then
144 echo "Module ${_module} still loaded after removal!"
145 report_fail "module_stuck_${_module}"
146 else
147 report_pass "module_unloaded_${_module}"
148 fi
149}
150
Anders Roxell164d52c2025-07-02 12:40:45 +0200151detect_platform_compatibility() {
152 local module=$1
153
154 # Skip ACPI-specific modules on non-ACPI systems
155 case "$module" in
156 *cppc_cpufreq*)
157 if [ ! -d /sys/firmware/acpi ]; then
158 echo "Skipping $module: ACPI not available on this platform"
159 report_skip "platform_${module}"
160 return 1
161 fi
162 ;;
163 esac
164
165 # Skip Allwinner-specific modules on non-Allwinner platforms
166 case "$module" in
167 sun*i-*)
168 if ! grep -q "allwinner" /proc/device-tree/compatible 2>/dev/null; then
169 echo "Skipping $module: Not an Allwinner platform"
170 report_skip "platform_${module}"
171 return 1
172 fi
173 ;;
174 esac
175
176 # Skip MediaTek modules with known issues on specific platforms
177 case "$module" in
178 mtk-adsp-ipc)
179 if grep -q "mediatek" /proc/device-tree/compatible 2>/dev/null; then
180 echo "Skipping $module: Known unload issues on MediaTek platforms"
181 report_skip "platform_${module}"
182 return 1
183 fi
184 ;;
185 esac
186
187 return 0
188}
189
190can_remove_module() {
191 local module=$1
192
193 # Check if other modules depend on this one
194 local used_by
195 used_by="$(lsmod | grep "^$module " | awk '{print $4}')"
196 if [ -n "$used_by" ] && [ "$used_by" != "-" ] && [ "$used_by" != "0" ]; then
197 echo "Warning: $module has dependencies ($used_by), may not unload cleanly"
198 return 1
199 fi
200
201 return 0
202}
203
Anders Roxellad114aa2025-04-28 12:25:26 +0200204kmemleak_scan() {
205 if [ -e /sys/kernel/debug/kmemleak ]; then
206 echo "Triggering kmemleak scan..."
207 echo scan > /sys/kernel/debug/kmemleak
208 sleep 5
209 if grep -q . /sys/kernel/debug/kmemleak; then
210 echo "Potential memory leaks detected:"
211 cat /sys/kernel/debug/kmemleak
212 report_fail "kmemleak_detected"
213 else
214 report_pass "kmemleak_no_leaks"
215 fi
216 else
217 echo "kmemleak not available, skipping scan."
218 fi
219}
220
Anders Roxelle3f51b82024-07-04 12:30:01 +0200221run () {
222 for module in ${MODULES_LIST}; do
Anders Roxell164d52c2025-07-02 12:40:45 +0200223 # Check platform compatibility first
224 if ! detect_platform_compatibility "$module"; then
225 continue
226 fi
227
Anders Roxelle3f51b82024-07-04 12:30:01 +0200228 # don't insert/remove modules that is already inserted.
229 if ! lsmod | grep "^${module}"; then
Anders Roxell4cbac792025-07-02 09:08:35 +0200230 # Clear kmemleak state before testing this module
231 if [ -e /sys/kernel/debug/kmemleak ]; then
232 echo clear > /sys/kernel/debug/kmemleak
233 fi
234
Anders Roxell10db4e02025-07-02 09:07:41 +0200235 # Record memory at start of all iterations
236 mem_start=$(get_mem_usage_kb)
237
Anders Roxelle3f51b82024-07-04 12:30:01 +0200238 for num in $(seq "${MODULE_MODPROBE_NUMBER}"); do
239 dmesg -C
240 report "" "${module}" "insert" "${num}"
241 echo
242 echo "modinfo ${module}"
243 modinfo "${module}"
Anders Roxella7b173b2025-04-28 12:24:19 +0200244 scan_dmesg_for_errors
245
Anders Roxell164d52c2025-07-02 12:40:45 +0200246 # Check if module can be safely removed
247 if can_remove_module "${module}"; then
248 report "--remove" "${module}" "remove" "${num}"
249 else
250 echo "Skipping removal of ${module} (has dependencies)"
251 report_skip "remove_module_${num}_${module}"
252 fi
Anders Roxella7b173b2025-04-28 12:24:19 +0200253 scan_dmesg_for_errors
Anders Roxelle6199bd2025-04-28 12:25:03 +0200254
255 check_module_unloaded "${module}"
Anders Roxelle3f51b82024-07-04 12:30:01 +0200256 done
Anders Roxell10db4e02025-07-02 09:07:41 +0200257
258 # Check for cumulative leaks after all iterations
259 mem_end=$(get_mem_usage_kb)
260 check_module_memory_leaks_cumulative "$module" "$mem_start" "$mem_end"
Anders Roxelle3f51b82024-07-04 12:30:01 +0200261 fi
262 done
263}
264
265# Test run.
266! check_root && error_msg "This script must be run as root"
267create_out_dir "${OUTPUT}"
268info_msg "Output directory: ${OUTPUT}"
269info_msg "About to run load/unload kernel modules ..."
270get_modules_list
271run
Anders Roxellad114aa2025-04-28 12:25:26 +0200272kmemleak_scan