Skip to content

Commit 0254127

Browse files
petrpavlumcgrof
authored andcommitted
module: Don't wait for GOING modules
During a system boot, it can happen that the kernel receives a burst of requests to insert the same module but loading it eventually fails during its init call. For instance, udev can make a request to insert a frequency module for each individual CPU when another frequency module is already loaded which causes the init function of the new module to return an error. Since commit 6e6de3d ("kernel/module.c: Only return -EEXIST for modules that have finished loading"), the kernel waits for modules in MODULE_STATE_GOING state to finish unloading before making another attempt to load the same module. This creates unnecessary work in the described scenario and delays the boot. In the worst case, it can prevent udev from loading drivers for other devices and might cause timeouts of services waiting on them and subsequently a failed boot. This patch attempts a different solution for the problem 6e6de3d was trying to solve. Rather than waiting for the unloading to complete, it returns a different error code (-EBUSY) for modules in the GOING state. This should avoid the error situation that was described in 6e6de3d (user space attempting to load a dependent module because the -EEXIST error code would suggest to user space that the first module had been loaded successfully), while avoiding the delay situation too. This has been tested on linux-next since December 2022 and passes all kmod selftests except test 0009 with module compression enabled but it has been confirmed that this issue has existed and has gone unnoticed since prior to this commit and can also be reproduced without module compression with a simple usleep(5000000) on tools/modprobe.c [0]. These failures are caused by hitting the kernel mod_concurrent_max and can happen either due to a self inflicted kernel module auto-loead DoS somehow or on a system with large CPU count and each CPU count incorrectly triggering many module auto-loads. Both of those issues need to be fixed in-kernel. [0] https://lore.kernel.org/all/Y9A4fiobL6IHp%2F%2FP@bombadil.infradead.org/ Fixes: 6e6de3d ("kernel/module.c: Only return -EEXIST for modules that have finished loading") Co-developed-by: Martin Wilck <mwilck@suse.com> Signed-off-by: Martin Wilck <mwilck@suse.com> Signed-off-by: Petr Pavlu <petr.pavlu@suse.com> Cc: stable@vger.kernel.org Reviewed-by: Petr Mladek <pmladek@suse.com> [mcgrof: enhance commit log with testing and kmod test result interpretation ] Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
1 parent 7bf70db commit 0254127

File tree

1 file changed

+21
-5
lines changed

1 file changed

+21
-5
lines changed

kernel/module/main.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2393,7 +2393,8 @@ static bool finished_loading(const char *name)
23932393
sched_annotate_sleep();
23942394
mutex_lock(&module_mutex);
23952395
mod = find_module_all(name, strlen(name), true);
2396-
ret = !mod || mod->state == MODULE_STATE_LIVE;
2396+
ret = !mod || mod->state == MODULE_STATE_LIVE
2397+
|| mod->state == MODULE_STATE_GOING;
23972398
mutex_unlock(&module_mutex);
23982399

23992400
return ret;
@@ -2569,20 +2570,35 @@ static int add_unformed_module(struct module *mod)
25692570

25702571
mod->state = MODULE_STATE_UNFORMED;
25712572

2572-
again:
25732573
mutex_lock(&module_mutex);
25742574
old = find_module_all(mod->name, strlen(mod->name), true);
25752575
if (old != NULL) {
2576-
if (old->state != MODULE_STATE_LIVE) {
2576+
if (old->state == MODULE_STATE_COMING
2577+
|| old->state == MODULE_STATE_UNFORMED) {
25772578
/* Wait in case it fails to load. */
25782579
mutex_unlock(&module_mutex);
25792580
err = wait_event_interruptible(module_wq,
25802581
finished_loading(mod->name));
25812582
if (err)
25822583
goto out_unlocked;
2583-
goto again;
2584+
2585+
/* The module might have gone in the meantime. */
2586+
mutex_lock(&module_mutex);
2587+
old = find_module_all(mod->name, strlen(mod->name),
2588+
true);
25842589
}
2585-
err = -EEXIST;
2590+
2591+
/*
2592+
* We are here only when the same module was being loaded. Do
2593+
* not try to load it again right now. It prevents long delays
2594+
* caused by serialized module load failures. It might happen
2595+
* when more devices of the same type trigger load of
2596+
* a particular module.
2597+
*/
2598+
if (old && old->state == MODULE_STATE_LIVE)
2599+
err = -EEXIST;
2600+
else
2601+
err = -EBUSY;
25862602
goto out;
25872603
}
25882604
mod_update_bounds(mod);

0 commit comments

Comments
 (0)