* [PATCH v2 0/3] kmod: modprobe: Extend holders removal to multi-level dependencies
@ 2023-03-29 13:51 Nicolas Schier
2023-03-29 13:51 ` [PATCH v2 1/3] kmod: modprobe: Remove holders recursively Nicolas Schier
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Nicolas Schier @ 2023-03-29 13:51 UTC (permalink / raw)
To: linux-modules; +Cc: Lucas De Marchi, Nicolas Schier
While commit 42b32d30c38e ("modprobe: Fix holders removal", 2022-03-29) already
implements removing first-level holders, indirect holders were not evaluated.
In a simple module dependency chain like
module3 depends on module2
module2 depends on module1
'modprobe -r --remove-holders module1' was only considering module2 and module1
and thus had to fail as module3 was still loaded and blocking removal of
module2.
By doing recursive depth-first removal this can be fixed for such simple
dependency.
(modprobe.8 man page is not touched as it does not yet have anything
about --remove-holders.)
Signed-off-by: Nicolas Schier <n.schier@avm.de>
---
To: linux-modules@vger.kernel.org
Cc: Lucas De Marchi <lucas.de.marchi@gmail.com>
---
Changes in v2:
* Several fixes (memleak, attempt to remove modules that were just
removed, --dry-run support)
* new patch: Add rudimentary implementation of sysfs tree modification
to delete_module()
* new patch: Add two tests regarding --remove-holders to
testsuite/test-modprobe.c
* Link to v1: https://lore.kernel.org/r/20230309-remove-holders-recursively-v1-1-c27cdba1edbf@avm.de
---
Nicolas Schier (3):
kmod: modprobe: Remove holders recursively
testsuite: delete_module: Roughly implement fake-removal in sysfs tree
testsuite: modprobe: Add test for --remove-holders
Makefile.am | 1 +
testsuite/delete_module.c | 262 ++++++++++++++++++++-
testsuite/module-playground/Makefile | 6 +
testsuite/module-playground/mod-dep-chain-a.c | 21 ++
testsuite/module-playground/mod-dep-chain-b.c | 22 ++
testsuite/module-playground/mod-dep-chain-c.c | 22 ++
testsuite/module-playground/mod-dep-chain.h | 8 +
.../remove-holders/lib/modules/6.2/modules.alias | 1 +
.../lib/modules/6.2/modules.alias.bin | Bin 0 -> 12 bytes
.../lib/modules/6.2/modules.builtin.alias.bin | 0
.../lib/modules/6.2/modules.builtin.bin | 0
.../remove-holders/lib/modules/6.2/modules.dep | 3 +
.../remove-holders/lib/modules/6.2/modules.dep.bin | Bin 0 -> 229 bytes
.../remove-holders/lib/modules/6.2/modules.devname | 0
.../remove-holders/lib/modules/6.2/modules.softdep | 1 +
.../remove-holders/lib/modules/6.2/modules.symbols | 4 +
.../lib/modules/6.2/modules.symbols.bin | Bin 0 -> 143 bytes
.../test-modprobe/remove-holders/proc/modules | 3 +
.../sys/module/mod_dep_chain_a/coresize | 1 +
.../module/mod_dep_chain_a/holders/mod_dep_chain_b | 1 +
.../sys/module/mod_dep_chain_a/initsize | 1 +
.../sys/module/mod_dep_chain_a/initstate | 1 +
.../sys/module/mod_dep_chain_a/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_a/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_a/refcnt | 1 +
.../sections/.gnu.linkonce.this_module | 1 +
.../sys/module/mod_dep_chain_a/sections/.init.data | 1 +
.../module/mod_dep_chain_a/sections/.note.Linux | 1 +
.../mod_dep_chain_a/sections/.note.gnu.build-id | 1 +
.../module/mod_dep_chain_a/sections/.orc_unwind | 1 +
.../module/mod_dep_chain_a/sections/.orc_unwind_ip | 1 +
.../module/mod_dep_chain_a/sections/.return_sites | 1 +
.../module/mod_dep_chain_a/sections/.rodata.str1.1 | 1 +
.../sys/module/mod_dep_chain_a/sections/.strtab | 1 +
.../sys/module/mod_dep_chain_a/sections/.symtab | 1 +
.../module/mod_dep_chain_a/sections/.text.unlikely | 1 +
.../sys/module/mod_dep_chain_a/sections/__kcrctab | 1 +
.../sys/module/mod_dep_chain_a/sections/__ksymtab | 1 +
.../mod_dep_chain_a/sections/__ksymtab_strings | 1 +
.../module/mod_dep_chain_a/sections/__mcount_loc | 1 +
.../sys/module/mod_dep_chain_a/taint | 1 +
.../sys/module/mod_dep_chain_b/coresize | 1 +
.../module/mod_dep_chain_b/holders/mod_dep_chain_c | 1 +
.../sys/module/mod_dep_chain_b/initsize | 1 +
.../sys/module/mod_dep_chain_b/initstate | 1 +
.../sys/module/mod_dep_chain_b/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_b/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_b/refcnt | 1 +
.../sections/.gnu.linkonce.this_module | 1 +
.../sys/module/mod_dep_chain_b/sections/.init.data | 1 +
.../module/mod_dep_chain_b/sections/.note.Linux | 1 +
.../mod_dep_chain_b/sections/.note.gnu.build-id | 1 +
.../module/mod_dep_chain_b/sections/.orc_unwind | 1 +
.../module/mod_dep_chain_b/sections/.orc_unwind_ip | 1 +
.../module/mod_dep_chain_b/sections/.return_sites | 1 +
.../module/mod_dep_chain_b/sections/.rodata.str1.8 | 1 +
.../sys/module/mod_dep_chain_b/sections/.strtab | 1 +
.../sys/module/mod_dep_chain_b/sections/.symtab | 1 +
.../module/mod_dep_chain_b/sections/.text.unlikely | 1 +
.../sys/module/mod_dep_chain_b/sections/__kcrctab | 1 +
.../sys/module/mod_dep_chain_b/sections/__ksymtab | 1 +
.../mod_dep_chain_b/sections/__ksymtab_strings | 1 +
.../module/mod_dep_chain_b/sections/__mcount_loc | 1 +
.../sys/module/mod_dep_chain_b/taint | 1 +
.../sys/module/mod_dep_chain_c/coresize | 1 +
.../sys/module/mod_dep_chain_c/holders/.gitignore | 0
.../sys/module/mod_dep_chain_c/initsize | 1 +
.../sys/module/mod_dep_chain_c/initstate | 1 +
.../sys/module/mod_dep_chain_c/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_c/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_c/refcnt | 1 +
.../sys/module/mod_dep_chain_c/taint | 1 +
.../remove-single/lib/modules/6.2/modules.alias | 1 +
.../lib/modules/6.2/modules.alias.bin | Bin 0 -> 12 bytes
.../lib/modules/6.2/modules.builtin.alias.bin | 0
.../lib/modules/6.2/modules.builtin.bin | 0
.../remove-single/lib/modules/6.2/modules.dep | 3 +
.../remove-single/lib/modules/6.2/modules.dep.bin | Bin 0 -> 229 bytes
.../remove-single/lib/modules/6.2/modules.devname | 0
.../remove-single/lib/modules/6.2/modules.softdep | 1 +
.../remove-single/lib/modules/6.2/modules.symbols | 4 +
.../lib/modules/6.2/modules.symbols.bin | Bin 0 -> 143 bytes
.../test-modprobe/remove-single/proc/modules | 3 +
.../sys/module/mod_dep_chain_a/coresize | 1 +
.../module/mod_dep_chain_a/holders/mod_dep_chain_b | 1 +
.../sys/module/mod_dep_chain_a/initsize | 1 +
.../sys/module/mod_dep_chain_a/initstate | 1 +
.../sys/module/mod_dep_chain_a/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_a/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_a/refcnt | 1 +
.../sections/.gnu.linkonce.this_module | 1 +
.../sys/module/mod_dep_chain_a/sections/.init.data | 1 +
.../module/mod_dep_chain_a/sections/.note.Linux | 1 +
.../mod_dep_chain_a/sections/.note.gnu.build-id | 1 +
.../module/mod_dep_chain_a/sections/.orc_unwind | 1 +
.../module/mod_dep_chain_a/sections/.orc_unwind_ip | 1 +
.../module/mod_dep_chain_a/sections/.return_sites | 1 +
.../module/mod_dep_chain_a/sections/.rodata.str1.1 | 1 +
.../sys/module/mod_dep_chain_a/sections/.strtab | 1 +
.../sys/module/mod_dep_chain_a/sections/.symtab | 1 +
.../module/mod_dep_chain_a/sections/.text.unlikely | 1 +
.../sys/module/mod_dep_chain_a/sections/__kcrctab | 1 +
.../sys/module/mod_dep_chain_a/sections/__ksymtab | 1 +
.../mod_dep_chain_a/sections/__ksymtab_strings | 1 +
.../module/mod_dep_chain_a/sections/__mcount_loc | 1 +
.../remove-single/sys/module/mod_dep_chain_a/taint | 1 +
.../sys/module/mod_dep_chain_b/coresize | 1 +
.../module/mod_dep_chain_b/holders/mod_dep_chain_c | 1 +
.../sys/module/mod_dep_chain_b/initsize | 1 +
.../sys/module/mod_dep_chain_b/initstate | 1 +
.../sys/module/mod_dep_chain_b/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_b/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_b/refcnt | 1 +
.../sections/.gnu.linkonce.this_module | 1 +
.../sys/module/mod_dep_chain_b/sections/.init.data | 1 +
.../module/mod_dep_chain_b/sections/.note.Linux | 1 +
.../mod_dep_chain_b/sections/.note.gnu.build-id | 1 +
.../module/mod_dep_chain_b/sections/.orc_unwind | 1 +
.../module/mod_dep_chain_b/sections/.orc_unwind_ip | 1 +
.../module/mod_dep_chain_b/sections/.return_sites | 1 +
.../module/mod_dep_chain_b/sections/.rodata.str1.8 | 1 +
.../sys/module/mod_dep_chain_b/sections/.strtab | 1 +
.../sys/module/mod_dep_chain_b/sections/.symtab | 1 +
.../module/mod_dep_chain_b/sections/.text.unlikely | 1 +
.../sys/module/mod_dep_chain_b/sections/__kcrctab | 1 +
.../sys/module/mod_dep_chain_b/sections/__ksymtab | 1 +
.../mod_dep_chain_b/sections/__ksymtab_strings | 1 +
.../module/mod_dep_chain_b/sections/__mcount_loc | 1 +
.../remove-single/sys/module/mod_dep_chain_b/taint | 1 +
.../sys/module/mod_dep_chain_c/coresize | 1 +
.../sys/module/mod_dep_chain_c/holders/.gitignore | 0
.../sys/module/mod_dep_chain_c/initsize | 1 +
.../sys/module/mod_dep_chain_c/initstate | 1 +
.../sys/module/mod_dep_chain_c/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_c/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_c/refcnt | 1 +
.../remove-single/sys/module/mod_dep_chain_c/taint | 1 +
testsuite/setup-rootfs.sh | 3 +
testsuite/test-modprobe.c | 55 +++++
tools/modprobe.c | 44 +++-
140 files changed, 551 insertions(+), 11 deletions(-)
---
base-commit: 3d1bd339ab942ea47e60f053f4b11b0c47ff082b
change-id: 20230309-remove-holders-recursively-f667f32e2a7d
Best regards,
--
Nicolas Schier
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/3] kmod: modprobe: Remove holders recursively
2023-03-29 13:51 [PATCH v2 0/3] kmod: modprobe: Extend holders removal to multi-level dependencies Nicolas Schier
@ 2023-03-29 13:51 ` Nicolas Schier
2023-03-29 13:58 ` Nicolas Schier
2023-04-12 19:21 ` Lucas De Marchi
2023-03-29 13:51 ` [PATCH v2 2/3] testsuite: delete_module: Roughly implement fake-removal in sysfs tree Nicolas Schier
2023-03-29 13:51 ` [PATCH v2 3/3] testsuite: modprobe: Add test for --remove-holders Nicolas Schier
2 siblings, 2 replies; 9+ messages in thread
From: Nicolas Schier @ 2023-03-29 13:51 UTC (permalink / raw)
To: linux-modules; +Cc: Lucas De Marchi, Nicolas Schier
Remove holders recursively when removal of module holders is requested.
Extend commit 42b32d30c38e ("modprobe: Fix holders removal") by removing
also indirect holders if --remove-holders is set. For a simple module
dependency chain like
module3 depends on module2
module2 depends on module1
'modprobe -r --remove-holders module1' will remove module3, module2 and
module1 in correct order:
$ modprobe -r --remove-holders module1 --verbose
rmmod module3
rmmod module2
rmmod module1
(Actually, it will do the same when specifying module2 or module3 for
removal instead of module1.)
As a side-effect, 'modprobe -r module3' (w/o --remove-holders) will also
remove all three modules from the example above, as after removal of
module3, module2 does not have any holders and the same holds for
module1 after removal of module2:
$ modprobe -r module3 --verbose
rmmod module3
rmmod module2
rmmod module1
Without recursive evaluation of holders, modprobe leaves module1 loaded.
Unfortunately, '--dry-run --remove-holders' breaks with indirect
dependencies.
Signed-off-by: Nicolas Schier <n.schier@avm.de>
---
I am a bit unhappy about the introduction of the 'recursive' parameter
to rmmod_do_modlist() as it always holds the same value as
stop_on_errors; is re-using (and renaming) possibly a better option?
modprobe --remove --remove-holders --dry-run now ignores current
refcounts of loaded modules when simulating removal of holders.
Changes in v2:
* Handle modules that have just been removed, gently
* Fix --dry-run by ignoring module refcounts (_only_ for --dry-run)
* Add missing kmod_module_unref_list() calls
---
tools/modprobe.c | 44 +++++++++++++++++++++++++++++++++++---------
1 file changed, 35 insertions(+), 9 deletions(-)
diff --git a/tools/modprobe.c b/tools/modprobe.c
index 3b7897c..a705f88 100644
--- a/tools/modprobe.c
+++ b/tools/modprobe.c
@@ -390,13 +390,27 @@ static int rmmod_do_remove_module(struct kmod_module *mod)
static int rmmod_do_module(struct kmod_module *mod, int flags);
/* Remove modules in reverse order */
-static int rmmod_do_modlist(struct kmod_list *list, bool stop_on_errors)
+static int rmmod_do_modlist(struct kmod_list *list, bool stop_on_errors,
+ bool recursive)
{
struct kmod_list *l;
kmod_list_foreach_reverse(l, list) {
struct kmod_module *m = kmod_module_get_module(l);
- int r = rmmod_do_module(m, RMMOD_FLAG_IGNORE_BUILTIN);
+ int r = 0;
+
+ if (recursive && kmod_module_get_initstate(m) >= 0) {
+ struct kmod_list *holders = kmod_module_get_holders(m);
+
+ r = rmmod_do_modlist(holders, stop_on_errors,
+ recursive);
+
+ kmod_module_unref_list(holders);
+ }
+
+ if (!r)
+ r = rmmod_do_module(m, RMMOD_FLAG_IGNORE_BUILTIN);
+
kmod_module_unref(m);
if (r < 0 && stop_on_errors)
@@ -448,15 +462,17 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
}
/* 1. @mod's post-softdeps in reverse order */
- rmmod_do_modlist(post, false);
+ rmmod_do_modlist(post, false, false);
/* 2. Other modules holding @mod */
if (flags & RMMOD_FLAG_REMOVE_HOLDERS) {
struct kmod_list *holders = kmod_module_get_holders(mod);
- err = rmmod_do_modlist(holders, true);
+ err = rmmod_do_modlist(holders, true, true);
if (err < 0)
goto error;
+
+ kmod_module_unref_list(holders);
}
/* 3. @mod itself, but check for refcnt first */
@@ -472,9 +488,16 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
}
}
- if (!cmd)
- err = rmmod_do_remove_module(mod);
- else
+ if (!cmd) {
+ int state = kmod_module_get_initstate(mod);
+
+ if (state < 0) {
+ /* Module was removed during recursive holder removal */
+ err = 0;
+ } else {
+ err = rmmod_do_remove_module(mod);
+ }
+ } else
err = command_do(mod, "remove", cmd, NULL);
if (err < 0)
@@ -488,14 +511,14 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
kmod_list_foreach(itr, deps) {
struct kmod_module *dep = kmod_module_get_module(itr);
if (kmod_module_get_refcnt(dep) == 0)
- rmmod_do_remove_module(dep);
+ rmmod_do_module(dep, flags);
kmod_module_unref(dep);
}
kmod_module_unref_list(deps);
}
/* 5. @mod's pre-softdeps in reverse order: errors are non-fatal */
- rmmod_do_modlist(pre, false);
+ rmmod_do_modlist(pre, false, false);
error:
kmod_module_unref_list(pre);
@@ -975,6 +998,9 @@ static int do_modprobe(int argc, char **orig_argv)
fstat(fileno(stderr), &stat_buf)))
use_syslog = 1;
+ if (remove_holders && dry_run)
+ ignore_loaded = 1;
+
log_open(use_syslog);
if (!do_show_config) {
--
2.40.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 2/3] testsuite: delete_module: Roughly implement fake-removal in sysfs tree
2023-03-29 13:51 [PATCH v2 0/3] kmod: modprobe: Extend holders removal to multi-level dependencies Nicolas Schier
2023-03-29 13:51 ` [PATCH v2 1/3] kmod: modprobe: Remove holders recursively Nicolas Schier
@ 2023-03-29 13:51 ` Nicolas Schier
2023-04-12 20:13 ` Lucas De Marchi
2023-03-29 13:51 ` [PATCH v2 3/3] testsuite: modprobe: Add test for --remove-holders Nicolas Schier
2 siblings, 1 reply; 9+ messages in thread
From: Nicolas Schier @ 2023-03-29 13:51 UTC (permalink / raw)
To: linux-modules; +Cc: Lucas De Marchi, Nicolas Schier
Extend delete_module() to simulate module removal in the testsuite's
sysfs representation. During fake-removal, decrement refcnts on all
modules that have the to-be-removed module as holder, and unlink the
sysfs sub tree of the module itself.
Signed-off-by: Nicolas Schier <n.schier@avm.de>
---
Changes in v2:
* new patch
---
Makefile.am | 1 +
testsuite/delete_module.c | 262 +++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 261 insertions(+), 2 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 8ba85c9..9a87824 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -293,6 +293,7 @@ testsuite_path_la_CPPFLAGS = $(AM_CPPFLAGS) \
testsuite_path_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS)
testsuite_delete_module_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS)
+testsuite_delete_module_la_LIBADD = libkmod/libkmod-internal.la
testsuite_init_module_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS)
testsuite_init_module_la_SOURCES = testsuite/init_module.c \
testsuite/stripped-module.h
diff --git a/testsuite/delete_module.c b/testsuite/delete_module.c
index f3ae20b..73fc71a 100644
--- a/testsuite/delete_module.c
+++ b/testsuite/delete_module.c
@@ -31,6 +31,7 @@
#include <unistd.h>
#include <shared/util.h>
+#include <libkmod/libkmod.h>
#include "testsuite.h"
@@ -135,11 +136,252 @@ static void init_retcodes(void)
}
}
+static bool lstat_is_dir(const char *dir)
+{
+ struct stat st;
+
+ if (!lstat(dir, &st))
+ return S_ISDIR(st.st_mode);
+
+ ERR("TRAP delete_module(): %s: lstat: %s\n", dir, strerror(errno));
+ return false;
+}
+
+static int unlink_tree(const char *dir)
+{
+ struct dirent *dent;
+ char *new_path;
+ bool is_dir;
+ DIR *dirp;
+ int ret;
+
+ dirp = opendir(dir);
+ if (!dirp) {
+ ERR("TRAP delete_module(): %s: opendir: %s\n", dir,
+ strerror(errno));
+ return errno;
+ }
+
+ errno = ret = 0;
+ while (!ret && (dent = readdir(dirp))) {
+ if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
+ continue;
+
+ if (asprintf(&new_path, "%s/%s", dir, dent->d_name) < 0) {
+ ERR("TRAP delete_module(): unlink_tree: out-of-memory\n");
+ return ENOMEM;
+ }
+
+ if (dent->d_type == DT_UNKNOWN)
+ is_dir = lstat_is_dir(new_path);
+ else
+ is_dir = dent->d_type == DT_DIR;
+
+ if (is_dir)
+ ret = unlink_tree(new_path);
+ else {
+ if (unlink(new_path)) {
+ ret = errno;
+ ERR("TRAP delete_module(): %s: unlink: %s\n", new_path, strerror(errno));
+ }
+ }
+
+ free(new_path);
+ }
+
+ if (errno) {
+ if (!ret)
+ ret = errno;
+ ERR("TRAP delete_module(): %s: readdir: %s\n", dir, strerror(errno));
+ }
+
+ if (closedir(dirp)) {
+ if (!ret)
+ ret = errno;
+ ERR("TRAP delete_module(): %s: closedir: %s\n", dir, strerror(errno));
+ }
+
+ if (rmdir(dir)) {
+ if (ret)
+ ret = errno;
+ ERR("TRAP delete_module(): %s: rmdir: %s\n", dir, strerror(errno));
+ }
+
+ return ret;
+}
+
+static const char *rootfs_path(void)
+{
+ char *rootfs;
+
+ rootfs = getenv(S_TC_ROOTFS);
+ if (rootfs)
+ return rootfs;
+
+ ERR("TRAP delete_module(): missing export %s?\n",
+ S_TC_ROOTFS);
+ return NULL;
+}
+
+static char *sysfs_module_path_get(const char *module, const char *subpath)
+{
+ const char *rootfs = rootfs_path();
+ char *sysfs_path;
+ int ret;
+
+ if (!rootfs)
+ return NULL;
+
+ ret = asprintf(&sysfs_path, "%s/sys/module/%s%s",
+ rootfs, module, subpath ? subpath : "");
+ if (ret >= 0)
+ return sysfs_path;
+
+ ERR("TRAP delete_module(): %s: out-of-memory\n", subpath);
+ return NULL;
+}
+
+static int unlink_sysfs_module_tree(struct mod *mod)
+{
+ char *sysfs_path;
+ int ret;
+
+ if (!(sysfs_path = sysfs_module_path_get(mod->name, NULL)))
+ return EFAULT;
+
+ ret = unlink_tree(sysfs_path);
+ free(sysfs_path);
+
+ return ret;
+}
+
+static int sysfs_kmod_remove_holder(const struct kmod_module *kmod,
+ const char *holder)
+{
+ const char *name = kmod_module_get_name(kmod);
+ char *sysfs_mod_holders;
+ char *sysfs_mod_refcnt;
+ char refcnt_str[34];
+ char *sysfs_mod;
+ int holders_fd;
+ int refcnt;
+ int ret;
+ int fd;
+
+ if (!(sysfs_mod = sysfs_module_path_get(name, NULL)) ||
+ !(sysfs_mod_refcnt = sysfs_module_path_get(name, "/refcnt")) ||
+ !(sysfs_mod_holders = sysfs_module_path_get(name, "/holders"))) {
+ ERR("TRAP delete_module(): %s: out-of-memory\n", name);
+ return ENOMEM;
+ }
+
+ holders_fd = open(sysfs_mod_holders, O_RDONLY|O_CLOEXEC);
+ if (holders_fd < 0) {
+ ret = errno;
+ ERR("TRAP delete_module(): %s: open: %s\n", sysfs_mod_holders,
+ strerror(errno));
+ goto fail_free_pathnames;
+ }
+
+ if (unlinkat(holders_fd, holder, 0)) {
+ ret = errno;
+ ERR("TRAP delete_module(): %s/%s: unlink: %s\n",
+ sysfs_mod_holders, holder, strerror(ret));
+ goto fail_close_holders_fd;
+ }
+
+ refcnt = kmod_module_get_refcnt(kmod);
+ if (refcnt < 0) {
+ ret = errno;
+ ERR("TRAP delete_module(): %s: Invalid refcnt or error: %d\n",
+ name, refcnt);
+ return ret;
+ }
+
+ if (refcnt == 0) {
+ ERR("TRAP delete_module(): %s: refcnt already dropped to 0, refusing decrement\n",
+ name);
+ return EINVAL;
+ }
+
+ refcnt--;
+ snprintf(refcnt_str, sizeof(refcnt_str), "%d\n", refcnt);
+
+ fd = open(sysfs_mod_refcnt, O_WRONLY|O_TRUNC|O_CLOEXEC);
+ if (fd < 0) {
+ ret = errno;
+ ERR("TRAP delete_module(): %s: open: %s\n", sysfs_mod_refcnt,
+ strerror(ret));
+ goto fail_free_pathnames;
+ }
+
+ ret = write(fd, refcnt_str, strlen(refcnt_str));
+ if (ret <= 0) {
+ ret = errno;
+ ERR("TRAP delete_module(): %s: write: %s\n", sysfs_mod_refcnt,
+ strerror(ret));
+ }
+
+ close(fd);
+
+fail_close_holders_fd:
+ close(holders_fd);
+
+fail_free_pathnames:
+ free(sysfs_mod_holders);
+ free(sysfs_mod_refcnt);
+ free(sysfs_mod);
+
+ return ret;
+}
+
+static int decrement_holders_refcnt(struct mod *removed_mod)
+{
+ struct kmod_list *list, *itr;
+ struct kmod_ctx *ctx;
+ int err;
+
+ ctx = kmod_new(NULL, NULL);
+ if (ctx == NULL) {
+ ERR("TRAP delete_module(): Unable to get kmod ctx\n");
+ return EFAULT;
+ }
+
+ err = kmod_module_new_from_loaded(ctx, &list);
+ if (err < 0) {
+ ERR("TRAP delete_module(): Unable to get list of loaded modules: %s\n",
+ strerror(-err));
+ goto fail_free_ctx;
+ }
+
+ kmod_list_foreach(itr, list) {
+ struct kmod_module *kmod = kmod_module_get_module(itr);
+ struct kmod_list *holders, *hitr;
+
+ holders = kmod_module_get_holders(kmod);
+ kmod_list_foreach(hitr, holders) {
+ struct kmod_module *hm = kmod_module_get_module(hitr);
+ const char *holder_name = kmod_module_get_name(hm);
+
+ if (!strcmp(holder_name, removed_mod->name))
+ sysfs_kmod_remove_holder(kmod, holder_name);
+
+ kmod_module_unref(hm);
+ }
+ kmod_module_unref_list(holders);
+ kmod_module_unref(kmod);
+ }
+ kmod_module_unref_list(list);
+
+fail_free_ctx:
+ kmod_unref(ctx);
+
+ return -err;
+}
+
TS_EXPORT long delete_module(const char *name, unsigned int flags);
/*
- * FIXME: change /sys/module/<modname> to fake-remove a module
- *
* Default behavior is to exit successfully. If this is not the intended
* behavior, set TESTSUITE_DELETE_MODULE_RETCODES env var.
*/
@@ -152,6 +394,22 @@ long delete_module(const char *modname, unsigned int flags)
if (mod == NULL)
return 0;
+ if (!mod->ret) {
+ /* Adjust sysfs tree after successful removal */
+
+ errno = decrement_holders_refcnt(mod);
+ if (errno) {
+ ERR("TRAP delete_module(): unable to decrement sysfs holders\n");
+ return EFAULT;
+ }
+
+ errno = unlink_sysfs_module_tree(mod);
+ if (errno) {
+ ERR("TRAP delete_module(): unable to adjust sysfs tree\n");
+ return EFAULT;
+ }
+ }
+
errno = mod->errcode;
return mod->ret;
}
--
2.40.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 3/3] testsuite: modprobe: Add test for --remove-holders
2023-03-29 13:51 [PATCH v2 0/3] kmod: modprobe: Extend holders removal to multi-level dependencies Nicolas Schier
2023-03-29 13:51 ` [PATCH v2 1/3] kmod: modprobe: Remove holders recursively Nicolas Schier
2023-03-29 13:51 ` [PATCH v2 2/3] testsuite: delete_module: Roughly implement fake-removal in sysfs tree Nicolas Schier
@ 2023-03-29 13:51 ` Nicolas Schier
2023-04-12 20:20 ` Lucas De Marchi
2 siblings, 1 reply; 9+ messages in thread
From: Nicolas Schier @ 2023-03-29 13:51 UTC (permalink / raw)
To: linux-modules; +Cc: Lucas De Marchi, Nicolas Schier
Add a test for 'modprobe --remove --remove-holders' and a simple
dependency module chain of three modules.
Signed-off-by: Nicolas Schier <n.schier@avm.de>
---
Changes in v2:
* new patch
---
testsuite/module-playground/Makefile | 6 +++
testsuite/module-playground/mod-dep-chain-a.c | 21 ++++++++
testsuite/module-playground/mod-dep-chain-b.c | 22 +++++++++
testsuite/module-playground/mod-dep-chain-c.c | 22 +++++++++
testsuite/module-playground/mod-dep-chain.h | 8 +++
.../remove-holders/lib/modules/6.2/modules.alias | 1 +
.../lib/modules/6.2/modules.alias.bin | Bin 0 -> 12 bytes
.../lib/modules/6.2/modules.builtin.alias.bin | 0
.../lib/modules/6.2/modules.builtin.bin | 0
.../remove-holders/lib/modules/6.2/modules.dep | 3 ++
.../remove-holders/lib/modules/6.2/modules.dep.bin | Bin 0 -> 229 bytes
.../remove-holders/lib/modules/6.2/modules.devname | 0
.../remove-holders/lib/modules/6.2/modules.softdep | 1 +
.../remove-holders/lib/modules/6.2/modules.symbols | 4 ++
.../lib/modules/6.2/modules.symbols.bin | Bin 0 -> 143 bytes
.../test-modprobe/remove-holders/proc/modules | 3 ++
.../sys/module/mod_dep_chain_a/coresize | 1 +
.../module/mod_dep_chain_a/holders/mod_dep_chain_b | 1 +
.../sys/module/mod_dep_chain_a/initsize | 1 +
.../sys/module/mod_dep_chain_a/initstate | 1 +
.../sys/module/mod_dep_chain_a/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_a/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_a/refcnt | 1 +
.../sections/.gnu.linkonce.this_module | 1 +
.../sys/module/mod_dep_chain_a/sections/.init.data | 1 +
.../module/mod_dep_chain_a/sections/.note.Linux | 1 +
.../mod_dep_chain_a/sections/.note.gnu.build-id | 1 +
.../module/mod_dep_chain_a/sections/.orc_unwind | 1 +
.../module/mod_dep_chain_a/sections/.orc_unwind_ip | 1 +
.../module/mod_dep_chain_a/sections/.return_sites | 1 +
.../module/mod_dep_chain_a/sections/.rodata.str1.1 | 1 +
.../sys/module/mod_dep_chain_a/sections/.strtab | 1 +
.../sys/module/mod_dep_chain_a/sections/.symtab | 1 +
.../module/mod_dep_chain_a/sections/.text.unlikely | 1 +
.../sys/module/mod_dep_chain_a/sections/__kcrctab | 1 +
.../sys/module/mod_dep_chain_a/sections/__ksymtab | 1 +
.../mod_dep_chain_a/sections/__ksymtab_strings | 1 +
.../module/mod_dep_chain_a/sections/__mcount_loc | 1 +
.../sys/module/mod_dep_chain_a/taint | 1 +
.../sys/module/mod_dep_chain_b/coresize | 1 +
.../module/mod_dep_chain_b/holders/mod_dep_chain_c | 1 +
.../sys/module/mod_dep_chain_b/initsize | 1 +
.../sys/module/mod_dep_chain_b/initstate | 1 +
.../sys/module/mod_dep_chain_b/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_b/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_b/refcnt | 1 +
.../sections/.gnu.linkonce.this_module | 1 +
.../sys/module/mod_dep_chain_b/sections/.init.data | 1 +
.../module/mod_dep_chain_b/sections/.note.Linux | 1 +
.../mod_dep_chain_b/sections/.note.gnu.build-id | 1 +
.../module/mod_dep_chain_b/sections/.orc_unwind | 1 +
.../module/mod_dep_chain_b/sections/.orc_unwind_ip | 1 +
.../module/mod_dep_chain_b/sections/.return_sites | 1 +
.../module/mod_dep_chain_b/sections/.rodata.str1.8 | 1 +
.../sys/module/mod_dep_chain_b/sections/.strtab | 1 +
.../sys/module/mod_dep_chain_b/sections/.symtab | 1 +
.../module/mod_dep_chain_b/sections/.text.unlikely | 1 +
.../sys/module/mod_dep_chain_b/sections/__kcrctab | 1 +
.../sys/module/mod_dep_chain_b/sections/__ksymtab | 1 +
.../mod_dep_chain_b/sections/__ksymtab_strings | 1 +
.../module/mod_dep_chain_b/sections/__mcount_loc | 1 +
.../sys/module/mod_dep_chain_b/taint | 1 +
.../sys/module/mod_dep_chain_c/coresize | 1 +
.../sys/module/mod_dep_chain_c/holders/.gitignore | 0
.../sys/module/mod_dep_chain_c/initsize | 1 +
.../sys/module/mod_dep_chain_c/initstate | 1 +
.../sys/module/mod_dep_chain_c/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_c/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_c/refcnt | 1 +
.../sys/module/mod_dep_chain_c/taint | 1 +
.../remove-single/lib/modules/6.2/modules.alias | 1 +
.../lib/modules/6.2/modules.alias.bin | Bin 0 -> 12 bytes
.../lib/modules/6.2/modules.builtin.alias.bin | 0
.../lib/modules/6.2/modules.builtin.bin | 0
.../remove-single/lib/modules/6.2/modules.dep | 3 ++
.../remove-single/lib/modules/6.2/modules.dep.bin | Bin 0 -> 229 bytes
.../remove-single/lib/modules/6.2/modules.devname | 0
.../remove-single/lib/modules/6.2/modules.softdep | 1 +
.../remove-single/lib/modules/6.2/modules.symbols | 4 ++
.../lib/modules/6.2/modules.symbols.bin | Bin 0 -> 143 bytes
.../test-modprobe/remove-single/proc/modules | 3 ++
.../sys/module/mod_dep_chain_a/coresize | 1 +
.../module/mod_dep_chain_a/holders/mod_dep_chain_b | 1 +
.../sys/module/mod_dep_chain_a/initsize | 1 +
.../sys/module/mod_dep_chain_a/initstate | 1 +
.../sys/module/mod_dep_chain_a/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_a/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_a/refcnt | 1 +
.../sections/.gnu.linkonce.this_module | 1 +
.../sys/module/mod_dep_chain_a/sections/.init.data | 1 +
.../module/mod_dep_chain_a/sections/.note.Linux | 1 +
.../mod_dep_chain_a/sections/.note.gnu.build-id | 1 +
.../module/mod_dep_chain_a/sections/.orc_unwind | 1 +
.../module/mod_dep_chain_a/sections/.orc_unwind_ip | 1 +
.../module/mod_dep_chain_a/sections/.return_sites | 1 +
.../module/mod_dep_chain_a/sections/.rodata.str1.1 | 1 +
.../sys/module/mod_dep_chain_a/sections/.strtab | 1 +
.../sys/module/mod_dep_chain_a/sections/.symtab | 1 +
.../module/mod_dep_chain_a/sections/.text.unlikely | 1 +
.../sys/module/mod_dep_chain_a/sections/__kcrctab | 1 +
.../sys/module/mod_dep_chain_a/sections/__ksymtab | 1 +
.../mod_dep_chain_a/sections/__ksymtab_strings | 1 +
.../module/mod_dep_chain_a/sections/__mcount_loc | 1 +
.../remove-single/sys/module/mod_dep_chain_a/taint | 1 +
.../sys/module/mod_dep_chain_b/coresize | 1 +
.../module/mod_dep_chain_b/holders/mod_dep_chain_c | 1 +
.../sys/module/mod_dep_chain_b/initsize | 1 +
.../sys/module/mod_dep_chain_b/initstate | 1 +
.../sys/module/mod_dep_chain_b/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_b/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_b/refcnt | 1 +
.../sections/.gnu.linkonce.this_module | 1 +
.../sys/module/mod_dep_chain_b/sections/.init.data | 1 +
.../module/mod_dep_chain_b/sections/.note.Linux | 1 +
.../mod_dep_chain_b/sections/.note.gnu.build-id | 1 +
.../module/mod_dep_chain_b/sections/.orc_unwind | 1 +
.../module/mod_dep_chain_b/sections/.orc_unwind_ip | 1 +
.../module/mod_dep_chain_b/sections/.return_sites | 1 +
.../module/mod_dep_chain_b/sections/.rodata.str1.8 | 1 +
.../sys/module/mod_dep_chain_b/sections/.strtab | 1 +
.../sys/module/mod_dep_chain_b/sections/.symtab | 1 +
.../module/mod_dep_chain_b/sections/.text.unlikely | 1 +
.../sys/module/mod_dep_chain_b/sections/__kcrctab | 1 +
.../sys/module/mod_dep_chain_b/sections/__ksymtab | 1 +
.../mod_dep_chain_b/sections/__ksymtab_strings | 1 +
.../module/mod_dep_chain_b/sections/__mcount_loc | 1 +
.../remove-single/sys/module/mod_dep_chain_b/taint | 1 +
.../sys/module/mod_dep_chain_c/coresize | 1 +
.../sys/module/mod_dep_chain_c/holders/.gitignore | 0
.../sys/module/mod_dep_chain_c/initsize | 1 +
.../sys/module/mod_dep_chain_c/initstate | 1 +
.../sys/module/mod_dep_chain_c/notes/.note.Linux | Bin 0 -> 60 bytes
.../mod_dep_chain_c/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
.../sys/module/mod_dep_chain_c/refcnt | 1 +
.../remove-single/sys/module/mod_dep_chain_c/taint | 1 +
testsuite/setup-rootfs.sh | 3 ++
testsuite/test-modprobe.c | 55 +++++++++++++++++++++
137 files changed, 255 insertions(+)
diff --git a/testsuite/module-playground/Makefile b/testsuite/module-playground/Makefile
index e6045b0..970822b 100644
--- a/testsuite/module-playground/Makefile
+++ b/testsuite/module-playground/Makefile
@@ -37,6 +37,12 @@ obj-m += mod-fake-hpsa.o
obj-m += mod-fake-scsi-mod.o
obj-m += mod-fake-cciss.o
+# mod-dep-chain-*: simple dependency chain:
+# mod-dep-chain-c depends on mod-dep-chain-b, which depends on mod-dep-chain-a
+obj-m += mod-dep-chain-a.o
+obj-m += mod-dep-chain-b.o
+obj-m += mod-dep-chain-c.o
+
else
# only build ARCH-specific module
ifeq ($(ARCH),)
diff --git a/testsuite/module-playground/mod-dep-chain-a.c b/testsuite/module-playground/mod-dep-chain-a.c
new file mode 100644
index 0000000..a3dd896
--- /dev/null
+++ b/testsuite/module-playground/mod-dep-chain-a.c
@@ -0,0 +1,21 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include "mod-dep-chain.h"
+
+void mod_dep_chain_a_info(void)
+{
+ pr_info("loaded\n");
+}
+EXPORT_SYMBOL(mod_dep_chain_a_info);
+
+static int mod_dep_chain_a_init(void)
+{
+ mod_dep_chain_a_info();
+ return 0;
+}
+
+module_init(mod_dep_chain_a_init);
+
+MODULE_AUTHOR("Nicolas Schier <n.schier@avm.de>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-dep-chain-b.c b/testsuite/module-playground/mod-dep-chain-b.c
new file mode 100644
index 0000000..3d71d0f
--- /dev/null
+++ b/testsuite/module-playground/mod-dep-chain-b.c
@@ -0,0 +1,22 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include "mod-dep-chain.h"
+
+void mod_dep_chain_b_info(void)
+{
+ pr_info("loaded and depends on mod-dep-chain-a\n");
+ mod_dep_chain_a_info();
+}
+EXPORT_SYMBOL(mod_dep_chain_b_info);
+
+static int mod_dep_chain_b_init(void)
+{
+ mod_dep_chain_b_info();
+ return 0;
+}
+
+module_init(mod_dep_chain_b_init);
+
+MODULE_AUTHOR("Nicolas Schier <n.schier@avm.de>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-dep-chain-c.c b/testsuite/module-playground/mod-dep-chain-c.c
new file mode 100644
index 0000000..862ce84
--- /dev/null
+++ b/testsuite/module-playground/mod-dep-chain-c.c
@@ -0,0 +1,22 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include "mod-dep-chain.h"
+
+void mod_dep_chain_c_info(void)
+{
+ pr_info("loaded and depends on mod-dep-chain-b\n");
+ mod_dep_chain_b_info();
+}
+EXPORT_SYMBOL(mod_dep_chain_c_info);
+
+static int mod_dep_chain_c_init(void)
+{
+ mod_dep_chain_c_info();
+ return 0;
+}
+
+module_init(mod_dep_chain_c_init);
+
+MODULE_AUTHOR("Nicolas Schier <n.schier@avm.de>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-dep-chain.h b/testsuite/module-playground/mod-dep-chain.h
new file mode 100644
index 0000000..0bfc26f
--- /dev/null
+++ b/testsuite/module-playground/mod-dep-chain.h
@@ -0,0 +1,8 @@
+#ifndef __mod_dep_chain_h__
+#define __mod_dep_chain_h__
+
+extern void mod_dep_chain_a_info(void);
+extern void mod_dep_chain_b_info(void);
+extern void mod_dep_chain_c_info(void);
+
+#endif
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.alias b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.alias.bin
new file mode 100644
index 0000000..7075435
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.alias.bin differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.builtin.alias.bin b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.builtin.alias.bin
new file mode 100644
index 0000000..e69de29
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.dep b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.dep
new file mode 100644
index 0000000..8248cf7
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.dep
@@ -0,0 +1,3 @@
+kernel/mod-dep-chain-a.ko:
+kernel/mod-dep-chain-b.ko: kernel/mod-dep-chain-a.ko
+kernel/mod-dep-chain-c.ko: kernel/mod-dep-chain-b.ko kernel/mod-dep-chain-a.ko
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.dep.bin
new file mode 100644
index 0000000..f8b999d
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.dep.bin differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.devname b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.devname
new file mode 100644
index 0000000..e69de29
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.symbols
new file mode 100644
index 0000000..84263a6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.symbols
@@ -0,0 +1,4 @@
+# Aliases for symbols, used by symbol_request().
+alias symbol:mod_dep_chain_a_info mod_dep_chain_a
+alias symbol:mod_dep_chain_c_info mod_dep_chain_c
+alias symbol:mod_dep_chain_b_info mod_dep_chain_b
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.symbols.bin
new file mode 100644
index 0000000..b246e9d
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-holders/lib/modules/6.2/modules.symbols.bin differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/proc/modules b/testsuite/rootfs-pristine/test-modprobe/remove-holders/proc/modules
new file mode 100644
index 0000000..5929acb
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/proc/modules
@@ -0,0 +1,3 @@
+mod_dep_chain_c 16384 0 [permanent], Live 0x0000000000000000 (POE)
+mod_dep_chain_b 16384 1 mod_dep_chain_c,[permanent], Live 0x0000000000000000 (POE)
+mod_dep_chain_a 16384 1 mod_dep_chain_b,[permanent], Live 0x0000000000000000 (POE)
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/coresize b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/coresize
new file mode 100644
index 0000000..08558e4
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/coresize
@@ -0,0 +1 @@
+16384
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/holders/mod_dep_chain_b b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/holders/mod_dep_chain_b
new file mode 120000
index 0000000..e26fe25
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/holders/mod_dep_chain_b
@@ -0,0 +1 @@
+../../mod_dep_chain_b
\ No newline at end of file
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/initsize b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/initsize
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/initsize
@@ -0,0 +1 @@
+0
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/initstate b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/initstate
new file mode 100644
index 0000000..e23fe64
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/initstate
@@ -0,0 +1 @@
+live
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/notes/.note.Linux b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/notes/.note.Linux
new file mode 100644
index 0000000..183aed9
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/notes/.note.Linux differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/notes/.note.gnu.build-id b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/notes/.note.gnu.build-id
new file mode 100644
index 0000000..fcfdc7f
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/notes/.note.gnu.build-id differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/refcnt b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/refcnt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/refcnt
@@ -0,0 +1 @@
+1
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.gnu.linkonce.this_module b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.gnu.linkonce.this_module
new file mode 100644
index 0000000..bcc5ba6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.gnu.linkonce.this_module
@@ -0,0 +1 @@
+0xffffffffc10fa000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.init.data b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.init.data
new file mode 100644
index 0000000..de75dee
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.init.data
@@ -0,0 +1 @@
+0xffffffffc034d000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.note.Linux b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.note.Linux
new file mode 100644
index 0000000..c8513b9
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.note.Linux
@@ -0,0 +1 @@
+0xffffffffc10f9024
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.note.gnu.build-id b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.note.gnu.build-id
new file mode 100644
index 0000000..8cbe085
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.note.gnu.build-id
@@ -0,0 +1 @@
+0xffffffffc10f9000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.orc_unwind b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.orc_unwind
new file mode 100644
index 0000000..7987d4e
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.orc_unwind
@@ -0,0 +1 @@
+0xffffffffc10f90b5
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.orc_unwind_ip b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.orc_unwind_ip
new file mode 100644
index 0000000..12cda4f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.orc_unwind_ip
@@ -0,0 +1 @@
+0xffffffffc10f90c1
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.return_sites b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.return_sites
new file mode 100644
index 0000000..cff4d30
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.return_sites
@@ -0,0 +1 @@
+0xffffffffc10f90b1
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.rodata.str1.1 b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.rodata.str1.1
new file mode 100644
index 0000000..a670b59
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.rodata.str1.1
@@ -0,0 +1 @@
+0xffffffffc10f9086
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.strtab b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.strtab
new file mode 100644
index 0000000..2905428
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.strtab
@@ -0,0 +1 @@
+0xffffffffc034e4e0
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.symtab b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.symtab
new file mode 100644
index 0000000..38b02ed
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.symtab
@@ -0,0 +1 @@
+0xffffffffc034e000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.text.unlikely b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.text.unlikely
new file mode 100644
index 0000000..0f8c361
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/.text.unlikely
@@ -0,0 +1 @@
+0xffffffffc10f8000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__kcrctab b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__kcrctab
new file mode 100644
index 0000000..83cf0eb
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__kcrctab
@@ -0,0 +1 @@
+0xffffffffc10f906c
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__ksymtab b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__ksymtab
new file mode 100644
index 0000000..8a160a3
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__ksymtab
@@ -0,0 +1 @@
+0xffffffffc10f9060
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__ksymtab_strings b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__ksymtab_strings
new file mode 100644
index 0000000..ed9d78d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__ksymtab_strings
@@ -0,0 +1 @@
+0xffffffffc10f9070
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__mcount_loc b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__mcount_loc
new file mode 100644
index 0000000..b6ddb2f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/sections/__mcount_loc
@@ -0,0 +1 @@
+0xffffffffc10f90a1
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/taint b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/taint
new file mode 100644
index 0000000..fed7ca6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_a/taint
@@ -0,0 +1 @@
+POE
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/coresize b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/coresize
new file mode 100644
index 0000000..08558e4
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/coresize
@@ -0,0 +1 @@
+16384
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/holders/mod_dep_chain_c b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/holders/mod_dep_chain_c
new file mode 120000
index 0000000..9100cb1
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/holders/mod_dep_chain_c
@@ -0,0 +1 @@
+../../mod_dep_chain_c
\ No newline at end of file
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/initsize b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/initsize
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/initsize
@@ -0,0 +1 @@
+0
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/initstate b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/initstate
new file mode 100644
index 0000000..e23fe64
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/initstate
@@ -0,0 +1 @@
+live
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/notes/.note.Linux b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/notes/.note.Linux
new file mode 100644
index 0000000..183aed9
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/notes/.note.Linux differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/notes/.note.gnu.build-id b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/notes/.note.gnu.build-id
new file mode 100644
index 0000000..673a782
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/notes/.note.gnu.build-id differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/refcnt b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/refcnt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/refcnt
@@ -0,0 +1 @@
+1
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.gnu.linkonce.this_module b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.gnu.linkonce.this_module
new file mode 100644
index 0000000..8048b8f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.gnu.linkonce.this_module
@@ -0,0 +1 @@
+0xffffffffc10ff000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.init.data b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.init.data
new file mode 100644
index 0000000..de75dee
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.init.data
@@ -0,0 +1 @@
+0xffffffffc034d000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.note.Linux b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.note.Linux
new file mode 100644
index 0000000..b451fc5
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.note.Linux
@@ -0,0 +1 @@
+0xffffffffc10fe024
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.note.gnu.build-id b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.note.gnu.build-id
new file mode 100644
index 0000000..b10979d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.note.gnu.build-id
@@ -0,0 +1 @@
+0xffffffffc10fe000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.orc_unwind b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.orc_unwind
new file mode 100644
index 0000000..5dc94f4
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.orc_unwind
@@ -0,0 +1 @@
+0xffffffffc10fe0d6
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.orc_unwind_ip b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.orc_unwind_ip
new file mode 100644
index 0000000..4dd4220
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.orc_unwind_ip
@@ -0,0 +1 @@
+0xffffffffc10fe0e2
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.return_sites b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.return_sites
new file mode 100644
index 0000000..74defae
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.return_sites
@@ -0,0 +1 @@
+0xffffffffc10fe0d2
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.rodata.str1.8 b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.rodata.str1.8
new file mode 100644
index 0000000..18b3917
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.rodata.str1.8
@@ -0,0 +1 @@
+0xffffffffc10fe088
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.strtab b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.strtab
new file mode 100644
index 0000000..a51ce15
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.strtab
@@ -0,0 +1 @@
+0xffffffffc034e4f8
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.symtab b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.symtab
new file mode 100644
index 0000000..38b02ed
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.symtab
@@ -0,0 +1 @@
+0xffffffffc034e000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.text.unlikely b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.text.unlikely
new file mode 100644
index 0000000..88cfba0
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/.text.unlikely
@@ -0,0 +1 @@
+0xffffffffc10fd000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__kcrctab b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__kcrctab
new file mode 100644
index 0000000..bbe2928
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__kcrctab
@@ -0,0 +1 @@
+0xffffffffc10fe06c
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__ksymtab b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__ksymtab
new file mode 100644
index 0000000..b48b59d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__ksymtab
@@ -0,0 +1 @@
+0xffffffffc10fe060
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__ksymtab_strings b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__ksymtab_strings
new file mode 100644
index 0000000..585eebd
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__ksymtab_strings
@@ -0,0 +1 @@
+0xffffffffc10fe070
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__mcount_loc b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__mcount_loc
new file mode 100644
index 0000000..fe363ee
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/sections/__mcount_loc
@@ -0,0 +1 @@
+0xffffffffc10fe0c2
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/taint b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/taint
new file mode 100644
index 0000000..fed7ca6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_b/taint
@@ -0,0 +1 @@
+POE
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/coresize b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/coresize
new file mode 100644
index 0000000..08558e4
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/coresize
@@ -0,0 +1 @@
+16384
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/holders/.gitignore b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/holders/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/initsize b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/initsize
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/initsize
@@ -0,0 +1 @@
+0
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/initstate b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/initstate
new file mode 100644
index 0000000..e23fe64
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/initstate
@@ -0,0 +1 @@
+live
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/notes/.note.Linux b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/notes/.note.Linux
new file mode 100644
index 0000000..183aed9
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/notes/.note.Linux differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/notes/.note.gnu.build-id b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/notes/.note.gnu.build-id
new file mode 100644
index 0000000..50a9201
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/notes/.note.gnu.build-id differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/refcnt b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/refcnt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/refcnt
@@ -0,0 +1 @@
+0
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/taint b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/taint
new file mode 100644
index 0000000..fed7ca6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-holders/sys/module/mod_dep_chain_c/taint
@@ -0,0 +1 @@
+POE
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.alias b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.alias.bin
new file mode 100644
index 0000000..7075435
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.alias.bin differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.builtin.alias.bin b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.builtin.alias.bin
new file mode 100644
index 0000000..e69de29
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.dep b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.dep
new file mode 100644
index 0000000..8248cf7
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.dep
@@ -0,0 +1,3 @@
+kernel/mod-dep-chain-a.ko:
+kernel/mod-dep-chain-b.ko: kernel/mod-dep-chain-a.ko
+kernel/mod-dep-chain-c.ko: kernel/mod-dep-chain-b.ko kernel/mod-dep-chain-a.ko
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.dep.bin
new file mode 100644
index 0000000..f8b999d
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.dep.bin differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.devname b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.devname
new file mode 100644
index 0000000..e69de29
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.symbols
new file mode 100644
index 0000000..84263a6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.symbols
@@ -0,0 +1,4 @@
+# Aliases for symbols, used by symbol_request().
+alias symbol:mod_dep_chain_a_info mod_dep_chain_a
+alias symbol:mod_dep_chain_c_info mod_dep_chain_c
+alias symbol:mod_dep_chain_b_info mod_dep_chain_b
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.symbols.bin
new file mode 100644
index 0000000..b246e9d
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-single/lib/modules/6.2/modules.symbols.bin differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/proc/modules b/testsuite/rootfs-pristine/test-modprobe/remove-single/proc/modules
new file mode 100644
index 0000000..5929acb
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/proc/modules
@@ -0,0 +1,3 @@
+mod_dep_chain_c 16384 0 [permanent], Live 0x0000000000000000 (POE)
+mod_dep_chain_b 16384 1 mod_dep_chain_c,[permanent], Live 0x0000000000000000 (POE)
+mod_dep_chain_a 16384 1 mod_dep_chain_b,[permanent], Live 0x0000000000000000 (POE)
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/coresize b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/coresize
new file mode 100644
index 0000000..08558e4
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/coresize
@@ -0,0 +1 @@
+16384
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/holders/mod_dep_chain_b b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/holders/mod_dep_chain_b
new file mode 120000
index 0000000..e26fe25
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/holders/mod_dep_chain_b
@@ -0,0 +1 @@
+../../mod_dep_chain_b
\ No newline at end of file
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/initsize b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/initsize
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/initsize
@@ -0,0 +1 @@
+0
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/initstate b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/initstate
new file mode 100644
index 0000000..e23fe64
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/initstate
@@ -0,0 +1 @@
+live
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/notes/.note.Linux b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/notes/.note.Linux
new file mode 100644
index 0000000..183aed9
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/notes/.note.Linux differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/notes/.note.gnu.build-id b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/notes/.note.gnu.build-id
new file mode 100644
index 0000000..fcfdc7f
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/notes/.note.gnu.build-id differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/refcnt b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/refcnt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/refcnt
@@ -0,0 +1 @@
+1
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.gnu.linkonce.this_module b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.gnu.linkonce.this_module
new file mode 100644
index 0000000..bcc5ba6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.gnu.linkonce.this_module
@@ -0,0 +1 @@
+0xffffffffc10fa000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.init.data b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.init.data
new file mode 100644
index 0000000..de75dee
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.init.data
@@ -0,0 +1 @@
+0xffffffffc034d000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.note.Linux b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.note.Linux
new file mode 100644
index 0000000..c8513b9
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.note.Linux
@@ -0,0 +1 @@
+0xffffffffc10f9024
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.note.gnu.build-id b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.note.gnu.build-id
new file mode 100644
index 0000000..8cbe085
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.note.gnu.build-id
@@ -0,0 +1 @@
+0xffffffffc10f9000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.orc_unwind b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.orc_unwind
new file mode 100644
index 0000000..7987d4e
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.orc_unwind
@@ -0,0 +1 @@
+0xffffffffc10f90b5
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.orc_unwind_ip b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.orc_unwind_ip
new file mode 100644
index 0000000..12cda4f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.orc_unwind_ip
@@ -0,0 +1 @@
+0xffffffffc10f90c1
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.return_sites b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.return_sites
new file mode 100644
index 0000000..cff4d30
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.return_sites
@@ -0,0 +1 @@
+0xffffffffc10f90b1
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.rodata.str1.1 b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.rodata.str1.1
new file mode 100644
index 0000000..a670b59
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.rodata.str1.1
@@ -0,0 +1 @@
+0xffffffffc10f9086
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.strtab b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.strtab
new file mode 100644
index 0000000..2905428
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.strtab
@@ -0,0 +1 @@
+0xffffffffc034e4e0
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.symtab b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.symtab
new file mode 100644
index 0000000..38b02ed
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.symtab
@@ -0,0 +1 @@
+0xffffffffc034e000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.text.unlikely b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.text.unlikely
new file mode 100644
index 0000000..0f8c361
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/.text.unlikely
@@ -0,0 +1 @@
+0xffffffffc10f8000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__kcrctab b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__kcrctab
new file mode 100644
index 0000000..83cf0eb
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__kcrctab
@@ -0,0 +1 @@
+0xffffffffc10f906c
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__ksymtab b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__ksymtab
new file mode 100644
index 0000000..8a160a3
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__ksymtab
@@ -0,0 +1 @@
+0xffffffffc10f9060
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__ksymtab_strings b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__ksymtab_strings
new file mode 100644
index 0000000..ed9d78d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__ksymtab_strings
@@ -0,0 +1 @@
+0xffffffffc10f9070
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__mcount_loc b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__mcount_loc
new file mode 100644
index 0000000..b6ddb2f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/sections/__mcount_loc
@@ -0,0 +1 @@
+0xffffffffc10f90a1
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/taint b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/taint
new file mode 100644
index 0000000..fed7ca6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_a/taint
@@ -0,0 +1 @@
+POE
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/coresize b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/coresize
new file mode 100644
index 0000000..08558e4
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/coresize
@@ -0,0 +1 @@
+16384
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/holders/mod_dep_chain_c b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/holders/mod_dep_chain_c
new file mode 120000
index 0000000..9100cb1
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/holders/mod_dep_chain_c
@@ -0,0 +1 @@
+../../mod_dep_chain_c
\ No newline at end of file
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/initsize b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/initsize
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/initsize
@@ -0,0 +1 @@
+0
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/initstate b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/initstate
new file mode 100644
index 0000000..e23fe64
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/initstate
@@ -0,0 +1 @@
+live
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/notes/.note.Linux b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/notes/.note.Linux
new file mode 100644
index 0000000..183aed9
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/notes/.note.Linux differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/notes/.note.gnu.build-id b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/notes/.note.gnu.build-id
new file mode 100644
index 0000000..673a782
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/notes/.note.gnu.build-id differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/refcnt b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/refcnt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/refcnt
@@ -0,0 +1 @@
+1
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.gnu.linkonce.this_module b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.gnu.linkonce.this_module
new file mode 100644
index 0000000..8048b8f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.gnu.linkonce.this_module
@@ -0,0 +1 @@
+0xffffffffc10ff000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.init.data b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.init.data
new file mode 100644
index 0000000..de75dee
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.init.data
@@ -0,0 +1 @@
+0xffffffffc034d000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.note.Linux b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.note.Linux
new file mode 100644
index 0000000..b451fc5
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.note.Linux
@@ -0,0 +1 @@
+0xffffffffc10fe024
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.note.gnu.build-id b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.note.gnu.build-id
new file mode 100644
index 0000000..b10979d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.note.gnu.build-id
@@ -0,0 +1 @@
+0xffffffffc10fe000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.orc_unwind b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.orc_unwind
new file mode 100644
index 0000000..5dc94f4
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.orc_unwind
@@ -0,0 +1 @@
+0xffffffffc10fe0d6
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.orc_unwind_ip b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.orc_unwind_ip
new file mode 100644
index 0000000..4dd4220
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.orc_unwind_ip
@@ -0,0 +1 @@
+0xffffffffc10fe0e2
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.return_sites b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.return_sites
new file mode 100644
index 0000000..74defae
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.return_sites
@@ -0,0 +1 @@
+0xffffffffc10fe0d2
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.rodata.str1.8 b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.rodata.str1.8
new file mode 100644
index 0000000..18b3917
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.rodata.str1.8
@@ -0,0 +1 @@
+0xffffffffc10fe088
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.strtab b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.strtab
new file mode 100644
index 0000000..a51ce15
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.strtab
@@ -0,0 +1 @@
+0xffffffffc034e4f8
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.symtab b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.symtab
new file mode 100644
index 0000000..38b02ed
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.symtab
@@ -0,0 +1 @@
+0xffffffffc034e000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.text.unlikely b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.text.unlikely
new file mode 100644
index 0000000..88cfba0
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/.text.unlikely
@@ -0,0 +1 @@
+0xffffffffc10fd000
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__kcrctab b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__kcrctab
new file mode 100644
index 0000000..bbe2928
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__kcrctab
@@ -0,0 +1 @@
+0xffffffffc10fe06c
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__ksymtab b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__ksymtab
new file mode 100644
index 0000000..b48b59d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__ksymtab
@@ -0,0 +1 @@
+0xffffffffc10fe060
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__ksymtab_strings b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__ksymtab_strings
new file mode 100644
index 0000000..585eebd
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__ksymtab_strings
@@ -0,0 +1 @@
+0xffffffffc10fe070
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__mcount_loc b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__mcount_loc
new file mode 100644
index 0000000..fe363ee
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/sections/__mcount_loc
@@ -0,0 +1 @@
+0xffffffffc10fe0c2
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/taint b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/taint
new file mode 100644
index 0000000..fed7ca6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_b/taint
@@ -0,0 +1 @@
+POE
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/coresize b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/coresize
new file mode 100644
index 0000000..08558e4
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/coresize
@@ -0,0 +1 @@
+16384
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/holders/.gitignore b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/holders/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/initsize b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/initsize
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/initsize
@@ -0,0 +1 @@
+0
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/initstate b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/initstate
new file mode 100644
index 0000000..e23fe64
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/initstate
@@ -0,0 +1 @@
+live
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/notes/.note.Linux b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/notes/.note.Linux
new file mode 100644
index 0000000..183aed9
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/notes/.note.Linux differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/notes/.note.gnu.build-id b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/notes/.note.gnu.build-id
new file mode 100644
index 0000000..50a9201
Binary files /dev/null and b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/notes/.note.gnu.build-id differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/refcnt b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/refcnt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/refcnt
@@ -0,0 +1 @@
+0
diff --git a/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/taint b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/taint
new file mode 100644
index 0000000..fed7ca6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/remove-single/sys/module/mod_dep_chain_c/taint
@@ -0,0 +1 @@
+POE
diff --git a/testsuite/setup-rootfs.sh b/testsuite/setup-rootfs.sh
index 4440ddc..94bfdcf 100755
--- a/testsuite/setup-rootfs.sh
+++ b/testsuite/setup-rootfs.sh
@@ -77,6 +77,9 @@ map=(
["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-modprobe/remove-holders/lib/modules/6.2/kernel/mod-dep-chain-a.ko"]="mod-dep-chain-a.ko"
+ ["test-modprobe/remove-holders/lib/modules/6.2/kernel/mod-dep-chain-b.ko"]="mod-dep-chain-b.ko"
+ ["test-modprobe/remove-holders/lib/modules/6.2/kernel/mod-dep-chain-c.ko"]="mod-dep-chain-c.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"
diff --git a/testsuite/test-modprobe.c b/testsuite/test-modprobe.c
index 309f3e3..20413db 100644
--- a/testsuite/test-modprobe.c
+++ b/testsuite/test-modprobe.c
@@ -466,4 +466,59 @@ DEFINE_TEST(modprobe_module_from_relpath,
.modules_loaded = "mod-simple",
);
+static noreturn int modprobe_remove_single(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "--remove",
+ "--verbose",
+ "mod-dep-chain-c",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+
+ exit(EXIT_FAILURE);
+}
+
+DEFINE_TEST(modprobe_remove_single,
+ .description = "check if modprobe removes a single module but no holders",
+ .config = {
+ [TC_UNAME_R] = "6.2",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/remove-single",
+ [TC_DELETE_MODULE_RETCODES] = "mod_dep_chain_c:0:0",
+ },
+ .modules_loaded = "mod-dep-chain-a,mod-dep-chain-b",
+ .need_spawn = true,
+ );
+
+static noreturn int modprobe_remove_with_holders(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "--remove",
+ "--remove-holders",
+ "--verbose",
+ "mod-dep-chain-a",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+
+ exit(EXIT_FAILURE);
+}
+
+DEFINE_TEST(modprobe_remove_with_holders,
+ .description = "check if modprobe removes also indirect holders",
+ .config = {
+ [TC_UNAME_R] = "6.2",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/remove-holders",
+ [TC_DELETE_MODULE_RETCODES] = "mod_dep_chain_a:0:0:mod_dep_chain_b:0:0:mod_dep_chain_c:0:0",
+ },
+ .modules_loaded = "",
+ .need_spawn = true,
+ );
+
TESTSUITE_MAIN();
--
2.40.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/3] kmod: modprobe: Remove holders recursively
2023-03-29 13:51 ` [PATCH v2 1/3] kmod: modprobe: Remove holders recursively Nicolas Schier
@ 2023-03-29 13:58 ` Nicolas Schier
2023-04-12 19:21 ` Lucas De Marchi
1 sibling, 0 replies; 9+ messages in thread
From: Nicolas Schier @ 2023-03-29 13:58 UTC (permalink / raw)
To: Nicolas Schier; +Cc: linux-modules, Lucas De Marchi
On Wed, Mar 29, 2023 at 03:51:35PM +0200 Nicolas Schier wrote:
> Remove holders recursively when removal of module holders is requested.
>
> Extend commit 42b32d30c38e ("modprobe: Fix holders removal") by removing
> also indirect holders if --remove-holders is set. For a simple module
> dependency chain like
>
> module3 depends on module2
> module2 depends on module1
>
> 'modprobe -r --remove-holders module1' will remove module3, module2 and
> module1 in correct order:
>
> $ modprobe -r --remove-holders module1 --verbose
> rmmod module3
> rmmod module2
> rmmod module1
>
> (Actually, it will do the same when specifying module2 or module3 for
> removal instead of module1.)
>
> As a side-effect, 'modprobe -r module3' (w/o --remove-holders) will also
> remove all three modules from the example above, as after removal of
> module3, module2 does not have any holders and the same holds for
> module1 after removal of module2:
>
> $ modprobe -r module3 --verbose
> rmmod module3
> rmmod module2
> rmmod module1
>
> Without recursive evaluation of holders, modprobe leaves module1 loaded.
>
> Unfortunately, '--dry-run --remove-holders' breaks with indirect
> dependencies.
Ups. This is not true anymore, I forgot to remove this sentence from the
commit message.
Kind regards,
Nicolas
>
> Signed-off-by: Nicolas Schier <n.schier@avm.de>
> ---
> I am a bit unhappy about the introduction of the 'recursive' parameter
> to rmmod_do_modlist() as it always holds the same value as
> stop_on_errors; is re-using (and renaming) possibly a better option?
>
> modprobe --remove --remove-holders --dry-run now ignores current
> refcounts of loaded modules when simulating removal of holders.
>
> Changes in v2:
> * Handle modules that have just been removed, gently
> * Fix --dry-run by ignoring module refcounts (_only_ for --dry-run)
> * Add missing kmod_module_unref_list() calls
> ---
> tools/modprobe.c | 44 +++++++++++++++++++++++++++++++++++---------
> 1 file changed, 35 insertions(+), 9 deletions(-)
>
> diff --git a/tools/modprobe.c b/tools/modprobe.c
> index 3b7897c..a705f88 100644
> --- a/tools/modprobe.c
> +++ b/tools/modprobe.c
> @@ -390,13 +390,27 @@ static int rmmod_do_remove_module(struct kmod_module *mod)
> static int rmmod_do_module(struct kmod_module *mod, int flags);
>
> /* Remove modules in reverse order */
> -static int rmmod_do_modlist(struct kmod_list *list, bool stop_on_errors)
> +static int rmmod_do_modlist(struct kmod_list *list, bool stop_on_errors,
> + bool recursive)
> {
> struct kmod_list *l;
>
> kmod_list_foreach_reverse(l, list) {
> struct kmod_module *m = kmod_module_get_module(l);
> - int r = rmmod_do_module(m, RMMOD_FLAG_IGNORE_BUILTIN);
> + int r = 0;
> +
> + if (recursive && kmod_module_get_initstate(m) >= 0) {
> + struct kmod_list *holders = kmod_module_get_holders(m);
> +
> + r = rmmod_do_modlist(holders, stop_on_errors,
> + recursive);
> +
> + kmod_module_unref_list(holders);
> + }
> +
> + if (!r)
> + r = rmmod_do_module(m, RMMOD_FLAG_IGNORE_BUILTIN);
> +
> kmod_module_unref(m);
>
> if (r < 0 && stop_on_errors)
> @@ -448,15 +462,17 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
> }
>
> /* 1. @mod's post-softdeps in reverse order */
> - rmmod_do_modlist(post, false);
> + rmmod_do_modlist(post, false, false);
>
> /* 2. Other modules holding @mod */
> if (flags & RMMOD_FLAG_REMOVE_HOLDERS) {
> struct kmod_list *holders = kmod_module_get_holders(mod);
>
> - err = rmmod_do_modlist(holders, true);
> + err = rmmod_do_modlist(holders, true, true);
> if (err < 0)
> goto error;
> +
> + kmod_module_unref_list(holders);
> }
>
> /* 3. @mod itself, but check for refcnt first */
> @@ -472,9 +488,16 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
> }
> }
>
> - if (!cmd)
> - err = rmmod_do_remove_module(mod);
> - else
> + if (!cmd) {
> + int state = kmod_module_get_initstate(mod);
> +
> + if (state < 0) {
> + /* Module was removed during recursive holder removal */
> + err = 0;
> + } else {
> + err = rmmod_do_remove_module(mod);
> + }
> + } else
> err = command_do(mod, "remove", cmd, NULL);
>
> if (err < 0)
> @@ -488,14 +511,14 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
> kmod_list_foreach(itr, deps) {
> struct kmod_module *dep = kmod_module_get_module(itr);
> if (kmod_module_get_refcnt(dep) == 0)
> - rmmod_do_remove_module(dep);
> + rmmod_do_module(dep, flags);
> kmod_module_unref(dep);
> }
> kmod_module_unref_list(deps);
> }
>
> /* 5. @mod's pre-softdeps in reverse order: errors are non-fatal */
> - rmmod_do_modlist(pre, false);
> + rmmod_do_modlist(pre, false, false);
>
> error:
> kmod_module_unref_list(pre);
> @@ -975,6 +998,9 @@ static int do_modprobe(int argc, char **orig_argv)
> fstat(fileno(stderr), &stat_buf)))
> use_syslog = 1;
>
> + if (remove_holders && dry_run)
> + ignore_loaded = 1;
> +
> log_open(use_syslog);
>
> if (!do_show_config) {
>
> --
> 2.40.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/3] kmod: modprobe: Remove holders recursively
2023-03-29 13:51 ` [PATCH v2 1/3] kmod: modprobe: Remove holders recursively Nicolas Schier
2023-03-29 13:58 ` Nicolas Schier
@ 2023-04-12 19:21 ` Lucas De Marchi
2023-04-18 10:10 ` Nicolas Schier
1 sibling, 1 reply; 9+ messages in thread
From: Lucas De Marchi @ 2023-04-12 19:21 UTC (permalink / raw)
To: Nicolas Schier; +Cc: linux-modules, Lucas De Marchi, Nicolas Schier
On Wed, Mar 29, 2023 at 03:51:35PM +0200, Nicolas Schier wrote:
>Remove holders recursively when removal of module holders is requested.
>
>Extend commit 42b32d30c38e ("modprobe: Fix holders removal") by removing
>also indirect holders if --remove-holders is set. For a simple module
>dependency chain like
>
> module3 depends on module2
> module2 depends on module1
>
>'modprobe -r --remove-holders module1' will remove module3, module2 and
>module1 in correct order:
>
> $ modprobe -r --remove-holders module1 --verbose
> rmmod module3
> rmmod module2
> rmmod module1
>
>(Actually, it will do the same when specifying module2 or module3 for
>removal instead of module1.)
>
>As a side-effect, 'modprobe -r module3' (w/o --remove-holders) will also
>remove all three modules from the example above, as after removal of
>module3, module2 does not have any holders and the same holds for
>module1 after removal of module2:
>
> $ modprobe -r module3 --verbose
> rmmod module3
> rmmod module2
> rmmod module1
>
>Without recursive evaluation of holders, modprobe leaves module1 loaded.
>
>Unfortunately, '--dry-run --remove-holders' breaks with indirect
>dependencies.
>
>Signed-off-by: Nicolas Schier <n.schier@avm.de>
>---
>I am a bit unhappy about the introduction of the 'recursive' parameter
yeah... it makes it slightly harder to read.
>to rmmod_do_modlist() as it always holds the same value as
>stop_on_errors; is re-using (and renaming) possibly a better option?
>
>modprobe --remove --remove-holders --dry-run now ignores current
>refcounts of loaded modules when simulating removal of holders.
>
>Changes in v2:
> * Handle modules that have just been removed, gently
> * Fix --dry-run by ignoring module refcounts (_only_ for --dry-run)
> * Add missing kmod_module_unref_list() calls
>---
> tools/modprobe.c | 44 +++++++++++++++++++++++++++++++++++---------
> 1 file changed, 35 insertions(+), 9 deletions(-)
>
>diff --git a/tools/modprobe.c b/tools/modprobe.c
>index 3b7897c..a705f88 100644
>--- a/tools/modprobe.c
>+++ b/tools/modprobe.c
>@@ -390,13 +390,27 @@ static int rmmod_do_remove_module(struct kmod_module *mod)
> static int rmmod_do_module(struct kmod_module *mod, int flags);
>
> /* Remove modules in reverse order */
>-static int rmmod_do_modlist(struct kmod_list *list, bool stop_on_errors)
>+static int rmmod_do_modlist(struct kmod_list *list, bool stop_on_errors,
>+ bool recursive)
> {
> struct kmod_list *l;
>
> kmod_list_foreach_reverse(l, list) {
> struct kmod_module *m = kmod_module_get_module(l);
>- int r = rmmod_do_module(m, RMMOD_FLAG_IGNORE_BUILTIN);
>+ int r = 0;
>+
>+ if (recursive && kmod_module_get_initstate(m) >= 0) {
>+ struct kmod_list *holders = kmod_module_get_holders(m);
>+
>+ r = rmmod_do_modlist(holders, stop_on_errors,
>+ recursive);
>+
>+ kmod_module_unref_list(holders);
>+ }
>+
>+ if (!r)
>+ r = rmmod_do_module(m, RMMOD_FLAG_IGNORE_BUILTIN);
>+
> kmod_module_unref(m);
>
> if (r < 0 && stop_on_errors)
>@@ -448,15 +462,17 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
> }
>
> /* 1. @mod's post-softdeps in reverse order */
>- rmmod_do_modlist(post, false);
>+ rmmod_do_modlist(post, false, false);
>
> /* 2. Other modules holding @mod */
> if (flags & RMMOD_FLAG_REMOVE_HOLDERS) {
> struct kmod_list *holders = kmod_module_get_holders(mod);
>
>- err = rmmod_do_modlist(holders, true);
>+ err = rmmod_do_modlist(holders, true, true);
> if (err < 0)
> goto error;
>+
>+ kmod_module_unref_list(holders);
this is a separate fix. We also need to unref it on error, so probably
best to do:
err = rmmod_do_modlist(holders, true, true);
kmod_module_unref_list(holders);
if (err < 0)
goto error;
I think the alternative to the recursive approach would be to make only
the kmod_module_get_holders() be recursive:
struct kmod_list *holders = recursive_holders(mod);
And let recursive holders do recurse on modules passing the list as
argument to be augmented. Then the rest remains the same.
> }
>
> /* 3. @mod itself, but check for refcnt first */
>@@ -472,9 +488,16 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
> }
> }
>
>- if (!cmd)
>- err = rmmod_do_remove_module(mod);
>- else
>+ if (!cmd) {
>+ int state = kmod_module_get_initstate(mod);
>+
>+ if (state < 0) {
>+ /* Module was removed during recursive holder removal */
>+ err = 0;
wouldn't this fall in this case inside rmmod_do_remove_module()?
err = kmod_module_remove_module(mod, flags);
if (err == -EEXIST) {
if (!first_time)
err = 0;
>+ } else {
>+ err = rmmod_do_remove_module(mod);
>+ }
>+ } else
> err = command_do(mod, "remove", cmd, NULL);
>
> if (err < 0)
>@@ -488,14 +511,14 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
> kmod_list_foreach(itr, deps) {
> struct kmod_module *dep = kmod_module_get_module(itr);
> if (kmod_module_get_refcnt(dep) == 0)
>- rmmod_do_remove_module(dep);
>+ rmmod_do_module(dep, flags);
not sure also recursing the holders of the modules left is what we want.
If there are holders, then the module's refcnt should not be 0 anyway.
Lucas De Marchi
> kmod_module_unref(dep);
> }
> kmod_module_unref_list(deps);
> }
>
> /* 5. @mod's pre-softdeps in reverse order: errors are non-fatal */
>- rmmod_do_modlist(pre, false);
>+ rmmod_do_modlist(pre, false, false);
>
> error:
> kmod_module_unref_list(pre);
>@@ -975,6 +998,9 @@ static int do_modprobe(int argc, char **orig_argv)
> fstat(fileno(stderr), &stat_buf)))
> use_syslog = 1;
>
>+ if (remove_holders && dry_run)
>+ ignore_loaded = 1;
>+
> log_open(use_syslog);
>
> if (!do_show_config) {
>
>--
>2.40.0
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/3] testsuite: delete_module: Roughly implement fake-removal in sysfs tree
2023-03-29 13:51 ` [PATCH v2 2/3] testsuite: delete_module: Roughly implement fake-removal in sysfs tree Nicolas Schier
@ 2023-04-12 20:13 ` Lucas De Marchi
0 siblings, 0 replies; 9+ messages in thread
From: Lucas De Marchi @ 2023-04-12 20:13 UTC (permalink / raw)
To: Nicolas Schier; +Cc: linux-modules, Lucas De Marchi, Nicolas Schier
On Wed, Mar 29, 2023 at 03:51:36PM +0200, Nicolas Schier wrote:
>Extend delete_module() to simulate module removal in the testsuite's
>sysfs representation. During fake-removal, decrement refcnts on all
>modules that have the to-be-removed module as holder, and unlink the
>sysfs sub tree of the module itself.
>
>Signed-off-by: Nicolas Schier <n.schier@avm.de>
>---
>Changes in v2:
> * new patch
>---
> Makefile.am | 1 +
> testsuite/delete_module.c | 262 +++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 261 insertions(+), 2 deletions(-)
>
>diff --git a/Makefile.am b/Makefile.am
>index 8ba85c9..9a87824 100644
>--- a/Makefile.am
>+++ b/Makefile.am
>@@ -293,6 +293,7 @@ testsuite_path_la_CPPFLAGS = $(AM_CPPFLAGS) \
> testsuite_path_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS)
>
> testsuite_delete_module_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS)
>+testsuite_delete_module_la_LIBADD = libkmod/libkmod-internal.la
I skimmed through this and it looks good. One thing that we may need to
rethink in future is if we want to keep delete_module.so linking to
libkmod. We may hit a situation that we are are overriding stuff to test
libkmod but for that we also use libkmod.
I think that hardcoding here the kernel behavior would be ok instead of
depending on what libkmod thinks it should do.
if we keep libkmod, then it´d be better to get just 1 ctx:
static void init_ctx(void)
{
... kmod_ctx - kmod_new(NULL, NULL);
}
static void init_retcodes(void)
{
}
static void init(void)
{
if (!need_init)
return;
need_init = false;
init_ctx();
init_retcodes();
}
but I like the additional coverage that you added here, so it's ok
to keep it like this and improve in future.
Reviewed-by: Lucas De Marchi <lucas.demarchi@intel.com>
Lucas De Marchi
> testsuite_init_module_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS)
> testsuite_init_module_la_SOURCES = testsuite/init_module.c \
> testsuite/stripped-module.h
>diff --git a/testsuite/delete_module.c b/testsuite/delete_module.c
>index f3ae20b..73fc71a 100644
>--- a/testsuite/delete_module.c
>+++ b/testsuite/delete_module.c
>@@ -31,6 +31,7 @@
> #include <unistd.h>
>
> #include <shared/util.h>
>+#include <libkmod/libkmod.h>
>
> #include "testsuite.h"
>
>@@ -135,11 +136,252 @@ static void init_retcodes(void)
> }
> }
>
>+static bool lstat_is_dir(const char *dir)
>+{
>+ struct stat st;
>+
>+ if (!lstat(dir, &st))
>+ return S_ISDIR(st.st_mode);
>+
>+ ERR("TRAP delete_module(): %s: lstat: %s\n", dir, strerror(errno));
>+ return false;
>+}
>+
>+static int unlink_tree(const char *dir)
>+{
>+ struct dirent *dent;
>+ char *new_path;
>+ bool is_dir;
>+ DIR *dirp;
>+ int ret;
>+
>+ dirp = opendir(dir);
>+ if (!dirp) {
>+ ERR("TRAP delete_module(): %s: opendir: %s\n", dir,
>+ strerror(errno));
>+ return errno;
>+ }
>+
>+ errno = ret = 0;
>+ while (!ret && (dent = readdir(dirp))) {
>+ if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
>+ continue;
>+
>+ if (asprintf(&new_path, "%s/%s", dir, dent->d_name) < 0) {
>+ ERR("TRAP delete_module(): unlink_tree: out-of-memory\n");
>+ return ENOMEM;
>+ }
>+
>+ if (dent->d_type == DT_UNKNOWN)
>+ is_dir = lstat_is_dir(new_path);
>+ else
>+ is_dir = dent->d_type == DT_DIR;
>+
>+ if (is_dir)
>+ ret = unlink_tree(new_path);
>+ else {
>+ if (unlink(new_path)) {
>+ ret = errno;
>+ ERR("TRAP delete_module(): %s: unlink: %s\n", new_path, strerror(errno));
>+ }
>+ }
>+
>+ free(new_path);
>+ }
>+
>+ if (errno) {
>+ if (!ret)
>+ ret = errno;
>+ ERR("TRAP delete_module(): %s: readdir: %s\n", dir, strerror(errno));
>+ }
>+
>+ if (closedir(dirp)) {
>+ if (!ret)
>+ ret = errno;
>+ ERR("TRAP delete_module(): %s: closedir: %s\n", dir, strerror(errno));
>+ }
>+
>+ if (rmdir(dir)) {
>+ if (ret)
>+ ret = errno;
>+ ERR("TRAP delete_module(): %s: rmdir: %s\n", dir, strerror(errno));
>+ }
>+
>+ return ret;
>+}
>+
>+static const char *rootfs_path(void)
>+{
>+ char *rootfs;
>+
>+ rootfs = getenv(S_TC_ROOTFS);
>+ if (rootfs)
>+ return rootfs;
>+
>+ ERR("TRAP delete_module(): missing export %s?\n",
>+ S_TC_ROOTFS);
>+ return NULL;
>+}
>+
>+static char *sysfs_module_path_get(const char *module, const char *subpath)
>+{
>+ const char *rootfs = rootfs_path();
>+ char *sysfs_path;
>+ int ret;
>+
>+ if (!rootfs)
>+ return NULL;
>+
>+ ret = asprintf(&sysfs_path, "%s/sys/module/%s%s",
>+ rootfs, module, subpath ? subpath : "");
>+ if (ret >= 0)
>+ return sysfs_path;
>+
>+ ERR("TRAP delete_module(): %s: out-of-memory\n", subpath);
>+ return NULL;
>+}
>+
>+static int unlink_sysfs_module_tree(struct mod *mod)
>+{
>+ char *sysfs_path;
>+ int ret;
>+
>+ if (!(sysfs_path = sysfs_module_path_get(mod->name, NULL)))
>+ return EFAULT;
>+
>+ ret = unlink_tree(sysfs_path);
>+ free(sysfs_path);
>+
>+ return ret;
>+}
>+
>+static int sysfs_kmod_remove_holder(const struct kmod_module *kmod,
>+ const char *holder)
>+{
>+ const char *name = kmod_module_get_name(kmod);
>+ char *sysfs_mod_holders;
>+ char *sysfs_mod_refcnt;
>+ char refcnt_str[34];
>+ char *sysfs_mod;
>+ int holders_fd;
>+ int refcnt;
>+ int ret;
>+ int fd;
>+
>+ if (!(sysfs_mod = sysfs_module_path_get(name, NULL)) ||
>+ !(sysfs_mod_refcnt = sysfs_module_path_get(name, "/refcnt")) ||
>+ !(sysfs_mod_holders = sysfs_module_path_get(name, "/holders"))) {
>+ ERR("TRAP delete_module(): %s: out-of-memory\n", name);
>+ return ENOMEM;
>+ }
>+
>+ holders_fd = open(sysfs_mod_holders, O_RDONLY|O_CLOEXEC);
>+ if (holders_fd < 0) {
>+ ret = errno;
>+ ERR("TRAP delete_module(): %s: open: %s\n", sysfs_mod_holders,
>+ strerror(errno));
>+ goto fail_free_pathnames;
>+ }
>+
>+ if (unlinkat(holders_fd, holder, 0)) {
>+ ret = errno;
>+ ERR("TRAP delete_module(): %s/%s: unlink: %s\n",
>+ sysfs_mod_holders, holder, strerror(ret));
>+ goto fail_close_holders_fd;
>+ }
>+
>+ refcnt = kmod_module_get_refcnt(kmod);
>+ if (refcnt < 0) {
>+ ret = errno;
>+ ERR("TRAP delete_module(): %s: Invalid refcnt or error: %d\n",
>+ name, refcnt);
>+ return ret;
>+ }
>+
>+ if (refcnt == 0) {
>+ ERR("TRAP delete_module(): %s: refcnt already dropped to 0, refusing decrement\n",
>+ name);
>+ return EINVAL;
>+ }
>+
>+ refcnt--;
>+ snprintf(refcnt_str, sizeof(refcnt_str), "%d\n", refcnt);
>+
>+ fd = open(sysfs_mod_refcnt, O_WRONLY|O_TRUNC|O_CLOEXEC);
>+ if (fd < 0) {
>+ ret = errno;
>+ ERR("TRAP delete_module(): %s: open: %s\n", sysfs_mod_refcnt,
>+ strerror(ret));
>+ goto fail_free_pathnames;
>+ }
>+
>+ ret = write(fd, refcnt_str, strlen(refcnt_str));
>+ if (ret <= 0) {
>+ ret = errno;
>+ ERR("TRAP delete_module(): %s: write: %s\n", sysfs_mod_refcnt,
>+ strerror(ret));
>+ }
>+
>+ close(fd);
>+
>+fail_close_holders_fd:
>+ close(holders_fd);
>+
>+fail_free_pathnames:
>+ free(sysfs_mod_holders);
>+ free(sysfs_mod_refcnt);
>+ free(sysfs_mod);
>+
>+ return ret;
>+}
>+
>+static int decrement_holders_refcnt(struct mod *removed_mod)
>+{
>+ struct kmod_list *list, *itr;
>+ struct kmod_ctx *ctx;
>+ int err;
>+
>+ ctx = kmod_new(NULL, NULL);
>+ if (ctx == NULL) {
>+ ERR("TRAP delete_module(): Unable to get kmod ctx\n");
>+ return EFAULT;
>+ }
>+
>+ err = kmod_module_new_from_loaded(ctx, &list);
>+ if (err < 0) {
>+ ERR("TRAP delete_module(): Unable to get list of loaded modules: %s\n",
>+ strerror(-err));
>+ goto fail_free_ctx;
>+ }
>+
>+ kmod_list_foreach(itr, list) {
>+ struct kmod_module *kmod = kmod_module_get_module(itr);
>+ struct kmod_list *holders, *hitr;
>+
>+ holders = kmod_module_get_holders(kmod);
>+ kmod_list_foreach(hitr, holders) {
>+ struct kmod_module *hm = kmod_module_get_module(hitr);
>+ const char *holder_name = kmod_module_get_name(hm);
>+
>+ if (!strcmp(holder_name, removed_mod->name))
>+ sysfs_kmod_remove_holder(kmod, holder_name);
>+
>+ kmod_module_unref(hm);
>+ }
>+ kmod_module_unref_list(holders);
>+ kmod_module_unref(kmod);
>+ }
>+ kmod_module_unref_list(list);
>+
>+fail_free_ctx:
>+ kmod_unref(ctx);
>+
>+ return -err;
>+}
>+
> TS_EXPORT long delete_module(const char *name, unsigned int flags);
>
> /*
>- * FIXME: change /sys/module/<modname> to fake-remove a module
>- *
> * Default behavior is to exit successfully. If this is not the intended
> * behavior, set TESTSUITE_DELETE_MODULE_RETCODES env var.
> */
>@@ -152,6 +394,22 @@ long delete_module(const char *modname, unsigned int flags)
> if (mod == NULL)
> return 0;
>
>+ if (!mod->ret) {
>+ /* Adjust sysfs tree after successful removal */
>+
>+ errno = decrement_holders_refcnt(mod);
>+ if (errno) {
>+ ERR("TRAP delete_module(): unable to decrement sysfs holders\n");
>+ return EFAULT;
>+ }
>+
>+ errno = unlink_sysfs_module_tree(mod);
>+ if (errno) {
>+ ERR("TRAP delete_module(): unable to adjust sysfs tree\n");
>+ return EFAULT;
>+ }
>+ }
>+
> errno = mod->errcode;
> return mod->ret;
> }
>
>--
>2.40.0
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 3/3] testsuite: modprobe: Add test for --remove-holders
2023-03-29 13:51 ` [PATCH v2 3/3] testsuite: modprobe: Add test for --remove-holders Nicolas Schier
@ 2023-04-12 20:20 ` Lucas De Marchi
0 siblings, 0 replies; 9+ messages in thread
From: Lucas De Marchi @ 2023-04-12 20:20 UTC (permalink / raw)
To: Nicolas Schier; +Cc: linux-modules, Lucas De Marchi, Nicolas Schier
On Wed, Mar 29, 2023 at 03:51:37PM +0200, Nicolas Schier wrote:
>Add a test for 'modprobe --remove --remove-holders' and a simple
>dependency module chain of three modules.
>
>Signed-off-by: Nicolas Schier <n.schier@avm.de>
>---
>Changes in v2:
> * new patch
>---
> testsuite/module-playground/Makefile | 6 +++
> testsuite/module-playground/mod-dep-chain-a.c | 21 ++++++++
> testsuite/module-playground/mod-dep-chain-b.c | 22 +++++++++
> testsuite/module-playground/mod-dep-chain-c.c | 22 +++++++++
> testsuite/module-playground/mod-dep-chain.h | 8 +++
> .../remove-holders/lib/modules/6.2/modules.alias | 1 +
> .../lib/modules/6.2/modules.alias.bin | Bin 0 -> 12 bytes
> .../lib/modules/6.2/modules.builtin.alias.bin | 0
> .../lib/modules/6.2/modules.builtin.bin | 0
> .../remove-holders/lib/modules/6.2/modules.dep | 3 ++
> .../remove-holders/lib/modules/6.2/modules.dep.bin | Bin 0 -> 229 bytes
> .../remove-holders/lib/modules/6.2/modules.devname | 0
> .../remove-holders/lib/modules/6.2/modules.softdep | 1 +
> .../remove-holders/lib/modules/6.2/modules.symbols | 4 ++
> .../lib/modules/6.2/modules.symbols.bin | Bin 0 -> 143 bytes
> .../test-modprobe/remove-holders/proc/modules | 3 ++
> .../sys/module/mod_dep_chain_a/coresize | 1 +
> .../module/mod_dep_chain_a/holders/mod_dep_chain_b | 1 +
> .../sys/module/mod_dep_chain_a/initsize | 1 +
> .../sys/module/mod_dep_chain_a/initstate | 1 +
> .../sys/module/mod_dep_chain_a/notes/.note.Linux | Bin 0 -> 60 bytes
> .../mod_dep_chain_a/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
> .../sys/module/mod_dep_chain_a/refcnt | 1 +
> .../sections/.gnu.linkonce.this_module | 1 +
> .../sys/module/mod_dep_chain_a/sections/.init.data | 1 +
> .../module/mod_dep_chain_a/sections/.note.Linux | 1 +
> .../mod_dep_chain_a/sections/.note.gnu.build-id | 1 +
> .../module/mod_dep_chain_a/sections/.orc_unwind | 1 +
> .../module/mod_dep_chain_a/sections/.orc_unwind_ip | 1 +
> .../module/mod_dep_chain_a/sections/.return_sites | 1 +
> .../module/mod_dep_chain_a/sections/.rodata.str1.1 | 1 +
> .../sys/module/mod_dep_chain_a/sections/.strtab | 1 +
> .../sys/module/mod_dep_chain_a/sections/.symtab | 1 +
> .../module/mod_dep_chain_a/sections/.text.unlikely | 1 +
> .../sys/module/mod_dep_chain_a/sections/__kcrctab | 1 +
> .../sys/module/mod_dep_chain_a/sections/__ksymtab | 1 +
> .../mod_dep_chain_a/sections/__ksymtab_strings | 1 +
> .../module/mod_dep_chain_a/sections/__mcount_loc | 1 +
> .../sys/module/mod_dep_chain_a/taint | 1 +
> .../sys/module/mod_dep_chain_b/coresize | 1 +
> .../module/mod_dep_chain_b/holders/mod_dep_chain_c | 1 +
> .../sys/module/mod_dep_chain_b/initsize | 1 +
> .../sys/module/mod_dep_chain_b/initstate | 1 +
> .../sys/module/mod_dep_chain_b/notes/.note.Linux | Bin 0 -> 60 bytes
> .../mod_dep_chain_b/notes/.note.gnu.build-id | Bin 0 -> 36 bytes
> .../sys/module/mod_dep_chain_b/refcnt | 1 +
> .../sections/.gnu.linkonce.this_module | 1 +
> .../sys/module/mod_dep_chain_b/sections/.init.data | 1 +
> .../module/mod_dep_chain_b/sections/.note.Linux | 1 +
> .../mod_dep_chain_b/sections/.note.gnu.build-id | 1 +
> .../module/mod_dep_chain_b/sections/.orc_unwind | 1 +
> .../module/mod_dep_chain_b/sections/.orc_unwind_ip | 1 +
> .../module/mod_dep_chain_b/sections/.return_sites | 1 +
> .../module/mod_dep_chain_b/sections/.rodata.str1.8 | 1 +
> .../sys/module/mod_dep_chain_b/sections/.strtab | 1 +
> .../sys/module/mod_dep_chain_b/sections/.symtab | 1 +
> .../module/mod_dep_chain_b/sections/.text.unlikely | 1 +
> .../sys/module/mod_dep_chain_b/sections/__kcrctab | 1 +
> .../sys/module/mod_dep_chain_b/sections/__ksymtab | 1 +
I think it would be ok to drop */sections and */notes/
since libkmod doesn't care about them. Otherwise looks good to me:
Reviewed-by: Lucas De Marchi <lucas.demarchi@intel.com>
thanks
Lucas De Marchi
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/3] kmod: modprobe: Remove holders recursively
2023-04-12 19:21 ` Lucas De Marchi
@ 2023-04-18 10:10 ` Nicolas Schier
0 siblings, 0 replies; 9+ messages in thread
From: Nicolas Schier @ 2023-04-18 10:10 UTC (permalink / raw)
To: Lucas De Marchi; +Cc: linux-modules, Lucas De Marchi, Nicolas Schier
[-- Attachment #1: Type: text/plain, Size: 11057 bytes --]
On Wed, Apr 12, 2023 at 12:21:51PM -0700, Lucas De Marchi wrote:
> On Wed, Mar 29, 2023 at 03:51:35PM +0200, Nicolas Schier wrote:
> > Remove holders recursively when removal of module holders is requested.
> >
> > Extend commit 42b32d30c38e ("modprobe: Fix holders removal") by removing
> > also indirect holders if --remove-holders is set. For a simple module
> > dependency chain like
> >
> > module3 depends on module2
> > module2 depends on module1
> >
> > 'modprobe -r --remove-holders module1' will remove module3, module2 and
> > module1 in correct order:
> >
> > $ modprobe -r --remove-holders module1 --verbose
> > rmmod module3
> > rmmod module2
> > rmmod module1
> >
> > (Actually, it will do the same when specifying module2 or module3 for
> > removal instead of module1.)
> >
> > As a side-effect, 'modprobe -r module3' (w/o --remove-holders) will also
> > remove all three modules from the example above, as after removal of
> > module3, module2 does not have any holders and the same holds for
> > module1 after removal of module2:
> >
> > $ modprobe -r module3 --verbose
> > rmmod module3
> > rmmod module2
> > rmmod module1
> >
> > Without recursive evaluation of holders, modprobe leaves module1 loaded.
> >
> > Unfortunately, '--dry-run --remove-holders' breaks with indirect
> > dependencies.
> >
> > Signed-off-by: Nicolas Schier <n.schier@avm.de>
> > ---
> > I am a bit unhappy about the introduction of the 'recursive' parameter
>
> yeah... it makes it slightly harder to read.
>
> > to rmmod_do_modlist() as it always holds the same value as
> > stop_on_errors; is re-using (and renaming) possibly a better option?
> >
> > modprobe --remove --remove-holders --dry-run now ignores current
> > refcounts of loaded modules when simulating removal of holders.
> >
> > Changes in v2:
> > * Handle modules that have just been removed, gently
> > * Fix --dry-run by ignoring module refcounts (_only_ for --dry-run)
> > * Add missing kmod_module_unref_list() calls
> > ---
> > tools/modprobe.c | 44 +++++++++++++++++++++++++++++++++++---------
> > 1 file changed, 35 insertions(+), 9 deletions(-)
> >
> > diff --git a/tools/modprobe.c b/tools/modprobe.c
> > index 3b7897c..a705f88 100644
> > --- a/tools/modprobe.c
> > +++ b/tools/modprobe.c
> > @@ -390,13 +390,27 @@ static int rmmod_do_remove_module(struct kmod_module *mod)
> > static int rmmod_do_module(struct kmod_module *mod, int flags);
> >
> > /* Remove modules in reverse order */
> > -static int rmmod_do_modlist(struct kmod_list *list, bool stop_on_errors)
> > +static int rmmod_do_modlist(struct kmod_list *list, bool stop_on_errors,
> > + bool recursive)
> > {
> > struct kmod_list *l;
> >
> > kmod_list_foreach_reverse(l, list) {
> > struct kmod_module *m = kmod_module_get_module(l);
> > - int r = rmmod_do_module(m, RMMOD_FLAG_IGNORE_BUILTIN);
> > + int r = 0;
> > +
> > + if (recursive && kmod_module_get_initstate(m) >= 0) {
> > + struct kmod_list *holders = kmod_module_get_holders(m);
> > +
> > + r = rmmod_do_modlist(holders, stop_on_errors,
> > + recursive);
> > +
> > + kmod_module_unref_list(holders);
> > + }
> > +
> > + if (!r)
> > + r = rmmod_do_module(m, RMMOD_FLAG_IGNORE_BUILTIN);
> > +
> > kmod_module_unref(m);
> >
> > if (r < 0 && stop_on_errors)
> > @@ -448,15 +462,17 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
> > }
> >
> > /* 1. @mod's post-softdeps in reverse order */
> > - rmmod_do_modlist(post, false);
> > + rmmod_do_modlist(post, false, false);
> >
> > /* 2. Other modules holding @mod */
> > if (flags & RMMOD_FLAG_REMOVE_HOLDERS) {
> > struct kmod_list *holders = kmod_module_get_holders(mod);
> >
> > - err = rmmod_do_modlist(holders, true);
> > + err = rmmod_do_modlist(holders, true, true);
> > if (err < 0)
> > goto error;
> > +
> > + kmod_module_unref_list(holders);
>
> this is a separate fix. We also need to unref it on error, so probably
> best to do:
>
> err = rmmod_do_modlist(holders, true, true);
> kmod_module_unref_list(holders);
> if (err < 0)
> goto error;
Thanks! Yes, sure. I sent it as a separate patch at
https://lore.kernel.org/linux-modules/20230418-add-missing-kmod_module_unref_list-v1-1-ab5b554f15ee@avm.de/
>
> I think the alternative to the recursive approach would be to make only
> the kmod_module_get_holders() be recursive:
>
> struct kmod_list *holders = recursive_holders(mod);
>
> And let recursive holders do recurse on modules passing the list as
> argument to be augmented. Then the rest remains the same.
Yes, that sounds much nicer than the stuff I did. I will try and send a v3.
> > }
> >
> > /* 3. @mod itself, but check for refcnt first */
> > @@ -472,9 +488,16 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
> > }
> > }
> >
> > - if (!cmd)
> > - err = rmmod_do_remove_module(mod);
> > - else
> > + if (!cmd) {
> > + int state = kmod_module_get_initstate(mod);
> > +
> > + if (state < 0) {
> > + /* Module was removed during recursive holder removal */
> > + err = 0;
>
> wouldn't this fall in this case inside rmmod_do_remove_module()?
>
> err = kmod_module_remove_module(mod, flags);
> if (err == -EEXIST) {
> if (!first_time)
> err = 0;
No, as the module might already be removed and kmod_module_remove_module() thus
returns -ENOENT. I think, your suggestion to regarding introduction of
recursive_holders() should remove the need for checking the kmod state here.
FTR: I tested the current patch w/o the kmod state check; as expected, it leads to errors:
testsuite failure, when using --remove-holders:
rmmod mod_dep_chain_c
TESTSUITE: Added module to test delete_module:
TESTSUITE: name=mod_dep_chain_c ret=0 errcode=0
TESTSUITE: Added module to test delete_module:
TESTSUITE: name=mod_dep_chain_b ret=0 errcode=0
TESTSUITE: Added module to test delete_module:
TESTSUITE: name=mod_dep_chain_a ret=0 errcode=0
rmmod mod_dep_chain_b
libkmod: kmod_module_get_holders: could not open '/sys/module/mod_dep_chain_c/holders': No such file or directory
rmmod mod_dep_chain_a
libkmod: kmod_module_get_holders: could not open '/sys/module/mod_dep_chain_c/holders': No such file or directory
libkmod: kmod_module_get_holders: could not open '/sys/module/mod_dep_chain_b/holders': No such file or directory
rmmod mod_dep_chain_a
libkmod: kmod_module_get_holders: could not open '/sys/module/mod_dep_chain_c/holders': No such file or directory
libkmod: kmod_module_get_holders: could not open '/sys/module/mod_dep_chain_b/holders': No such file or directory
libkmod: kmod_module_get_holders: could not open '/sys/module/mod_dep_chain_a/holders': No such file or directory
modprobe: ERROR: could not remove 'mod_dep_chain_a': No such file or directory
TESTSUITE: ERR: TRAP delete_module(): /home/nicolas/src/kmod/testsuite/rootfs/test-modprobe/remove-holders/sys/module/mod_dep_chain_a: opendir: No such file or directory
TESTSUITE: ERR: TRAP delete_module(): unable to adjust sysfs tree
TESTSUITE: running modprobe_remove_with_holders, in forked context
TESTSUITE: ERR: 'modprobe_remove_with_holders' [2320200] exited with return code 1
TESTSUITE: ERR: FAILED: modprobe_remove_with_holders
TESTSUITE: ------
FAIL testsuite/test-modprobe (exit status: 1)
and on my dev maschine I also see attempts of module removal that fail:
The relevant snippets from lsmod output:
btrfs 1777664 0
zstd_compress 294912 1 btrfs
...
raid456 180224 0
async_raid6_recov 24576 1 raid456
async_memcpy 20480 2 raid456,async_raid6_recov
async_pq 20480 2 raid456,async_raid6_recov
async_xor 20480 3 async_pq,raid456,async_raid6_recov
async_tx 20480 5 async_pq,async_memcpy,async_xor,raid456,async_raid6_recov
xor 24576 2 async_xor,btrfs
raid6_pq 122880 4 async_pq,btrfs,raid456,async_raid6_recov
and the modprobe call, without the additional kmod state check:
sudo tools/modprobe -r --remove-holders xor -vv
modprobe: INFO: custom logging function 0x5564236f5700 registered
rmmod btrfs
rmmod zstd_compress
rmmod raid456
rmmod async_raid6_recov
rmmod async_pq
rmmod raid6_pq
rmmod async_xor
rmmod xor
rmmod async_memcpy
rmmod async_tx
rmmod xor
modprobe: ERROR: could not remove 'xor': No such file or directory
modprobe: INFO: context 0x556423e68440 released
[exit code 1]
Both, testsuite and the modprobe -r, succeed as w/o complaints with the kmod
state check included.
(Again, as written above: I hope the kmod state check insertion becomes
obsolete when switching to something like recursive_holders().)
> > + } else {
> > + err = rmmod_do_remove_module(mod);
> > + }
> > + } else
> > err = command_do(mod, "remove", cmd, NULL);
> >
> > if (err < 0)
> > @@ -488,14 +511,14 @@ static int rmmod_do_module(struct kmod_module *mod, int flags)
> > kmod_list_foreach(itr, deps) {
> > struct kmod_module *dep = kmod_module_get_module(itr);
> > if (kmod_module_get_refcnt(dep) == 0)
> > - rmmod_do_remove_module(dep);
> > + rmmod_do_module(dep, flags);
>
> not sure also recursing the holders of the modules left is what we want.
> If there are holders, then the module's refcnt should not be 0 anyway.
>
> Lucas De Marchi
Yeah, I have no strong opinion to that. Currently, 'modprobe -r
--remove-holders MOD' removes only direct dependencies of MOD if they have a refcnt
of 0 after MOD has been removed. I think, removing MOD's dependencies
recursively, would make it more of an inverse operation:
modprobe mod-dep-chain-c (loads mod-dep-chain-a, -b and -c)
modprobe -r --remove-holders mod-dep-chain-c (unloads only mod-dep-chain-b ?)
(If option '--remove-dependencies' wasn't burned, it could have been a
switch to enable this?)
But I cannot say, if someone really needs that functionality; thus, I
will drop it in v3.
Thanks for review and suggestions!
Kind regards,
Nicolas
> > kmod_module_unref(dep);
> > }
> > kmod_module_unref_list(deps);
> > }
> >
> > /* 5. @mod's pre-softdeps in reverse order: errors are non-fatal */
> > - rmmod_do_modlist(pre, false);
> > + rmmod_do_modlist(pre, false, false);
> >
> > error:
> > kmod_module_unref_list(pre);
> > @@ -975,6 +998,9 @@ static int do_modprobe(int argc, char **orig_argv)
> > fstat(fileno(stderr), &stat_buf)))
> > use_syslog = 1;
> >
> > + if (remove_holders && dry_run)
> > + ignore_loaded = 1;
> > +
> > log_open(use_syslog);
> >
> > if (!do_show_config) {
> >
> > --
> > 2.40.0
> >
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2023-04-18 10:11 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-03-29 13:51 [PATCH v2 0/3] kmod: modprobe: Extend holders removal to multi-level dependencies Nicolas Schier
2023-03-29 13:51 ` [PATCH v2 1/3] kmod: modprobe: Remove holders recursively Nicolas Schier
2023-03-29 13:58 ` Nicolas Schier
2023-04-12 19:21 ` Lucas De Marchi
2023-04-18 10:10 ` Nicolas Schier
2023-03-29 13:51 ` [PATCH v2 2/3] testsuite: delete_module: Roughly implement fake-removal in sysfs tree Nicolas Schier
2023-04-12 20:13 ` Lucas De Marchi
2023-03-29 13:51 ` [PATCH v2 3/3] testsuite: modprobe: Add test for --remove-holders Nicolas Schier
2023-04-12 20:20 ` Lucas De Marchi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).