[PATCH] module: track module names currently loading

From: Brandon Philips
Date: Tue Jun 01 2010 - 19:13:54 EST


After "make locking more fine-grained" multiple instances of the same module
can reach mod_sysfs_init() and spew -EEXIST from kobject_add_internal().

Track the modules that are currently in load_module() but are not in the
modules list yet so we can kick out duplicate modules early.

NOTE: I didn't use mod->list to track the loading modules because I
couldn't figure out how to unwind from an error in a nice way.

Signed-off-by: Brandon Philips <bphilips@xxxxxxx>
---
kernel/module.c | 45 +++++++++++++++++++++++++++++++++++++++++----
1 file changed, 41 insertions(+), 4 deletions(-)

Index: linux-2.6/kernel/module.c
===================================================================
--- linux-2.6.orig/kernel/module.c
+++ linux-2.6/kernel/module.c
@@ -77,6 +77,7 @@
DEFINE_MUTEX(module_mutex);
EXPORT_SYMBOL_GPL(module_mutex);
static LIST_HEAD(modules);
+static LIST_HEAD(modules_loading);
#ifdef CONFIG_KGDB_KDB
struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */
#endif /* CONFIG_KGDB_KDB */
@@ -2055,6 +2056,23 @@ static inline void kmemleak_load_module(
}
#endif

+/* Track module names running through load_module but not in modules list */
+struct module_name {
+ struct list_head list;
+ char name[MODULE_NAME_LEN];
+};
+
+static struct module_name *find_module_loading(const char *name)
+{
+ struct module_name *mod_name;
+
+ list_for_each_entry(mod_name, &modules_loading, list) {
+ if (strcmp(mod_name->name, name) == 0)
+ return mod_name;
+ }
+ return NULL;
+}
+
/* Allocate and load the module: note that size of section 0 is always
zero, and we rely on this for optional sections. */
static noinline struct module *load_module(void __user *umod,
@@ -2074,6 +2092,7 @@ static noinline struct module *load_modu
void *ptr = NULL; /* Stops spurious gcc warning */
unsigned long symoffs, stroffs, *strmap;
void __percpu *percpu;
+ struct module_name *mod_name;

mm_segment_t old_fs;

@@ -2198,24 +2217,36 @@ static noinline struct module *load_modu
goto free_mod;
}

- if (find_module(mod->name)) {
- err = -EEXIST;
+ mod_name = kzalloc(sizeof(*mod_name), GFP_KERNEL);
+ if (!mod_name) {
+ err = -ENOMEM;
goto free_mod;
}
+ strcpy(mod_name->name, mod->name);
+ INIT_LIST_HEAD(&mod_name->list);
+
+ mutex_lock(&module_mutex);
+ if (find_module(mod->name) || find_module_loading(mod->name)) {
+ mutex_unlock(&module_mutex);
+ err = -EEXIST;
+ goto free_mod_name;
+ }
+ list_add(&mod_name->list, &modules_loading);
+ mutex_unlock(&module_mutex);

mod->state = MODULE_STATE_COMING;

/* Allow arches to frob section contents and sizes. */
err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod);
if (err < 0)
- goto free_mod;
+ goto del_loading;

if (pcpuindex) {
/* We have a special allocation for this section. */
err = percpu_modalloc(mod, sechdrs[pcpuindex].sh_size,
sechdrs[pcpuindex].sh_addralign);
if (err)
- goto free_mod;
+ goto del_loading;
sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
}
/* Keep this around for failure path. */
@@ -2486,6 +2517,8 @@ static noinline struct module *load_modu
* The mutex protects against concurrent writers.
*/
mutex_lock(&module_mutex);
+ list_del(&mod_name->list);
+ kfree(mod_name);
list_add_rcu(&mod->list, &modules);
mutex_unlock(&module_mutex);

@@ -2530,6 +2563,10 @@ static noinline struct module *load_modu
/* mod will be freed with core. Don't access it beyond this line! */
free_percpu:
free_percpu(percpu);
+ del_loading:
+ list_del(&mod_name->list);
+ free_mod_name:
+ kfree(mod_name);
free_mod:
kfree(args);
kfree(strmap);
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/