From: Li Wang <li.wang@linux.dev>
To: ltp@lists.linux.it
Subject: [LTP] [PATCH 1/3] tst_kconfig: load and unload kernel modules for CONFIG_*=m
Date: Wed, 7 Oct 2026 18:57:28 +0800 [thread overview]
Message-ID: <20261007105730.94492-2-li.wang@linux.dev> (raw)
In-Reply-To: <20261007105730.94492-1-li.wang@linux.dev>
Tests that depend on a config set to =m must currently load the
module themselves, and may fail with misleading errors when it is
not loaded, e.g. bind() returning EADDRNOTAVAIL for AF_VSOCK when
vsock_loopback is missing.
Record the module for every config_module_maps[] entry seen as =m
during kconfig_parse_line(), load them in
tst_kconfig_module_load() via tst_modprobe(), and unload them in
reverse order from tst_kconfig_module_unload_all(), called by
do_test_cleanup(). Modules still in use are skipped without
aborting the test.
Loading and unloading are performed only as root; otherwise the
recorded list is left untouched.
Signed-off-by: Li Wang <li.wang@linux.dev>
---
include/tst_kconfig.h | 28 +++++++++++++++++
lib/tst_kconfig.c | 70 +++++++++++++++++++++++++++++++++++++++++++
lib/tst_test.c | 4 +++
3 files changed, 102 insertions(+)
diff --git a/include/tst_kconfig.h b/include/tst_kconfig.h
index c454f3467..540ae369c 100644
--- a/include/tst_kconfig.h
+++ b/include/tst_kconfig.h
@@ -105,4 +105,32 @@ void tst_kcmdline_parse(struct tst_kcmdline_var params[], size_t params_len);
*/
int tst_has_slow_kconfig(void);
+/*
+ * tst_kconfig_module_load() - Load kernel modules for CONFIG_*=m entries.
+ *
+ * Loads every module recorded during the last kernel config check, in
+ * the order they were discovered. The list is populated by
+ * kconfig_module_record() from kconfig_parse_line() for each CONFIG_*
+ * entry that is set to =m and has a matching entry in
+ * config_module_maps[].
+ *
+ * Loading is skipped when not running as root, and does nothing when
+ * the recorded list is empty.
+ */
+void tst_kconfig_module_load(void);
+
+/*
+ * tst_kconfig_module_unload_all() - Unload modules loaded by
+ * tst_kconfig_module_load().
+ *
+ * Unloads every module recorded by tst_kconfig_module_load(), in
+ * reverse load order so that inter-module dependencies are respected.
+ * Modules still in use are skipped silently, leaving the system state
+ * as close as possible to what it was before the test started.
+ *
+ * Called from do_test_cleanup() so that the modules are released when
+ * the test finishes, regardless of the test result.
+ */
+void tst_kconfig_module_unload_all(void);
+
#endif /* TST_KCONFIG_H__ */
diff --git a/lib/tst_kconfig.c b/lib/tst_kconfig.c
index 49957e28d..7e3ee1fe8 100644
--- a/lib/tst_kconfig.c
+++ b/lib/tst_kconfig.c
@@ -16,8 +16,14 @@
#include "tst_bool_expr.h"
#include "tst_safe_stdio.h"
+#include "tst_module.h"
#include "tst_kconfig_checks.h"
+#define MAX_LOADED_MODULES 16
+
+static const char *loaded_modules[MAX_LOADED_MODULES];
+static unsigned int loaded_module_count;
+
static int kconfig_skip_check(void)
{
char *skipped = getenv("KCONFIG_SKIP_CHECK");
@@ -189,6 +195,35 @@ static void kconfig_module_check(struct tst_kconfig_var *var)
}
}
+static const char *module_name_for_config(const char *cfg)
+{
+ size_t i;
+
+ for (i = 0; config_module_maps[i].config; i++) {
+ if (!strcmp(config_module_maps[i].config, cfg))
+ return config_module_maps[i].module_name;
+ }
+
+ return NULL;
+}
+
+static void kconfig_module_record(const char *cfg)
+{
+ unsigned int i;
+ const char *mod = module_name_for_config(cfg);
+
+ if (!mod)
+ return;
+
+ for (i = 0; i < loaded_module_count; i++) {
+ if (!strcmp(loaded_modules[i], mod))
+ return;
+ }
+
+ if (loaded_module_count < MAX_LOADED_MODULES)
+ loaded_modules[loaded_module_count++] = mod;
+}
+
static inline int kconfig_parse_line(const char *line,
struct tst_kconfig_var *vars,
unsigned int vars_len)
@@ -268,6 +303,8 @@ out:
vars[i].choice = 'm';
kconfig_runtime_check(&vars[i]);
kconfig_module_check(&vars[i]);
+ if (vars[i].choice == 'm')
+ kconfig_module_record(vars[i].id);
return 1;
}
}
@@ -300,6 +337,39 @@ exit:
close_kconfig(fp);
}
+void tst_kconfig_module_load(void)
+{
+ unsigned int i;
+
+ if (geteuid() != 0)
+ return;
+
+ for (i = 0; i < loaded_module_count; i++) {
+ tst_res(TDEBUG, "Loading module '%s'", loaded_modules[i]);
+ tst_modprobe(loaded_modules[i], NULL);
+ }
+}
+
+void tst_kconfig_module_unload_all(void)
+{
+ unsigned int i;
+
+ if (!loaded_module_count)
+ return;
+
+ if (geteuid() != 0)
+ return;
+
+ for (i = loaded_module_count; i > 0; i--) {
+ const char *const argv[] = { "rmmod", loaded_modules[i - 1], NULL };
+
+ tst_res(TDEBUG, "Unloading module '%s'", loaded_modules[i - 1]);
+ tst_cmd(argv, "/dev/null", "/dev/null", TST_CMD_PASS_RETVAL);
+ }
+
+ loaded_module_count = 0;
+}
+
static size_t array_len(const char *const kconfigs[])
{
size_t i = 0;
diff --git a/lib/tst_test.c b/lib/tst_test.c
index 10ff73397..04a85a781 100644
--- a/lib/tst_test.c
+++ b/lib/tst_test.c
@@ -425,6 +425,7 @@ static void do_test_cleanup(void)
tst_test->cleanup();
tst_free_all();
+ tst_kconfig_module_unload_all();
tst_brk_handler = tst_vbrk_;
}
@@ -1474,6 +1475,9 @@ static void do_setup(int argc, char *argv[])
if (tst_test->needs_kconfigs && tst_kconfig_check(tst_test->needs_kconfigs))
tst_brk(TCONF, "Aborting due to unsuitable kernel config, see above!");
+ if (tst_test->needs_kconfigs)
+ tst_kconfig_module_load();
+
if (tst_test->needs_root && geteuid() != 0)
tst_brk(TCONF, "Test needs to be run as root");
--
2.55.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
next prev parent reply other threads:[~2026-10-07 10:58 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-07 10:57 [LTP] [PATCH 0/3] tst_kconfig: automatic module load/unload for CONFIG_*=m Li Wang
2026-10-07 10:57 ` Li Wang [this message]
2026-10-07 14:17 ` [LTP] tst_kconfig: load and unload kernel modules " linuxtestproject.agent
2026-10-08 5:09 ` Li Wang
2026-10-07 10:57 ` [LTP] [PATCH 2/3] tst_kconfig: add module mappings for vsock/vsock_loopback Li Wang
2026-10-07 10:57 ` [LTP] [PATCH 3/3] testcases: drop manual tst_modprobe() now handled by tst_kconfig Li Wang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261007105730.94492-2-li.wang@linux.dev \
--to=li.wang@linux.dev \
--cc=ltp@lists.linux.it \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.