Commit 480b02df authored by Rusty Russell's avatar Rusty Russell

module: drop the lock while waiting for module to complete initialization.

This fixes "gave up waiting for init of module libcrc32c." which
happened at boot time due to multiple parallel module loads.

The problem was a deadlock: we wait for a module to finish
initializing, but we keep the module_lock mutex so it can't complete.
In particular, this could reasonably happen if a module does a
request_module() in its initialization routine.

So we change use_module() to return an errno rather than a bool, and if
it's -EBUSY we drop the lock and wait in the caller, then reaquire the
lock.
Reported-by: default avatarBrandon Philips <brandon@ifup.org>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Tested-by: default avatarBrandon Philips <brandon@ifup.org>
parent fedb3d27
...@@ -561,33 +561,26 @@ int use_module(struct module *a, struct module *b) ...@@ -561,33 +561,26 @@ int use_module(struct module *a, struct module *b)
struct module_use *use; struct module_use *use;
int no_warn, err; int no_warn, err;
if (b == NULL || already_uses(a, b)) return 1; if (b == NULL || already_uses(a, b))
/* If we're interrupted or time out, we fail. */
if (wait_event_interruptible_timeout(
module_wq, (err = strong_try_module_get(b)) != -EBUSY,
30 * HZ) <= 0) {
printk("%s: gave up waiting for init of module %s.\n",
a->name, b->name);
return 0; return 0;
}
/* If strong_try_module_get() returned a different error, we fail. */ /* If we're interrupted or time out, we fail. */
err = strong_try_module_get(b);
if (err) if (err)
return 0; return err;
DEBUGP("Allocating new usage for %s.\n", a->name); DEBUGP("Allocating new usage for %s.\n", a->name);
use = kmalloc(sizeof(*use), GFP_ATOMIC); use = kmalloc(sizeof(*use), GFP_ATOMIC);
if (!use) { if (!use) {
printk("%s: out of memory loading\n", a->name); printk("%s: out of memory loading\n", a->name);
module_put(b); module_put(b);
return 0; return -ENOMEM;
} }
use->module_which_uses = a; use->module_which_uses = a;
list_add(&use->list, &b->modules_which_use_me); list_add(&use->list, &b->modules_which_use_me);
no_warn = sysfs_create_link(b->holders_dir, &a->mkobj.kobj, a->name); no_warn = sysfs_create_link(b->holders_dir, &a->mkobj.kobj, a->name);
return 1; return 0;
} }
EXPORT_SYMBOL_GPL(use_module); EXPORT_SYMBOL_GPL(use_module);
...@@ -880,7 +873,7 @@ static inline void module_unload_free(struct module *mod) ...@@ -880,7 +873,7 @@ static inline void module_unload_free(struct module *mod)
int use_module(struct module *a, struct module *b) int use_module(struct module *a, struct module *b)
{ {
return strong_try_module_get(b) == 0; return strong_try_module_get(b);
} }
EXPORT_SYMBOL_GPL(use_module); EXPORT_SYMBOL_GPL(use_module);
...@@ -1051,17 +1044,39 @@ static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs, ...@@ -1051,17 +1044,39 @@ static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs,
struct module *owner; struct module *owner;
const struct kernel_symbol *sym; const struct kernel_symbol *sym;
const unsigned long *crc; const unsigned long *crc;
DEFINE_WAIT(wait);
int err;
long timeleft = 30 * HZ;
again:
sym = find_symbol(name, &owner, &crc, sym = find_symbol(name, &owner, &crc,
!(mod->taints & (1 << TAINT_PROPRIETARY_MODULE)), true); !(mod->taints & (1 << TAINT_PROPRIETARY_MODULE)), true);
/* use_module can fail due to OOM, if (!sym)
or module initialization or unloading */ return NULL;
if (sym) {
if (!check_version(sechdrs, versindex, name, mod, crc, owner) if (!check_version(sechdrs, versindex, name, mod, crc, owner))
|| !use_module(mod, owner)) return NULL;
sym = NULL;
prepare_to_wait(&module_wq, &wait, TASK_INTERRUPTIBLE);
err = use_module(mod, owner);
if (likely(!err) || err != -EBUSY || signal_pending(current)) {
finish_wait(&module_wq, &wait);
return err ? NULL : sym;
} }
return sym;
/* Module is still loading. Drop lock and wait. */
mutex_unlock(&module_mutex);
timeleft = schedule_timeout(timeleft);
mutex_lock(&module_mutex);
finish_wait(&module_wq, &wait);
/* Module might be gone entirely, or replaced. Re-lookup. */
if (timeleft)
goto again;
printk(KERN_WARNING "%s: gave up waiting for init of module %s.\n",
mod->name, owner->name);
return NULL;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment