module: Fix a few concurrent module loading races Concurrently starting multiple OSTs on a single OSS frequently triggers 30s deadlocks on module_mutex. This RHEL 6 kernel bug applies to any module that results in additional request_module() calls in its init callback. In Lustre, at least ptlrpc and libcfs are affected. This patch adapts fixes from the following upstream commits: 9bea7f2 module: fix bne2 "gave up waiting for init of module libcrc32c" be593f4 module: verify_export_symbols under the lock 3bafeb6 module: move find_module check to end 80a3d1b module: move sysfs exposure to end of load_module diff -rNup linux-2.6.32-431.20.3.el6.orig/kernel/module.c linux-2.6.32-431.20.3.el6/kernel/module.c --- linux-2.6.32-431.20.3.el6.orig/kernel/module.c 2014-06-07 05:42:39.000000000 +0800 +++ linux-2.6.32-431.20.3.el6/kernel/module.c 2014-07-28 00:04:43.000000000 +0800 @@ -658,6 +658,33 @@ static int already_uses(struct module *a } /* Module a uses b */ +static int ref_module(struct module *a, struct module *b) +{ + struct module_use *use; + int err; + + if (b == NULL || already_uses(a, b)) + return 0; + + /* If module isn't available, we fail. */ + err = strong_try_module_get(b); + if (err) + return err; + + DEBUGP("Allocating new usage for %s.\n", a->name); + use = kmalloc(sizeof(*use), GFP_ATOMIC); + if (!use) { + printk("%s: out of memory loading\n", a->name); + module_put(b); + return -ENOMEM; + } + + use->module_which_uses = a; + list_add(&use->list, &b->modules_which_use_me); + return 0; +} + +/* Module a uses b */ int use_module(struct module *a, struct module *b) { struct module_use *use; @@ -707,7 +734,6 @@ static void module_unload_free(struct mo module_put(i); list_del(&use->list); kfree(use); - sysfs_remove_link(i->holders_dir, mod->name); /* There can be at most one match. */ break; } @@ -962,6 +988,11 @@ static inline void module_unload_free(st { } +static inline int ref_module(struct module *a, struct module *b) +{ + return strong_try_module_get(b); +} + int use_module(struct module *a, struct module *b) { return strong_try_module_get(b) == 0; @@ -1130,24 +1161,60 @@ static inline int same_magic(const char static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs, unsigned int versindex, const char *name, - struct module *mod) + struct module *mod, + char ownername[]) { struct module *owner; const struct kernel_symbol *sym; const unsigned long *crc; + int err; + mutex_lock(&module_mutex); sym = find_symbol(name, &owner, &crc, !(mod->taints & (1 << TAINT_PROPRIETARY_MODULE)), true); - /* use_module can fail due to OOM, - or module initialization or unloading */ - if (sym) { - if (!check_version(sechdrs, versindex, name, mod, crc, owner) - || !use_module(mod, owner)) - sym = NULL; + if (!sym) + goto unlock; + + if (!check_version(sechdrs, versindex, name, mod, crc, owner)) { + sym = ERR_PTR(-EINVAL); + goto getname; } + + err = ref_module(mod, owner); + if (err) { + sym = ERR_PTR(err); + goto getname; + } + +getname: + /* We must make copy under the lock if we failed to get ref. */ + strncpy(ownername, module_name(owner), MODULE_NAME_LEN); +unlock: + mutex_unlock(&module_mutex); return sym; } +static const struct kernel_symbol *resolve_symbol_wait(Elf_Shdr *sechdrs, + unsigned int versindex, + const char *name, + struct module *mod) +{ + const struct kernel_symbol *ksym; + char ownername[MODULE_NAME_LEN]; + + mutex_unlock(&module_mutex); + if (wait_event_interruptible_timeout(module_wq, + !IS_ERR(ksym = resolve_symbol(sechdrs, versindex, name, + mod, ownername)) || + PTR_ERR(ksym) != -EBUSY, + msecs_to_jiffies(30 * MSEC_PER_SEC)) <= 0) { + printk(KERN_WARNING "%s: gave up waiting for init of module %s.\n", + mod->name, ownername); + } + mutex_lock(&module_mutex); + return ksym; +} + /* * /sys/module/foo/sections stuff * J. Corbet @@ -1375,6 +1442,30 @@ static inline void remove_notes_attrs(st #endif #ifdef CONFIG_SYSFS +static void add_usage_links(struct module *mod) +{ +#ifdef CONFIG_MODULE_UNLOAD + struct module_use *use; + int nowarn; + + list_for_each_entry(use, &mod->modules_which_use_me, list) { + nowarn = sysfs_create_link(use->module_which_uses->holders_dir, + &mod->mkobj.kobj, mod->name); + } +#endif +} + +static void del_usage_links(struct module *mod) +{ +#ifdef CONFIG_MODULE_UNLOAD + struct module_use *use; + + list_for_each_entry(use, &mod->modules_which_use_me, list) + sysfs_remove_link(use->module_which_uses->holders_dir, mod->name); +#endif +} + + int module_add_modinfo_attrs(struct module *mod) { struct module_attribute *attr; @@ -1456,6 +1547,10 @@ int mod_sysfs_setup(struct module *mod, { int err; + err = mod_sysfs_init(mod); + if (err) + goto out; + mod->holders_dir = kobject_create_and_add("holders", &mod->mkobj.kobj); if (!mod->holders_dir) { err = -ENOMEM; @@ -1470,6 +1565,8 @@ int mod_sysfs_setup(struct module *mod, if (err) goto out_unreg_param; + add_usage_links(mod); + kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD); return 0; @@ -1479,6 +1576,7 @@ out_unreg_holders: kobject_put(mod->holders_dir); out_unreg: kobject_put(&mod->mkobj.kobj); +out: return err; } @@ -1493,10 +1591,15 @@ static void mod_sysfs_fini(struct module { } +static void del_usage_links(struct module *mod) +{ +} + #endif /* CONFIG_SYSFS */ static void mod_kobject_remove(struct module *mod) { + del_usage_links(mod); module_remove_modinfo_attrs(mod); module_param_sysfs_remove(mod); kobject_put(mod->mkobj.drivers_dir); @@ -1576,6 +1679,8 @@ EXPORT_SYMBOL_GPL(__symbol_get); /* * Ensure that an exported symbol [global namespace] does not already exist * in the kernel or in some other module's exported symbol table. + * + * You must hold the module_mutex. */ static int verify_export_symbols(struct module *mod) { @@ -1641,21 +1746,23 @@ static int simplify_symbols(Elf_Shdr *se break; case SHN_UNDEF: - ksym = resolve_symbol(sechdrs, versindex, - strtab + sym[i].st_name, mod); + ksym = resolve_symbol_wait(sechdrs, versindex, + strtab + sym[i].st_name, + mod); /* Ok if resolved. */ - if (ksym) { + if (ksym && !IS_ERR(ksym)) { sym[i].st_value = ksym->value; break; } /* Ok if weak. */ - if (ELF_ST_BIND(sym[i].st_info) == STB_WEAK) + if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK) break; - printk(KERN_WARNING "%s: Unknown symbol %s\n", - mod->name, strtab + sym[i].st_name); - ret = -ENOENT; + printk(KERN_WARNING "%s: Unknown symbol %s (err %li)\n", + mod->name, strtab + sym[i].st_name, + PTR_ERR(ksym)); + ret = PTR_ERR(ksym) ?: -ENOENT; break; default: @@ -2240,11 +2347,6 @@ static noinline struct module *load_modu goto free_mod; } - if (find_module(mod->name)) { - err = -EEXIST; - goto free_mod; - } - mod->state = MODULE_STATE_COMING; /* Allow arches to frob section contents and sizes. */ @@ -2338,11 +2440,6 @@ static noinline struct module *load_modu /* Now we've moved module, initialize linked lists, etc. */ module_unload_init(mod); - /* add kobject, so we can reference it. */ - err = mod_sysfs_init(mod); - if (err) - goto free_unload; - /* Set up license info based on the info section */ set_license(mod, get_modinfo(sechdrs, infoindex, "license")); @@ -2461,11 +2558,6 @@ static noinline struct module *load_modu goto cleanup; } - /* Find duplicate symbols */ - err = verify_export_symbols(mod); - if (err < 0) - goto cleanup; - /* Set up and sort exception table */ mod->extable = section_objs(hdr, sechdrs, secstrings, "__ex_table", sizeof(*mod->extable), &mod->num_exentries); @@ -2521,6 +2613,16 @@ static noinline struct module *load_modu * function to insert in a way safe to concurrent readers. * The mutex protects against concurrent writers. */ + if (find_module(mod->name)) { + err = -EEXIST; + goto already_exists; + } + + /* Find duplicate symbols */ + err = verify_export_symbols(mod); + if (err < 0) + goto already_exists; + list_add_rcu(&mod->list, &modules); err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, NULL); @@ -2530,6 +2632,7 @@ static noinline struct module *load_modu err = mod_sysfs_setup(mod, mod->kp, mod->num_kp); if (err < 0) goto unlink; + add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs); add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs); @@ -2544,15 +2647,13 @@ static noinline struct module *load_modu unlink: /* Unlink carefully: kallsyms could be walking list. */ list_del_rcu(&mod->list); + already_exists: synchronize_sched(); module_arch_cleanup(mod); ddebug: dynamic_debug_remove(debug); cleanup: free_modinfo(mod); - kobject_del(&mod->mkobj.kobj); - kobject_put(&mod->mkobj.kobj); - free_unload: module_unload_free(mod); #if defined(CONFIG_MODULE_UNLOAD) && defined(CONFIG_SMP) percpu_modfree(mod->refptr);