mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 17:30:02 +00:00
![Rasmus Villemoes](/assets/img/avatar_default.png)
Allow the developer to specifiy the initial value of the modprobe_path[] string. This can be used to set it to the empty string initially, thus effectively disabling request_module() during early boot until userspace writes a new value via the /proc/sys/kernel/modprobe interface. [1] When building a custom kernel (often for an embedded target), it's normal to build everything into the kernel that is needed for booting, and indeed the initramfs often contains no modules at all, so every such request_module() done before userspace init has mounted the real rootfs is a waste of time. This is particularly useful when combined with the previous patch, which made the initramfs unpacking asynchronous - for that to work, it had to make any usermodehelper call wait for the unpacking to finish before attempting to invoke the userspace helper. By eliminating all such (known-to-be-futile) calls of usermodehelper, the initramfs unpacking and the {device,late}_initcalls can proceed in parallel for much longer. For a relatively slow ppc board I'm working on, the two patches combined lead to 0.2s faster boot - but more importantly, the fact that the initramfs unpacking proceeds completely in the background while devices get probed means I get to handle the gpio watchdog in time without getting reset. [1] __request_module() already has an early -ENOENT return when modprobe_path is the empty string. Link: https://lkml.kernel.org/r/20210313212528.2956377-3-linux@rasmusvillemoes.dk Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Jessica Yu <jeyu@kernel.org> Acked-by: Luis Chamberlain <mcgrof@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Nick Desaulniers <ndesaulniers@google.com> Cc: Takashi Iwai <tiwai@suse.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
177 lines
5 KiB
C
177 lines
5 KiB
C
/*
|
|
* kmod - the kernel module loader
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/task.h>
|
|
#include <linux/binfmts.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/unistd.h>
|
|
#include <linux/kmod.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/cred.h>
|
|
#include <linux/file.h>
|
|
#include <linux/fdtable.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/security.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/resource.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/rwsem.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/async.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <trace/events/module.h>
|
|
|
|
/*
|
|
* Assuming:
|
|
*
|
|
* threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE,
|
|
* (u64) THREAD_SIZE * 8UL);
|
|
*
|
|
* If you need less than 50 threads would mean we're dealing with systems
|
|
* smaller than 3200 pages. This assumes you are capable of having ~13M memory,
|
|
* and this would only be an upper limit, after which the OOM killer would take
|
|
* effect. Systems like these are very unlikely if modules are enabled.
|
|
*/
|
|
#define MAX_KMOD_CONCURRENT 50
|
|
static atomic_t kmod_concurrent_max = ATOMIC_INIT(MAX_KMOD_CONCURRENT);
|
|
static DECLARE_WAIT_QUEUE_HEAD(kmod_wq);
|
|
|
|
/*
|
|
* This is a restriction on having *all* MAX_KMOD_CONCURRENT threads
|
|
* running at the same time without returning. When this happens we
|
|
* believe you've somehow ended up with a recursive module dependency
|
|
* creating a loop.
|
|
*
|
|
* We have no option but to fail.
|
|
*
|
|
* Userspace should proactively try to detect and prevent these.
|
|
*/
|
|
#define MAX_KMOD_ALL_BUSY_TIMEOUT 5
|
|
|
|
/*
|
|
modprobe_path is set via /proc/sys.
|
|
*/
|
|
char modprobe_path[KMOD_PATH_LEN] = CONFIG_MODPROBE_PATH;
|
|
|
|
static void free_modprobe_argv(struct subprocess_info *info)
|
|
{
|
|
kfree(info->argv[3]); /* check call_modprobe() */
|
|
kfree(info->argv);
|
|
}
|
|
|
|
static int call_modprobe(char *module_name, int wait)
|
|
{
|
|
struct subprocess_info *info;
|
|
static char *envp[] = {
|
|
"HOME=/",
|
|
"TERM=linux",
|
|
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
|
|
NULL
|
|
};
|
|
|
|
char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL);
|
|
if (!argv)
|
|
goto out;
|
|
|
|
module_name = kstrdup(module_name, GFP_KERNEL);
|
|
if (!module_name)
|
|
goto free_argv;
|
|
|
|
argv[0] = modprobe_path;
|
|
argv[1] = "-q";
|
|
argv[2] = "--";
|
|
argv[3] = module_name; /* check free_modprobe_argv() */
|
|
argv[4] = NULL;
|
|
|
|
info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
|
|
NULL, free_modprobe_argv, NULL);
|
|
if (!info)
|
|
goto free_module_name;
|
|
|
|
return call_usermodehelper_exec(info, wait | UMH_KILLABLE);
|
|
|
|
free_module_name:
|
|
kfree(module_name);
|
|
free_argv:
|
|
kfree(argv);
|
|
out:
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/**
|
|
* __request_module - try to load a kernel module
|
|
* @wait: wait (or not) for the operation to complete
|
|
* @fmt: printf style format string for the name of the module
|
|
* @...: arguments as specified in the format string
|
|
*
|
|
* Load a module using the user mode module loader. The function returns
|
|
* zero on success or a negative errno code or positive exit code from
|
|
* "modprobe" on failure. Note that a successful module load does not mean
|
|
* the module did not then unload and exit on an error of its own. Callers
|
|
* must check that the service they requested is now available not blindly
|
|
* invoke it.
|
|
*
|
|
* If module auto-loading support is disabled then this function
|
|
* simply returns -ENOENT.
|
|
*/
|
|
int __request_module(bool wait, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char module_name[MODULE_NAME_LEN];
|
|
int ret;
|
|
|
|
/*
|
|
* We don't allow synchronous module loading from async. Module
|
|
* init may invoke async_synchronize_full() which will end up
|
|
* waiting for this task which already is waiting for the module
|
|
* loading to complete, leading to a deadlock.
|
|
*/
|
|
WARN_ON_ONCE(wait && current_is_async());
|
|
|
|
if (!modprobe_path[0])
|
|
return -ENOENT;
|
|
|
|
va_start(args, fmt);
|
|
ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
|
|
va_end(args);
|
|
if (ret >= MODULE_NAME_LEN)
|
|
return -ENAMETOOLONG;
|
|
|
|
ret = security_kernel_module_request(module_name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (atomic_dec_if_positive(&kmod_concurrent_max) < 0) {
|
|
pr_warn_ratelimited("request_module: kmod_concurrent_max (%u) close to 0 (max_modprobes: %u), for module %s, throttling...",
|
|
atomic_read(&kmod_concurrent_max),
|
|
MAX_KMOD_CONCURRENT, module_name);
|
|
ret = wait_event_killable_timeout(kmod_wq,
|
|
atomic_dec_if_positive(&kmod_concurrent_max) >= 0,
|
|
MAX_KMOD_ALL_BUSY_TIMEOUT * HZ);
|
|
if (!ret) {
|
|
pr_warn_ratelimited("request_module: modprobe %s cannot be processed, kmod busy with %d threads for more than %d seconds now",
|
|
module_name, MAX_KMOD_CONCURRENT, MAX_KMOD_ALL_BUSY_TIMEOUT);
|
|
return -ETIME;
|
|
} else if (ret == -ERESTARTSYS) {
|
|
pr_warn_ratelimited("request_module: sigkill sent for modprobe %s, giving up", module_name);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
trace_module_request(module_name, wait, _RET_IP_);
|
|
|
|
ret = call_modprobe(module_name, wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
|
|
|
|
atomic_inc(&kmod_concurrent_max);
|
|
wake_up(&kmod_wq);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(__request_module);
|