Commit 12cabdd2 authored by Rusty Russell's avatar Rusty Russell Committed by Linus Torvalds

[PATCH] Module init reentry fix

In some configurations, parport and bttv request a module inside their
module_init function.  Drop the lock around mod->init(), change
module->live to module->state so we can detect modules which are in
init.
parent dd820501
...@@ -116,10 +116,16 @@ struct module_ref ...@@ -116,10 +116,16 @@ struct module_ref
atomic_t count; atomic_t count;
} ____cacheline_aligned; } ____cacheline_aligned;
enum module_state
{
MODULE_STATE_LIVE,
MODULE_STATE_COMING,
MODULE_STATE_GOING,
};
struct module struct module
{ {
/* Am I live (yet)? */ enum module_state state;
int live;
/* Member of list of modules */ /* Member of list of modules */
struct list_head list; struct list_head list;
...@@ -177,6 +183,14 @@ struct module ...@@ -177,6 +183,14 @@ struct module
char args[0]; char args[0];
}; };
/* FIXME: It'd be nice to isolate modules during init, too, so they
aren't used before they (may) fail. But presently too much code
(IDE & SCSI) require entry into the module during init.*/
static inline int module_is_live(struct module *mod)
{
return mod->state != MODULE_STATE_GOING;
}
#ifdef CONFIG_MODULE_UNLOAD #ifdef CONFIG_MODULE_UNLOAD
void __symbol_put(const char *symbol); void __symbol_put(const char *symbol);
...@@ -195,7 +209,7 @@ static inline int try_module_get(struct module *module) ...@@ -195,7 +209,7 @@ static inline int try_module_get(struct module *module)
if (module) { if (module) {
unsigned int cpu = get_cpu(); unsigned int cpu = get_cpu();
if (likely(module->live)) if (likely(module_is_live(module)))
local_inc(&module->ref[cpu].count); local_inc(&module->ref[cpu].count);
else else
ret = 0; ret = 0;
...@@ -210,7 +224,7 @@ static inline void module_put(struct module *module) ...@@ -210,7 +224,7 @@ static inline void module_put(struct module *module)
unsigned int cpu = get_cpu(); unsigned int cpu = get_cpu();
local_dec(&module->ref[cpu].count); local_dec(&module->ref[cpu].count);
/* Maybe they're waiting for us to drop reference? */ /* Maybe they're waiting for us to drop reference? */
if (unlikely(!module->live)) if (unlikely(!module_is_live(module)))
wake_up_process(module->waiter); wake_up_process(module->waiter);
put_cpu(); put_cpu();
} }
...@@ -219,7 +233,7 @@ static inline void module_put(struct module *module) ...@@ -219,7 +233,7 @@ static inline void module_put(struct module *module)
#else /*!CONFIG_MODULE_UNLOAD*/ #else /*!CONFIG_MODULE_UNLOAD*/
static inline int try_module_get(struct module *module) static inline int try_module_get(struct module *module)
{ {
return !module || module->live; return !module || module_is_live(module);
} }
static inline void module_put(struct module *module) static inline void module_put(struct module *module)
{ {
......
...@@ -44,6 +44,14 @@ ...@@ -44,6 +44,14 @@
static DECLARE_MUTEX(module_mutex); static DECLARE_MUTEX(module_mutex);
LIST_HEAD(modules); /* FIXME: Accessed w/o lock on oops by some archs */ LIST_HEAD(modules); /* FIXME: Accessed w/o lock on oops by some archs */
/* We require a truly strong try_module_get() */
static inline int strong_try_module_get(struct module *mod)
{
if (mod && mod->state == MODULE_STATE_COMING)
return 0;
return try_module_get(mod);
}
/* Convenient structure for holding init and core sizes */ /* Convenient structure for holding init and core sizes */
struct sizes struct sizes
{ {
...@@ -378,7 +386,7 @@ sys_delete_module(const char *name_user, unsigned int flags) ...@@ -378,7 +386,7 @@ sys_delete_module(const char *name_user, unsigned int flags)
} }
/* Already dying? */ /* Already dying? */
if (!mod->live) { if (mod->state == MODULE_STATE_GOING) {
/* FIXME: if (force), slam module count and wake up /* FIXME: if (force), slam module count and wake up
waiter --RR */ waiter --RR */
DEBUGP("%s already dying\n", mod->name); DEBUGP("%s already dying\n", mod->name);
...@@ -386,6 +394,16 @@ sys_delete_module(const char *name_user, unsigned int flags) ...@@ -386,6 +394,16 @@ sys_delete_module(const char *name_user, unsigned int flags)
goto out; goto out;
} }
/* Coming up? Allow force on stuck modules. */
if (mod->state == MODULE_STATE_COMING) {
forced = try_force(flags);
if (!forced) {
/* This module can't be removed */
ret = -EBUSY;
goto out;
}
}
if (!mod->exit || mod->unsafe) { if (!mod->exit || mod->unsafe) {
forced = try_force(flags); forced = try_force(flags);
if (!forced) { if (!forced) {
...@@ -407,7 +425,7 @@ sys_delete_module(const char *name_user, unsigned int flags) ...@@ -407,7 +425,7 @@ sys_delete_module(const char *name_user, unsigned int flags)
ret = -EWOULDBLOCK; ret = -EWOULDBLOCK;
} else { } else {
mod->waiter = current; mod->waiter = current;
mod->live = 0; mod->state = MODULE_STATE_GOING;
} }
restart_refcounts(); restart_refcounts();
...@@ -507,7 +525,7 @@ static inline void module_unload_free(struct module *mod) ...@@ -507,7 +525,7 @@ static inline void module_unload_free(struct module *mod)
static inline int use_module(struct module *a, struct module *b) static inline int use_module(struct module *a, struct module *b)
{ {
return try_module_get(b); return strong_try_module_get(b);
} }
static inline void module_unload_init(struct module *mod) static inline void module_unload_init(struct module *mod)
...@@ -578,7 +596,7 @@ void *__symbol_get(const char *symbol) ...@@ -578,7 +596,7 @@ void *__symbol_get(const char *symbol)
spin_lock_irqsave(&modlist_lock, flags); spin_lock_irqsave(&modlist_lock, flags);
value = __find_symbol(symbol, &ksg); value = __find_symbol(symbol, &ksg);
if (value && !try_module_get(ksg->owner)) if (value && !strong_try_module_get(ksg->owner))
value = 0; value = 0;
spin_unlock_irqrestore(&modlist_lock, flags); spin_unlock_irqrestore(&modlist_lock, flags);
...@@ -935,12 +953,8 @@ static struct module *load_module(void *umod, ...@@ -935,12 +953,8 @@ static struct module *load_module(void *umod,
goto free_mod; goto free_mod;
} }
/* Initialize the lists, since they will be list_del'd if init fails */
INIT_LIST_HEAD(&mod->extable.list);
INIT_LIST_HEAD(&mod->list);
INIT_LIST_HEAD(&mod->symbols.list);
mod->symbols.owner = mod; mod->symbols.owner = mod;
mod->live = 0; mod->state = MODULE_STATE_COMING;
module_unload_init(mod); module_unload_init(mod);
/* How much space will we need? (Common area in first) */ /* How much space will we need? (Common area in first) */
...@@ -1097,51 +1111,40 @@ sys_init_module(void *umod, ...@@ -1097,51 +1111,40 @@ sys_init_module(void *umod,
flush_icache_range((unsigned long)mod->module_core, flush_icache_range((unsigned long)mod->module_core,
(unsigned long)mod->module_core + mod->core_size); (unsigned long)mod->module_core + mod->core_size);
/* Now sew it into exception list (just in case...). */ /* Now sew it into the lists. They won't access us, since
strong_try_module_get() will fail. */
spin_lock_irq(&modlist_lock); spin_lock_irq(&modlist_lock);
list_add(&mod->extable.list, &extables); list_add(&mod->extable.list, &extables);
list_add_tail(&mod->symbols.list, &symbols);
spin_unlock_irq(&modlist_lock); spin_unlock_irq(&modlist_lock);
list_add(&mod->list, &modules);
/* Drop lock so they can recurse */
up(&module_mutex);
/* Note, setting the mod->live to 1 here is safe because we haven't
* linked the module into the system's kernel symbol table yet,
* which means that the only way any other kernel code can call
* into this module right now is if this module hands out entry
* pointers to the other code. We assume that no module hands out
* entry pointers to the rest of the kernel unless it is ready to
* have them used.
*/
mod->live = 1;
/* Start the module */ /* Start the module */
ret = mod->init ? mod->init() : 0; ret = mod->init ? mod->init() : 0;
if (ret < 0) { if (ret < 0) {
/* Init routine failed: abort. Try to protect us from /* Init routine failed: abort. Try to protect us from
buggy refcounters. */ buggy refcounters. */
mod->state = MODULE_STATE_GOING;
synchronize_kernel(); synchronize_kernel();
if (mod->unsafe) { if (mod->unsafe)
printk(KERN_ERR "%s: module is now stuck!\n", printk(KERN_ERR "%s: module is now stuck!\n",
mod->name); mod->name);
/* Mark it "live" so that they can force else {
deletion later, and we don't keep getting down(&module_mutex);
woken on every decrement. */
} else {
mod->live = 0;
free_module(mod); free_module(mod);
up(&module_mutex);
} }
up(&module_mutex);
return ret; return ret;
} }
/* Now it's a first class citizen! */ /* Now it's a first class citizen! */
spin_lock_irq(&modlist_lock); mod->state = MODULE_STATE_LIVE;
list_add_tail(&mod->symbols.list, &symbols);
spin_unlock_irq(&modlist_lock);
list_add(&mod->list, &modules);
module_free(mod, mod->module_init); module_free(mod, mod->module_init);
mod->module_init = NULL; mod->module_init = NULL;
/* All ok! */
up(&module_mutex);
return 0; return 0;
} }
......
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