|
Petr Pavlu |
63bdff |
From: Petr Pavlu <petr.pavlu@suse.com>
|
|
Petr Pavlu |
63bdff |
Date: Mon, 5 Dec 2022 11:35:57 +0100
|
|
Petr Pavlu |
63bdff |
Subject: module: Don't wait for GOING modules
|
|
Petr Pavlu |
63bdff |
Git-commit: 0254127ab977e70798707a7a2b757c9f3c971210
|
|
Petr Pavlu |
63bdff |
Patch-mainline: v6.2-rc6
|
|
Petr Pavlu |
63bdff |
References: bsc#1196058, bsc#1186449, bsc#1204356, bsc#1204662
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
During a system boot, it can happen that the kernel receives a burst of
|
|
Petr Pavlu |
63bdff |
requests to insert the same module but loading it eventually fails
|
|
Petr Pavlu |
63bdff |
during its init call. For instance, udev can make a request to insert
|
|
Petr Pavlu |
63bdff |
a frequency module for each individual CPU when another frequency module
|
|
Petr Pavlu |
63bdff |
is already loaded which causes the init function of the new module to
|
|
Petr Pavlu |
63bdff |
return an error.
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
Since commit 6e6de3dee51a ("kernel/module.c: Only return -EEXIST for
|
|
Petr Pavlu |
63bdff |
modules that have finished loading"), the kernel waits for modules in
|
|
Petr Pavlu |
63bdff |
MODULE_STATE_GOING state to finish unloading before making another
|
|
Petr Pavlu |
63bdff |
attempt to load the same module.
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
This creates unnecessary work in the described scenario and delays the
|
|
Petr Pavlu |
63bdff |
boot. In the worst case, it can prevent udev from loading drivers for
|
|
Petr Pavlu |
63bdff |
other devices and might cause timeouts of services waiting on them and
|
|
Petr Pavlu |
63bdff |
subsequently a failed boot.
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
This patch attempts a different solution for the problem 6e6de3dee51a
|
|
Petr Pavlu |
63bdff |
was trying to solve. Rather than waiting for the unloading to complete,
|
|
Petr Pavlu |
63bdff |
it returns a different error code (-EBUSY) for modules in the GOING
|
|
Petr Pavlu |
63bdff |
state. This should avoid the error situation that was described in
|
|
Petr Pavlu |
63bdff |
6e6de3dee51a (user space attempting to load a dependent module because
|
|
Petr Pavlu |
63bdff |
the -EEXIST error code would suggest to user space that the first module
|
|
Petr Pavlu |
63bdff |
had been loaded successfully), while avoiding the delay situation too.
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
This has been tested on linux-next since December 2022 and passes
|
|
Petr Pavlu |
63bdff |
all kmod selftests except test 0009 with module compression enabled
|
|
Petr Pavlu |
63bdff |
but it has been confirmed that this issue has existed and has gone
|
|
Petr Pavlu |
63bdff |
unnoticed since prior to this commit and can also be reproduced without
|
|
Petr Pavlu |
63bdff |
module compression with a simple usleep(5000000) on tools/modprobe.c [0].
|
|
Petr Pavlu |
63bdff |
These failures are caused by hitting the kernel mod_concurrent_max and can
|
|
Petr Pavlu |
63bdff |
happen either due to a self inflicted kernel module auto-loead DoS somehow
|
|
Petr Pavlu |
63bdff |
or on a system with large CPU count and each CPU count incorrectly triggering
|
|
Petr Pavlu |
63bdff |
many module auto-loads. Both of those issues need to be fixed in-kernel.
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
[0] https://lore.kernel.org/all/Y9A4fiobL6IHp%2F%2FP@bombadil.infradead.org/
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
Fixes: 6e6de3dee51a ("kernel/module.c: Only return -EEXIST for modules that have finished loading")
|
|
Petr Pavlu |
63bdff |
Co-developed-by: Martin Wilck <mwilck@suse.com>
|
|
Petr Pavlu |
63bdff |
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
|
Petr Pavlu |
63bdff |
Signed-off-by: Petr Pavlu <petr.pavlu@suse.com>
|
|
Petr Pavlu |
63bdff |
Cc: stable@vger.kernel.org
|
|
Petr Pavlu |
63bdff |
Reviewed-by: Petr Mladek <pmladek@suse.com>
|
|
Petr Pavlu |
63bdff |
[mcgrof: enhance commit log with testing and kmod test result interpretation ]
|
|
Petr Pavlu |
63bdff |
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
|
|
Petr Pavlu |
63bdff |
---
|
|
Petr Pavlu |
63bdff |
kernel/module.c | 26 +++++++++++++++++++++-----
|
|
Petr Pavlu |
63bdff |
1 file changed, 21 insertions(+), 5 deletions(-)
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
diff --git a/kernel/module.c b/kernel/module.c
|
|
Petr Pavlu |
63bdff |
index 48568a0f5651..4ac3fe43e6c8 100644
|
|
Petr Pavlu |
63bdff |
--- a/kernel/module.c
|
|
Petr Pavlu |
63bdff |
+++ b/kernel/module.c
|
|
Petr Pavlu |
63bdff |
@@ -2393,7 +2393,8 @@ static bool finished_loading(const char *name)
|
|
Petr Pavlu |
63bdff |
sched_annotate_sleep();
|
|
Petr Pavlu |
63bdff |
mutex_lock(&module_mutex);
|
|
Petr Pavlu |
63bdff |
mod = find_module_all(name, strlen(name), true);
|
|
Petr Pavlu |
63bdff |
- ret = !mod || mod->state == MODULE_STATE_LIVE;
|
|
Petr Pavlu |
63bdff |
+ ret = !mod || mod->state == MODULE_STATE_LIVE
|
|
Petr Pavlu |
63bdff |
+ || mod->state == MODULE_STATE_GOING;
|
|
Petr Pavlu |
63bdff |
mutex_unlock(&module_mutex);
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
return ret;
|
|
Petr Pavlu |
63bdff |
@@ -2569,20 +2570,35 @@ static int add_unformed_module(struct module *mod)
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
mod->state = MODULE_STATE_UNFORMED;
|
|
Petr Pavlu |
63bdff |
|
|
Petr Pavlu |
63bdff |
-again:
|
|
Petr Pavlu |
63bdff |
mutex_lock(&module_mutex);
|
|
Petr Pavlu |
63bdff |
old = find_module_all(mod->name, strlen(mod->name), true);
|
|
Petr Pavlu |
63bdff |
if (old != NULL) {
|
|
Petr Pavlu |
63bdff |
- if (old->state != MODULE_STATE_LIVE) {
|
|
Petr Pavlu |
63bdff |
+ if (old->state == MODULE_STATE_COMING
|
|
Petr Pavlu |
63bdff |
+ || old->state == MODULE_STATE_UNFORMED) {
|
|
Petr Pavlu |
63bdff |
/* Wait in case it fails to load. */
|
|
Petr Pavlu |
63bdff |
mutex_unlock(&module_mutex);
|
|
Petr Pavlu |
63bdff |
err = wait_event_interruptible(module_wq,
|
|
Petr Pavlu |
63bdff |
finished_loading(mod->name));
|
|
Petr Pavlu |
63bdff |
if (err)
|
|
Petr Pavlu |
63bdff |
goto out_unlocked;
|
|
Petr Pavlu |
63bdff |
- goto again;
|
|
Petr Pavlu |
63bdff |
+
|
|
Petr Pavlu |
63bdff |
+ /* The module might have gone in the meantime. */
|
|
Petr Pavlu |
63bdff |
+ mutex_lock(&module_mutex);
|
|
Petr Pavlu |
63bdff |
+ old = find_module_all(mod->name, strlen(mod->name),
|
|
Petr Pavlu |
63bdff |
+ true);
|
|
Petr Pavlu |
63bdff |
}
|
|
Petr Pavlu |
63bdff |
- err = -EEXIST;
|
|
Petr Pavlu |
63bdff |
+
|
|
Petr Pavlu |
63bdff |
+ /*
|
|
Petr Pavlu |
63bdff |
+ * We are here only when the same module was being loaded. Do
|
|
Petr Pavlu |
63bdff |
+ * not try to load it again right now. It prevents long delays
|
|
Petr Pavlu |
63bdff |
+ * caused by serialized module load failures. It might happen
|
|
Petr Pavlu |
63bdff |
+ * when more devices of the same type trigger load of
|
|
Petr Pavlu |
63bdff |
+ * a particular module.
|
|
Petr Pavlu |
63bdff |
+ */
|
|
Petr Pavlu |
63bdff |
+ if (old && old->state == MODULE_STATE_LIVE)
|
|
Petr Pavlu |
63bdff |
+ err = -EEXIST;
|
|
Petr Pavlu |
63bdff |
+ else
|
|
Petr Pavlu |
63bdff |
+ err = -EBUSY;
|
|
Petr Pavlu |
63bdff |
goto out;
|
|
Petr Pavlu |
63bdff |
}
|
|
Petr Pavlu |
63bdff |
mod_update_bounds(mod);
|
|
Petr Pavlu |
63bdff |
|