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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox