+++ /dev/null
-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 <corbet@lwn.net>
-@@ -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);