blob: 63fead8fa455c5c6af83ed4b6c56e766beeaaf44 [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 Roxella7b173b2025-04-28 12:24:19 +0200131scan_dmesg_for_errors() {
132 echo "=== Scanning dmesg for errors ==="
133 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"
134}
135
Anders Roxelle6199bd2025-04-28 12:25:03 +0200136check_module_unloaded() {
137 local _module="$1"
138 if lsmod | grep "^${_module} " > /dev/null; then
139 echo "Module ${_module} still loaded after removal!"
140 report_fail "module_stuck_${_module}"
141 else
142 report_pass "module_unloaded_${_module}"
143 fi
144}
145
Anders Roxellad114aa2025-04-28 12:25:26 +0200146kmemleak_scan() {
147 if [ -e /sys/kernel/debug/kmemleak ]; then
148 echo "Triggering kmemleak scan..."
149 echo scan > /sys/kernel/debug/kmemleak
150 sleep 5
151 if grep -q . /sys/kernel/debug/kmemleak; then
152 echo "Potential memory leaks detected:"
153 cat /sys/kernel/debug/kmemleak
154 report_fail "kmemleak_detected"
155 else
156 report_pass "kmemleak_no_leaks"
157 fi
158 else
159 echo "kmemleak not available, skipping scan."
160 fi
161}
162
Anders Roxelle3f51b82024-07-04 12:30:01 +0200163run () {
164 for module in ${MODULES_LIST}; do
165 # don't insert/remove modules that is already inserted.
166 if ! lsmod | grep "^${module}"; then
Anders Roxell10db4e02025-07-02 09:07:41 +0200167 # Record memory at start of all iterations
168 mem_start=$(get_mem_usage_kb)
169
Anders Roxelle3f51b82024-07-04 12:30:01 +0200170 for num in $(seq "${MODULE_MODPROBE_NUMBER}"); do
171 dmesg -C
172 report "" "${module}" "insert" "${num}"
173 echo
174 echo "modinfo ${module}"
175 modinfo "${module}"
Anders Roxella7b173b2025-04-28 12:24:19 +0200176 scan_dmesg_for_errors
177
Anders Roxelle3f51b82024-07-04 12:30:01 +0200178 report "--remove" "${module}" "remove" "${num}"
Anders Roxella7b173b2025-04-28 12:24:19 +0200179 scan_dmesg_for_errors
Anders Roxelle6199bd2025-04-28 12:25:03 +0200180
181 check_module_unloaded "${module}"
Anders Roxelle3f51b82024-07-04 12:30:01 +0200182 done
Anders Roxell10db4e02025-07-02 09:07:41 +0200183
184 # Check for cumulative leaks after all iterations
185 mem_end=$(get_mem_usage_kb)
186 check_module_memory_leaks_cumulative "$module" "$mem_start" "$mem_end"
Anders Roxelle3f51b82024-07-04 12:30:01 +0200187 fi
188 done
189}
190
191# Test run.
192! check_root && error_msg "This script must be run as root"
193create_out_dir "${OUTPUT}"
194info_msg "Output directory: ${OUTPUT}"
195info_msg "About to run load/unload kernel modules ..."
196get_modules_list
197run
Anders Roxellad114aa2025-04-28 12:25:26 +0200198kmemleak_scan