Anders Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 1 | #!/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 |
| 7 | OUTPUT="$(pwd)/output" |
| 8 | RESULT_FILE="${OUTPUT}/result.txt" |
| 9 | export RESULT_FILE |
| 10 | |
| 11 | MODULES_LIST="" |
| 12 | MODULES_SUBDIRS="" |
| 13 | MODULE_MODPROBE_NUMBER="1" |
Anders Roxell | 8aa8dbf | 2024-07-11 14:42:19 +0200 | [diff] [blame] | 14 | SKIPLIST="" |
Anders Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 15 | SHARD_NUMBER=1 |
| 16 | SHARD_INDEX=1 |
Anders Roxell | 261c1ee | 2025-07-02 09:17:39 +0200 | [diff] [blame] | 17 | MEMORY_TOLERANCE=512 |
Anders Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 18 | |
| 19 | usage() { |
| 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 Roxell | 8aa8dbf | 2024-07-11 14:42:19 +0200 | [diff] [blame] | 25 | [-s <skiplist modules to skip> ] |
Anders Roxell | 261c1ee | 2025-07-02 09:17:39 +0200 | [diff] [blame] | 26 | [-t <memory tolerance in KB for leak detection> ] |
Anders Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 27 | [-h ]" 1>&2 |
| 28 | exit 0 |
| 29 | } |
| 30 | |
Anders Roxell | 261c1ee | 2025-07-02 09:17:39 +0200 | [diff] [blame] | 31 | while getopts "c:d:i:l:n:s:t:h" o; do |
Anders Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 32 | 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 Roxell | 8aa8dbf | 2024-07-11 14:42:19 +0200 | [diff] [blame] | 38 | s) SKIPLIST="${OPTARG}" ;; |
Anders Roxell | 261c1ee | 2025-07-02 09:17:39 +0200 | [diff] [blame] | 39 | t) MEMORY_TOLERANCE="${OPTARG}" ;; |
Anders Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 40 | h|*) usage ;; |
| 41 | esac |
| 42 | done |
| 43 | |
Anders Roxell | cb19cb9 | 2025-06-06 00:25:27 +0200 | [diff] [blame] | 44 | get_mem_usage_kb() { |
| 45 | grep -i "MemAvailable:" /proc/meminfo | awk '{ print $2 }' |
| 46 | } |
| 47 | |
Anders Roxell | b856256 | 2025-07-02 09:06:40 +0200 | [diff] [blame] | 48 | check_module_memory_leaks_cumulative() { |
Anders Roxell | 9d498ad | 2025-07-02 09:07:09 +0200 | [diff] [blame] | 49 | local module=$1 |
| 50 | local mem_start=$2 |
| 51 | local mem_end=$3 |
Anders Roxell | f6792ff | 2025-07-02 09:08:13 +0200 | [diff] [blame] | 52 | |
| 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 Roxell | cb19cb9 | 2025-06-06 00:25:27 +0200 | [diff] [blame] | 69 | else |
Anders Roxell | f6792ff | 2025-07-02 09:08:13 +0200 | [diff] [blame] | 70 | # 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 Roxell | cb19cb9 | 2025-06-06 00:25:27 +0200 | [diff] [blame] | 79 | fi |
| 80 | } |
| 81 | |
Anders Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 82 | get_modules_list() { |
| 83 | if [ -z "${MODULES_LIST}" ]; then |
Anders Roxell | 25fd951 | 2025-04-28 13:50:31 +0200 | [diff] [blame] | 84 | 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 Roxell | 8aa8dbf | 2024-07-11 14:42:19 +0200 | [diff] [blame] | 99 | split --verbose --numeric-suffixes=1 -n l/"${SHARD_INDEX}"/"${SHARD_NUMBER}" /tmp/modules_to_run.txt > /tmp/shardfile |
Anders Roxell | bde4f2e | 2024-07-09 19:29:06 +0200 | [diff] [blame] | 100 | 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 Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 108 | 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 | |
| 117 | report() { |
| 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 Roxell | 164d52c | 2025-07-02 12:40:45 +0200 | [diff] [blame^] | 131 | report_skip() { |
| 132 | local test_name="$1" |
| 133 | echo "<LAVA_SIGNAL_TESTCASE TEST_CASE_ID=${test_name} RESULT=skip>" |
| 134 | } |
| 135 | |
Anders Roxell | a7b173b | 2025-04-28 12:24:19 +0200 | [diff] [blame] | 136 | scan_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 Roxell | e6199bd | 2025-04-28 12:25:03 +0200 | [diff] [blame] | 141 | check_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 Roxell | 164d52c | 2025-07-02 12:40:45 +0200 | [diff] [blame^] | 151 | detect_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 | |
| 190 | can_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 Roxell | ad114aa | 2025-04-28 12:25:26 +0200 | [diff] [blame] | 204 | kmemleak_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 Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 221 | run () { |
| 222 | for module in ${MODULES_LIST}; do |
Anders Roxell | 164d52c | 2025-07-02 12:40:45 +0200 | [diff] [blame^] | 223 | # Check platform compatibility first |
| 224 | if ! detect_platform_compatibility "$module"; then |
| 225 | continue |
| 226 | fi |
| 227 | |
Anders Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 228 | # don't insert/remove modules that is already inserted. |
| 229 | if ! lsmod | grep "^${module}"; then |
Anders Roxell | 4cbac79 | 2025-07-02 09:08:35 +0200 | [diff] [blame] | 230 | # 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 Roxell | 10db4e0 | 2025-07-02 09:07:41 +0200 | [diff] [blame] | 235 | # Record memory at start of all iterations |
| 236 | mem_start=$(get_mem_usage_kb) |
| 237 | |
Anders Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 238 | 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 Roxell | a7b173b | 2025-04-28 12:24:19 +0200 | [diff] [blame] | 244 | scan_dmesg_for_errors |
| 245 | |
Anders Roxell | 164d52c | 2025-07-02 12:40:45 +0200 | [diff] [blame^] | 246 | # 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 Roxell | a7b173b | 2025-04-28 12:24:19 +0200 | [diff] [blame] | 253 | scan_dmesg_for_errors |
Anders Roxell | e6199bd | 2025-04-28 12:25:03 +0200 | [diff] [blame] | 254 | |
| 255 | check_module_unloaded "${module}" |
Anders Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 256 | done |
Anders Roxell | 10db4e0 | 2025-07-02 09:07:41 +0200 | [diff] [blame] | 257 | |
| 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 Roxell | e3f51b8 | 2024-07-04 12:30:01 +0200 | [diff] [blame] | 261 | fi |
| 262 | done |
| 263 | } |
| 264 | |
| 265 | # Test run. |
| 266 | ! check_root && error_msg "This script must be run as root" |
| 267 | create_out_dir "${OUTPUT}" |
| 268 | info_msg "Output directory: ${OUTPUT}" |
| 269 | info_msg "About to run load/unload kernel modules ..." |
| 270 | get_modules_list |
| 271 | run |
Anders Roxell | ad114aa | 2025-04-28 12:25:26 +0200 | [diff] [blame] | 272 | kmemleak_scan |