add module testing

Test to load/unload modules multiple times to see if something bad
happens, which it shouldn't.

The majority of drivers registers themselves to an API that calls back
into the driver when the hardware side (or network protocol, file
system, etc) is getting detected or requested.

Loading and unloading modules that the DUT don't have hardware for is
likely to find more of the obvious bugs.

Signed-off-by: Anders Roxell <anders.roxell@linaro.org>
diff --git a/automated/linux/modules/modules.sh b/automated/linux/modules/modules.sh
new file mode 100755
index 0000000..adabf88
--- /dev/null
+++ b/automated/linux/modules/modules.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2024 Linaro Ltd.
+
+# shellcheck disable=SC1091
+. ../../lib/sh-test-lib
+OUTPUT="$(pwd)/output"
+RESULT_FILE="${OUTPUT}/result.txt"
+export RESULT_FILE
+
+MODULES_LIST=""
+MODULES_SUBDIRS=""
+MODULE_MODPROBE_NUMBER="1"
+SHARD_NUMBER=1
+SHARD_INDEX=1
+
+usage() {
+	echo "Usage: $0 [-d <subdir of the modules directory> ]
+		[-l <space separated module list> ]
+		[-c <Number of load/unload of a module> ]
+		[-i <sharding bucket to run> ]
+		[-n <number of shard buckets to create> ]
+		[-h ]" 1>&2
+	exit 0
+}
+
+while getopts "c:d:i:l:n:h" o; do
+	case "$o" in
+		d) MODULES_SUBDIRS="${OPTARG}" ;;
+		l) MODULES_LIST="${OPTARG}" ;;
+		c) MODULE_MODPROBE_NUMBER="${OPTARG}" ;;
+		i) SHARD_INDEX="${OPTARG}" ;;
+		n) SHARD_NUMBER="${OPTARG}" ;;
+		h|*) usage ;;
+	esac
+done
+
+get_modules_list() {
+	if [ -z "${MODULES_LIST}" ]; then
+		rm -f /tmp/find_modules.txt
+		for subdir in ${MODULES_SUBDIRS}; do
+			find /lib/modules/"$(uname -r)"/kernel/"${subdir}" -type f -name '*.ko*' | tee -a /tmp/find_modules.txt
+		done
+	split --verbose --numeric-suffixes=1 -n l/"${SHARD_INDEX}"/"${SHARD_NUMBER}" /tmp/find_modules.txt > /tmp/shardfile
+	echo "============== Tests to run ==============="
+	cat /tmp/shardfile
+	echo "===========End Tests to run ==============="
+	if [ -s /tmp/shardfile ]; then
+		report_pass "shardfile"
+	else
+		report_fail "shardfile"
+	fi
+		while IFS= read -r line
+		do
+			module_basename=$(basename "${line}")
+			module_name=${module_basename%.*}
+			MODULES_LIST="${MODULES_LIST} ${module_name}"
+		done < /tmp/shardfile
+	fi
+}
+
+report() {
+	local _modprop_flag="${1}"
+	local _module="${2}"
+	local _text="${3}"
+	local _num="${4}"
+	echo
+	echo "${_text} module: ${_module}"
+	if ! modprobe "${_module}" "${_modprop_flag}"; then
+		report_fail "${_text}_module_${_num}_${_module}"
+	else
+		report_pass "${_text}_module_${_num}_${_module}"
+	fi
+}
+
+run () {
+	for module in ${MODULES_LIST}; do
+		# don't insert/remove modules that is already inserted.
+		if ! lsmod | grep "^${module}"; then
+			for num in $(seq "${MODULE_MODPROBE_NUMBER}"); do
+				dmesg -C
+				report "" "${module}" "insert" "${num}"
+				echo
+				echo "modinfo ${module}"
+				modinfo "${module}"
+				report "--remove" "${module}" "remove" "${num}"
+				dmesg -l 0,1,2,3,4,5
+			done
+		fi
+	done
+}
+
+# Test run.
+! check_root && error_msg "This script must be run as root"
+create_out_dir "${OUTPUT}"
+info_msg "Output directory: ${OUTPUT}"
+info_msg "About to run  load/unload kernel modules ..."
+get_modules_list
+run
diff --git a/automated/linux/modules/modules.yaml b/automated/linux/modules/modules.yaml
new file mode 100644
index 0000000..5464926
--- /dev/null
+++ b/automated/linux/modules/modules.yaml
@@ -0,0 +1,40 @@
+metadata:
+    format: Lava-Test Test Definition 1.0
+    name: module-tests
+    description: |
+                 Load and unload kernel modules.
+    maintainer:
+        - anders.roxell@linaro.org
+    os:
+        - debian
+        - ubuntu
+        - openembedded
+    scope:
+        - functional
+    devices:
+        - rockpi4
+        - x86
+
+params:
+    # If MODULES_LIST is specified with a list of space separated modules then
+    # MOUDLES_SUBDIRS wont have any affect.
+    MODULES_LIST: ""
+
+    # A list of space separated module directories like: 'net, or 'mm'.
+    MODULES_SUBDIRS: ""
+
+    # Number of load/unload of every module
+    MODULE_MODPROBE_NUMBER: 1
+
+    # Number of shards that will be done, default 1 which is the same as no
+    # sharding.
+    SHARD_NUMBER: 1
+
+    # Which bucket to run, default '1' which is the same as no sharding, run
+    # all modules in the MODULES_SUBDIRS list.
+    SHARD_INDEX: 1
+run:
+    steps:
+        - cd ./automated/linux/modules/
+        - ./modules.sh -d "${MODULES_SUBDIRS}" -l "${MODULES_LIST}" -c "${MODULE_MODPROBE_NUMBER}" -n "${SHARD_NUMBER}" -i "${SHARD_INDEX}"
+        - ../../utils/send-to-lava.sh ./output/result.txt