modprobe: Allow passing path to module

This is useful to kernel module developers for testing a just compiled
module: instead of using insmod, they can load the module from the path
while getting all the benefits of modprobe (e.g. module dependency
resolution).

v2:
  - Add test for relative path as well. (Lucas)
  - Add note warning about modules with dependencies not matching the
    installed depmod database. (Lucas)

Signed-off-by: Gustavo Sousa <gustavo.sousa@intel.com>
Cc: Lucas De Marchi <lucas.demarchi@intel.com>
Signed-off-by: Lucas De Marchi <lucas.de.marchi@gmail.com>
This commit is contained in:
Gustavo Sousa 2023-01-13 18:37:45 -03:00 committed by Lucas De Marchi
parent f3db15e900
commit 883d931d1b
24 changed files with 91 additions and 10 deletions

View File

@ -115,6 +115,13 @@
kernel (in addition to any options listed in the configuration
file).
</para>
<para>
When loading modules, <replaceable>modulename</replaceable> can also
be a path to the module. If the path is relative, it must
explicitly start with "./". Note that this may fail when using a
path to a module with dependencies not matching the installed depmod
database.
</para>
</refsect1>
<refsect1><title>OPTIONS</title>

View File

@ -56,6 +56,8 @@ map=(
["test-modprobe/alias-to-none/lib/modules/4.4.4/kernel/"]="mod-simple.ko"
["test-modprobe/module-param-kcmdline/lib/modules/4.4.4/kernel/"]="mod-simple.ko"
["test-modprobe/external/lib/modules/external/"]="mod-simple.ko"
["test-modprobe/module-from-abspath/home/foo/"]="mod-simple.ko"
["test-modprobe/module-from-relpath/home/foo/"]="mod-simple.ko"
["test-depmod/modules-order-compressed/lib/modules/4.4.4/kernel/drivers/block/cciss.ko"]="mod-fake-cciss.ko"
["test-depmod/modules-order-compressed/lib/modules/4.4.4/kernel/drivers/scsi/hpsa.ko"]="mod-fake-hpsa.ko"
["test-depmod/modules-order-compressed/lib/modules/4.4.4/kernel/drivers/scsi/scsi_mod.ko"]="mod-fake-scsi-mod.ko"

View File

@ -0,0 +1 @@
# Aliases extracted from modules themselves.

View File

@ -0,0 +1 @@
/lib/modules/external/mod-simple.ko:

View File

@ -0,0 +1 @@
# Soft dependencies extracted from modules themselves.

View File

@ -0,0 +1 @@
# Aliases for symbols, used by symbol_request().

View File

@ -0,0 +1 @@
# Aliases extracted from modules themselves.

View File

@ -0,0 +1 @@
/lib/modules/external/mod-simple.ko:

View File

@ -0,0 +1 @@
# Soft dependencies extracted from modules themselves.

View File

@ -0,0 +1 @@
# Aliases for symbols, used by symbol_request().

View File

@ -422,4 +422,54 @@ DEFINE_TEST(modprobe_external,
.modules_loaded = "mod-simple",
);
static noreturn int modprobe_module_from_abspath(const struct test *t)
{
const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
const char *const args[] = {
progname,
"/home/foo/mod-simple.ko",
NULL,
};
test_spawn_prog(progname, args);
exit(EXIT_FAILURE);
}
DEFINE_TEST(modprobe_module_from_abspath,
.description = "check modprobe able to load module given as an absolute path",
.config = {
[TC_UNAME_R] = "4.4.4",
[TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-from-abspath",
[TC_INIT_MODULE_RETCODES] = "",
},
.modules_loaded = "mod-simple",
);
static noreturn int modprobe_module_from_relpath(const struct test *t)
{
const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
const char *const args[] = {
progname,
"./mod-simple.ko",
NULL,
};
if (chdir("/home/foo") != 0) {
perror("failed to change into /home/foo");
exit(EXIT_FAILURE);
}
test_spawn_prog(progname, args);
exit(EXIT_FAILURE);
}
DEFINE_TEST(modprobe_module_from_relpath,
.description = "check modprobe able to load module given as a relative path",
.config = {
[TC_UNAME_R] = "4.4.4",
[TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-from-relpath",
[TC_INIT_MODULE_RETCODES] = "",
},
.need_spawn = true,
.modules_loaded = "mod-simple",
);
TESTSUITE_MAIN();

View File

@ -614,14 +614,23 @@ static int insmod(struct kmod_ctx *ctx, const char *alias,
const char *extra_options)
{
struct kmod_list *l, *list = NULL;
struct kmod_module *mod = NULL;
int err, flags = 0;
err = kmod_module_new_from_lookup(ctx, alias, &list);
if (list == NULL || err < 0) {
LOG("Module %s not found in directory %s\n", alias,
ctx ? kmod_get_dirname(ctx) : "(missing)");
return -ENOENT;
if (strncmp(alias, "/", 1) == 0 || strncmp(alias, "./", 2) == 0) {
err = kmod_module_new_from_path(ctx, alias, &mod);
if (err < 0) {
LOG("Failed to get module from path %s: %s\n", alias,
strerror(-err));
return -ENOENT;
}
} else {
err = kmod_module_new_from_lookup(ctx, alias, &list);
if (list == NULL || err < 0) {
LOG("Module %s not found in directory %s\n", alias,
ctx ? kmod_get_dirname(ctx) : "(missing)");
return -ENOENT;
}
}
if (strip_modversion || force)
@ -642,13 +651,18 @@ static int insmod(struct kmod_ctx *ctx, const char *alias,
if (first_time)
flags |= KMOD_PROBE_FAIL_ON_LOADED;
kmod_list_foreach(l, list) {
struct kmod_module *mod = kmod_module_get_module(l);
/* If module is loaded from path */
if (mod != NULL) {
err = insmod_insert(mod, flags, extra_options);
kmod_module_unref(mod);
} else {
kmod_list_foreach(l, list) {
mod = kmod_module_get_module(l);
err = insmod_insert(mod, flags, extra_options);
kmod_module_unref(mod);
}
kmod_module_unref_list(list);
}
kmod_module_unref_list(list);
return err;
}