From: "Li Wang (Hieco)" <li.wang@linux.dev>
To: ltp@lists.linux.it
Cc: wakel@google.com
Subject: [LTP] [PATCH v7 2/4] tst_kconfig: load and unload kernel modules for CONFIG_*=m
Date: Tue, 3 Nov 2026 15:57:20 +0800 [thread overview]
Message-ID: <20261103075722.181317-3-li.wang@linux.dev> (raw)
In-Reply-To: <20261103075722.181317-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
and not already loaded while parsing the kernel config. When
needs_kconfigs is set, load them via tst_modprobe() after a
successful tst_kconfig_check(), and unload them in reverse order
from do_cleanup(). Only modules that were actually loaded are
unloaded; modules still in use are skipped. Dependencies pulled in
by modprobe are not unloaded.
Modules are only loaded when running as root.
Signed-off-by: Li Wang (Hieco) <li.wang@linux.dev>
---
include/tst_kconfig.h | 21 ++++++++++
lib/tst_kconfig.c | 89 +++++++++++++++++++++++++++++++++++++++++++
lib/tst_test.c | 13 +++++--
3 files changed, 120 insertions(+), 3 deletions(-)
diff --git a/include/tst_kconfig.h b/include/tst_kconfig.h
index c454f3467..d603919d9 100644
--- a/include/tst_kconfig.h
+++ b/include/tst_kconfig.h
@@ -105,4 +105,25 @@ 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 the modules for CONFIG_* options found as =m in the kernel config
+ * that are not already loaded, in the order they were found.
+ * Does nothing when not running as root.
+ */
+void tst_kconfig_module_load(void);
+
+/**
+ * tst_kconfig_module_unload() - Unload modules loaded by
+ * tst_kconfig_module_load().
+ *
+ * Unloads, in reverse order, only the modules that were actually loaded.
+ * Modules still in use are skipped silently. Dependencies pulled in by
+ * modprobe are not unloaded.
+ *
+ * Called from do_cleanup(), so it runs regardless of the test result.
+ */
+void tst_kconfig_module_unload(void);
+
#endif /* TST_KCONFIG_H__ */
diff --git a/lib/tst_kconfig.c b/lib/tst_kconfig.c
index f055c2e5f..1b5fa104a 100644
--- a/lib/tst_kconfig.c
+++ b/lib/tst_kconfig.c
@@ -16,8 +16,16 @@
#include "tst_bool_expr.h"
#include "tst_safe_stdio.h"
+#include "lapi/syscalls.h"
+#include "tst_module.h"
#include "tst_kconfig_checks.h"
+#define MAX_LOADED_MODULES 32
+
+static const char *loaded_modules[MAX_LOADED_MODULES];
+static unsigned int module_count;
+static unsigned int loaded_module_count;
+
static int kconfig_skip_check(void)
{
char *skipped = getenv("KCONFIG_SKIP_CHECK");
@@ -189,6 +197,38 @@ static void kconfig_module_check(struct tst_kconfig_var *var)
}
}
+static void kconfig_module_record(struct tst_kconfig_var *var)
+{
+ unsigned int i;
+ const char *mod = NULL;
+
+ if (var->choice != 'm')
+ return;
+
+ for (i = 0; config_module_maps[i].config; i++) {
+ if (!strcmp(config_module_maps[i].config, var->id)) {
+ mod = config_module_maps[i].module_name;
+ break;
+ }
+ }
+
+ if (!mod)
+ return;
+
+ if (tst_module_is_loaded(mod))
+ return;
+
+ for (i = 0; i < module_count; i++) {
+ if (!strcmp(loaded_modules[i], mod))
+ return;
+ }
+
+ if (module_count >= MAX_LOADED_MODULES)
+ tst_brk(TBROK, "Too many modules, increase MAX_LOADED_MODULES");
+
+ loaded_modules[module_count++] = mod;
+}
+
static inline int kconfig_parse_line(const char *line,
struct tst_kconfig_var *vars,
unsigned int vars_len)
@@ -268,6 +308,7 @@ out:
vars[i].choice = 'm';
kconfig_runtime_check(&vars[i]);
kconfig_module_check(&vars[i]);
+ kconfig_module_record(&vars[i]);
return 1;
}
}
@@ -300,6 +341,54 @@ exit:
close_kconfig(fp);
}
+void tst_kconfig_module_load(void)
+{
+ unsigned int i;
+
+ if (geteuid() != 0)
+ return;
+
+ for (i = 0; i < module_count; i++) {
+ tst_res(TDEBUG, "Loading module '%s'", loaded_modules[i]);
+ tst_modprobe(loaded_modules[i], NULL);
+
+ loaded_module_count++;
+ }
+}
+
+void tst_kconfig_module_unload(void)
+{
+ char name[64], *p;
+
+ if (!loaded_module_count)
+ return;
+
+ while (loaded_module_count > 0) {
+ snprintf(name, sizeof(name), "%s",
+ loaded_modules[--loaded_module_count]);
+
+ for (p = name; (p = strchr(p, '-')); )
+ *p = '_';
+
+ tst_res(TDEBUG, "Unloading module '%s'", name);
+
+ if (!syscall(__NR_delete_module, name, O_NONBLOCK))
+ continue;
+
+ switch (errno) {
+ case ENOENT: /* already unloaded */
+ case EWOULDBLOCK: /* in use */
+ case EBUSY: /* still loading or unloading */
+ case ENOSYS: /* no CONFIG_MODULE_UNLOAD */
+ break;
+ default:
+ tst_res(TWARN | TERRNO, "Failed to unload module '%s'", name);
+ }
+ }
+
+ 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..a9e679bee 100644
--- a/lib/tst_test.c
+++ b/lib/tst_test.c
@@ -1471,12 +1471,16 @@ static void do_setup(int argc, char *argv[])
check_supported_kver();
- 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_root && geteuid() != 0)
tst_brk(TCONF, "Test needs to be run as root");
+ if (tst_test->needs_kconfigs) {
+ if (tst_kconfig_check(tst_test->needs_kconfigs))
+ tst_brk(TCONF, "Aborting due to unsuitable kernel config, see above!");
+
+ tst_kconfig_module_load();
+ }
+
if (tst_test->min_kver)
check_kver(tst_test->min_kver, 1);
@@ -1690,6 +1694,9 @@ static void do_cleanup(void)
if (tst_test->restore_wallclock)
tst_wallclock_restore();
+ if (tst_test->needs_kconfigs)
+ tst_kconfig_module_unload();
+
cleanup_ipc();
}
--
2.56.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
next prev parent reply other threads:[~2026-11-03 7:58 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 7:57 [LTP] [PATCH v7 0/4] tst_kconfig: automatic module load/unload for CONFIG_*=m Li Wang (Hieco)
2026-11-03 7:57 ` [LTP] [PATCH v7 1/4] lib: add module is loaded check Li Wang (Hieco)
2026-11-03 10:19 ` [LTP] " linuxtestproject.agent
2026-11-06 12:42 ` [LTP] [PATCH v7 1/4] " Cyril Hrubis
2026-11-09 3:25 ` Li Wang
2026-11-11 16:13 ` Cyril Hrubis
2026-11-03 7:57 ` Li Wang (Hieco) [this message]
2026-11-06 12:50 ` [LTP] [PATCH v7 2/4] tst_kconfig: load and unload kernel modules for CONFIG_*=m Cyril Hrubis
2026-11-09 3:36 ` Li Wang
2026-11-03 7:57 ` [LTP] [PATCH v7 3/4] tst_kconfig: add module mappings for vsock/vsock_loopback Li Wang (Hieco)
2026-11-03 7:57 ` [LTP] [PATCH v7 4/4] testcases: drop manual tst_modprobe() now handled by tst_kconfig Li Wang (Hieco)
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=20261103075722.181317-3-li.wang@linux.dev \
--to=li.wang@linux.dev \
--cc=ltp@lists.linux.it \
--cc=wakel@google.com \
/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