Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75676500 authored by Rusty Russell's avatar Rusty Russell
Browse files

module: make locking more fine-grained.



Kay Sievers <kay.sievers@vrfy.org> reports that we still have some
contention over module loading which is slowing boot.

Linus also disliked a previous "drop lock and regrab" patch to fix the
bne2 "gave up waiting for init of module libcrc32c" message.

This is more ambitious: we only grab the lock where we need it.

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Cc: Brandon Philips <brandon@ifup.org>
Cc: Kay Sievers <kay.sievers@vrfy.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
parent 6407ebb2
Loading
Loading
Loading
Loading
+42 −23
Original line number Original line Diff line number Diff line
@@ -72,7 +72,11 @@
/* If this is set, the section belongs in the init part of the module */
/* If this is set, the section belongs in the init part of the module */
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))


/* List of modules, protected by module_mutex or preempt_disable
/*
 * Mutex protects:
 * 1) List of modules (also safely readable with preempt_disable),
 * 2) module_use links,
 * 3) module_addr_min/module_addr_max.
 * (delete uses stop_machine/add uses RCU list operations). */
 * (delete uses stop_machine/add uses RCU list operations). */
DEFINE_MUTEX(module_mutex);
DEFINE_MUTEX(module_mutex);
EXPORT_SYMBOL_GPL(module_mutex);
EXPORT_SYMBOL_GPL(module_mutex);
@@ -90,7 +94,8 @@ static DECLARE_WAIT_QUEUE_HEAD(module_wq);


static BLOCKING_NOTIFIER_HEAD(module_notify_list);
static BLOCKING_NOTIFIER_HEAD(module_notify_list);


/* Bounds of module allocation, for speeding __module_address */
/* Bounds of module allocation, for speeding __module_address.
 * Protected by module_mutex. */
static unsigned long module_addr_min = -1UL, module_addr_max = 0;
static unsigned long module_addr_min = -1UL, module_addr_max = 0;


int register_module_notifier(struct notifier_block * nb)
int register_module_notifier(struct notifier_block * nb)
@@ -329,7 +334,7 @@ static bool find_symbol_in_section(const struct symsearch *syms,
}
}


/* Find a symbol and return it, along with, (optional) crc and
/* Find a symbol and return it, along with, (optional) crc and
 * (optional) module which owns it */
 * (optional) module which owns it.  Needs preempt disabled or module_mutex. */
const struct kernel_symbol *find_symbol(const char *name,
const struct kernel_symbol *find_symbol(const char *name,
					struct module **owner,
					struct module **owner,
					const unsigned long **crc,
					const unsigned long **crc,
@@ -576,7 +581,7 @@ static int add_module_usage(struct module *a, struct module *b)
	return 0;
	return 0;
}
}


/* Module a uses b */
/* Module a uses b: caller needs module_mutex() */
int use_module(struct module *a, struct module *b)
int use_module(struct module *a, struct module *b)
{
{
	int err;
	int err;
@@ -610,6 +615,7 @@ static void module_unload_free(struct module *mod)
{
{
	struct module_use *use, *tmp;
	struct module_use *use, *tmp;


	mutex_lock(&module_mutex);
	list_for_each_entry_safe(use, tmp, &mod->target_list, target_list) {
	list_for_each_entry_safe(use, tmp, &mod->target_list, target_list) {
		struct module *i = use->target;
		struct module *i = use->target;
		DEBUGP("%s unusing %s\n", mod->name, i->name);
		DEBUGP("%s unusing %s\n", mod->name, i->name);
@@ -618,6 +624,7 @@ static void module_unload_free(struct module *mod)
		list_del(&use->target_list);
		list_del(&use->target_list);
		kfree(use);
		kfree(use);
	}
	}
	mutex_unlock(&module_mutex);
}
}


#ifdef CONFIG_MODULE_FORCE_UNLOAD
#ifdef CONFIG_MODULE_FORCE_UNLOAD
@@ -784,12 +791,13 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
	blocking_notifier_call_chain(&module_notify_list,
	blocking_notifier_call_chain(&module_notify_list,
				     MODULE_STATE_GOING, mod);
				     MODULE_STATE_GOING, mod);
	async_synchronize_full();
	async_synchronize_full();
	mutex_lock(&module_mutex);

	/* Store the name of the last unloaded module for diagnostic purposes */
	/* Store the name of the last unloaded module for diagnostic purposes */
	strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
	strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
	ddebug_remove_module(mod->name);
	ddebug_remove_module(mod->name);
	free_module(mod);


	free_module(mod);
	return 0;
out:
out:
	mutex_unlock(&module_mutex);
	mutex_unlock(&module_mutex);
	return ret;
	return ret;
@@ -1006,6 +1014,8 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs,
{
{
	const unsigned long *crc;
	const unsigned long *crc;


	/* Since this should be found in kernel (which can't be removed),
	 * no locking is necessary. */
	if (!find_symbol(MODULE_SYMBOL_PREFIX "module_layout", NULL,
	if (!find_symbol(MODULE_SYMBOL_PREFIX "module_layout", NULL,
			 &crc, true, false))
			 &crc, true, false))
		BUG();
		BUG();
@@ -1048,8 +1058,7 @@ static inline int same_magic(const char *amagic, const char *bmagic,
}
}
#endif /* CONFIG_MODVERSIONS */
#endif /* CONFIG_MODVERSIONS */


/* Resolve a symbol for this module.  I.e. if we find one, record usage.
/* Resolve a symbol for this module.  I.e. if we find one, record usage. */
   Must be holding module_mutex. */
static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs,
static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs,
						  unsigned int versindex,
						  unsigned int versindex,
						  const char *name,
						  const char *name,
@@ -1059,6 +1068,7 @@ static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs,
	const struct kernel_symbol *sym;
	const struct kernel_symbol *sym;
	const unsigned long *crc;
	const unsigned long *crc;


	mutex_lock(&module_mutex);
	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,
	/* use_module can fail due to OOM,
@@ -1068,6 +1078,7 @@ static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs,
		    || !use_module(mod, owner))
		    || !use_module(mod, owner))
			sym = NULL;
			sym = NULL;
	}
	}
	mutex_unlock(&module_mutex);
	return sym;
	return sym;
}
}


@@ -1306,10 +1317,12 @@ static void add_usage_links(struct module *mod)
	struct module_use *use;
	struct module_use *use;
	int nowarn;
	int nowarn;


	mutex_lock(&module_mutex);
	list_for_each_entry(use, &mod->target_list, target_list) {
	list_for_each_entry(use, &mod->target_list, target_list) {
		nowarn = sysfs_create_link(use->target->holders_dir,
		nowarn = sysfs_create_link(use->target->holders_dir,
					   &mod->mkobj.kobj, mod->name);
					   &mod->mkobj.kobj, mod->name);
	}
	}
	mutex_unlock(&module_mutex);
#endif
#endif
}
}


@@ -1318,8 +1331,10 @@ static void del_usage_links(struct module *mod)
#ifdef CONFIG_MODULE_UNLOAD
#ifdef CONFIG_MODULE_UNLOAD
	struct module_use *use;
	struct module_use *use;


	mutex_lock(&module_mutex);
	list_for_each_entry(use, &mod->target_list, target_list)
	list_for_each_entry(use, &mod->target_list, target_list)
		sysfs_remove_link(use->target->holders_dir, mod->name);
		sysfs_remove_link(use->target->holders_dir, mod->name);
	mutex_unlock(&module_mutex);
#endif
#endif
}
}


@@ -1497,13 +1512,15 @@ static int __unlink_module(void *_mod)
	return 0;
	return 0;
}
}


/* Free a module, remove from lists, etc (must hold module_mutex). */
/* Free a module, remove from lists, etc. */
static void free_module(struct module *mod)
static void free_module(struct module *mod)
{
{
	trace_module_free(mod);
	trace_module_free(mod);


	/* Delete from various lists */
	/* Delete from various lists */
	mutex_lock(&module_mutex);
	stop_machine(__unlink_module, mod, NULL);
	stop_machine(__unlink_module, mod, NULL);
	mutex_unlock(&module_mutex);
	remove_notes_attrs(mod);
	remove_notes_attrs(mod);
	remove_sect_attrs(mod);
	remove_sect_attrs(mod);
	mod_kobject_remove(mod);
	mod_kobject_remove(mod);
@@ -1575,7 +1592,14 @@ static int verify_export_symbols(struct module *mod)


	for (i = 0; i < ARRAY_SIZE(arr); i++) {
	for (i = 0; i < ARRAY_SIZE(arr); i++) {
		for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) {
		for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) {
			if (find_symbol(s->name, &owner, NULL, true, false)) {
			const struct kernel_symbol *sym;

			/* Stopping preemption makes find_symbol safe. */
			preempt_disable();
			sym = find_symbol(s->name, &owner, NULL, true, false);
			preempt_enable();

			if (sym) {
				printk(KERN_ERR
				printk(KERN_ERR
				       "%s: exports duplicate symbol %s"
				       "%s: exports duplicate symbol %s"
				       " (owned by %s)\n",
				       " (owned by %s)\n",
@@ -2021,11 +2045,13 @@ static void *module_alloc_update_bounds(unsigned long size)
	void *ret = module_alloc(size);
	void *ret = module_alloc(size);


	if (ret) {
	if (ret) {
		mutex_lock(&module_mutex);
		/* Update module bounds. */
		/* Update module bounds. */
		if ((unsigned long)ret < module_addr_min)
		if ((unsigned long)ret < module_addr_min)
			module_addr_min = (unsigned long)ret;
			module_addr_min = (unsigned long)ret;
		if ((unsigned long)ret + size > module_addr_max)
		if ((unsigned long)ret + size > module_addr_max)
			module_addr_max = (unsigned long)ret + size;
			module_addr_max = (unsigned long)ret + size;
		mutex_unlock(&module_mutex);
	}
	}
	return ret;
	return ret;
}
}
@@ -2482,7 +2508,9 @@ static noinline struct module *load_module(void __user *umod,
	 * function to insert in a way safe to concurrent readers.
	 * function to insert in a way safe to concurrent readers.
	 * The mutex protects against concurrent writers.
	 * The mutex protects against concurrent writers.
	 */
	 */
	mutex_lock(&module_mutex);
	list_add_rcu(&mod->list, &modules);
	list_add_rcu(&mod->list, &modules);
	mutex_unlock(&module_mutex);


	err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, NULL);
	err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, NULL);
	if (err < 0)
	if (err < 0)
@@ -2504,8 +2532,10 @@ static noinline struct module *load_module(void __user *umod,
	return mod;
	return mod;


 unlink:
 unlink:
	mutex_lock(&module_mutex);
	/* Unlink carefully: kallsyms could be walking list. */
	/* Unlink carefully: kallsyms could be walking list. */
	list_del_rcu(&mod->list);
	list_del_rcu(&mod->list);
	mutex_unlock(&module_mutex);
	synchronize_sched();
	synchronize_sched();
	module_arch_cleanup(mod);
	module_arch_cleanup(mod);
 cleanup:
 cleanup:
@@ -2556,19 +2586,10 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
	if (!capable(CAP_SYS_MODULE) || modules_disabled)
	if (!capable(CAP_SYS_MODULE) || modules_disabled)
		return -EPERM;
		return -EPERM;


	/* Only one module load at a time, please */
	if (mutex_lock_interruptible(&module_mutex) != 0)
		return -EINTR;

	/* Do all the hard work */
	/* Do all the hard work */
	mod = load_module(umod, len, uargs);
	mod = load_module(umod, len, uargs);
	if (IS_ERR(mod)) {
	if (IS_ERR(mod))
		mutex_unlock(&module_mutex);
		return PTR_ERR(mod);
		return PTR_ERR(mod);
	}

	/* Drop lock so they can recurse */
	mutex_unlock(&module_mutex);


	blocking_notifier_call_chain(&module_notify_list,
	blocking_notifier_call_chain(&module_notify_list,
			MODULE_STATE_COMING, mod);
			MODULE_STATE_COMING, mod);
@@ -2585,9 +2606,7 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
		module_put(mod);
		module_put(mod);
		blocking_notifier_call_chain(&module_notify_list,
		blocking_notifier_call_chain(&module_notify_list,
					     MODULE_STATE_GOING, mod);
					     MODULE_STATE_GOING, mod);
		mutex_lock(&module_mutex);
		free_module(mod);
		free_module(mod);
		mutex_unlock(&module_mutex);
		wake_up(&module_wq);
		wake_up(&module_wq);
		return ret;
		return ret;
	}
	}