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

Commit d790be38 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'modules-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux

Pull module updates from Rusty Russell:
 "The exciting thing here is the getting rid of stop_machine on module
  removal.  This is possible by using a simple atomic_t for the counter,
  rather than our fancy per-cpu counter: it turns out that no one is
  doing a module increment per net packet, so the slowdown should be in
  the noise"

* tag 'modules-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux:
  param: do not set store func without write perm
  params: cleanup sysfs allocation
  kernel:module Fix coding style errors and warnings.
  module: Remove stop_machine from module unloading
  module: Replace module_ref with atomic_t refcnt
  lib/bug: Use RCU list ops for module_bug_list
  module: Unlink module with RCU synchronizing instead of stop_machine
  module: Wait for RCU synchronizing before releasing a module
parents 64ec45bf b0a65b0c
Loading
Loading
Loading
Loading
+1 −15
Original line number Original line Diff line number Diff line
@@ -210,20 +210,6 @@ enum module_state {
	MODULE_STATE_UNFORMED,	/* Still setting it up. */
	MODULE_STATE_UNFORMED,	/* Still setting it up. */
};
};


/**
 * struct module_ref - per cpu module reference counts
 * @incs: number of module get on this cpu
 * @decs: number of module put on this cpu
 *
 * We force an alignment on 8 or 16 bytes, so that alloc_percpu()
 * put @incs/@decs in same cache line, with no extra memory cost,
 * since alloc_percpu() is fine grained.
 */
struct module_ref {
	unsigned long incs;
	unsigned long decs;
} __attribute((aligned(2 * sizeof(unsigned long))));

struct module {
struct module {
	enum module_state state;
	enum module_state state;


@@ -367,7 +353,7 @@ struct module {
	/* Destruction function. */
	/* Destruction function. */
	void (*exit)(void);
	void (*exit)(void);


	struct module_ref __percpu *refptr;
	atomic_t refcnt;
#endif
#endif


#ifdef CONFIG_CONSTRUCTORS
#ifdef CONFIG_CONSTRUCTORS
+1 −1
Original line number Original line Diff line number Diff line
@@ -80,7 +80,7 @@ DECLARE_EVENT_CLASS(module_refcnt,


	TP_fast_assign(
	TP_fast_assign(
		__entry->ip	= ip;
		__entry->ip	= ip;
		__entry->refcnt	= __this_cpu_read(mod->refptr->incs) - __this_cpu_read(mod->refptr->decs);
		__entry->refcnt	= atomic_read(&mod->refcnt);
		__assign_str(name, mod->name);
		__assign_str(name, mod->name);
	),
	),


+75 −95
Original line number Original line Diff line number Diff line
@@ -42,7 +42,6 @@
#include <linux/vermagic.h>
#include <linux/vermagic.h>
#include <linux/notifier.h>
#include <linux/notifier.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/stop_machine.h>
#include <linux/device.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/mutex.h>
@@ -98,7 +97,7 @@
 * 1) List of modules (also safely readable with preempt_disable),
 * 1) List of modules (also safely readable with preempt_disable),
 * 2) module_use links,
 * 2) module_use links,
 * 3) module_addr_min/module_addr_max.
 * 3) module_addr_min/module_addr_max.
 * (delete uses stop_machine/add uses RCU list operations). */
 * (delete and add uses RCU list operations). */
DEFINE_MUTEX(module_mutex);
DEFINE_MUTEX(module_mutex);
EXPORT_SYMBOL_GPL(module_mutex);
EXPORT_SYMBOL_GPL(module_mutex);
static LIST_HEAD(modules);
static LIST_HEAD(modules);
@@ -628,18 +627,23 @@ static char last_unloaded_module[MODULE_NAME_LEN+1];


EXPORT_TRACEPOINT_SYMBOL(module_get);
EXPORT_TRACEPOINT_SYMBOL(module_get);


/* MODULE_REF_BASE is the base reference count by kmodule loader. */
#define MODULE_REF_BASE	1

/* Init the unload section of the module. */
/* Init the unload section of the module. */
static int module_unload_init(struct module *mod)
static int module_unload_init(struct module *mod)
{
{
	mod->refptr = alloc_percpu(struct module_ref);
	/*
	if (!mod->refptr)
	 * Initialize reference counter to MODULE_REF_BASE.
		return -ENOMEM;
	 * refcnt == 0 means module is going.
	 */
	atomic_set(&mod->refcnt, MODULE_REF_BASE);


	INIT_LIST_HEAD(&mod->source_list);
	INIT_LIST_HEAD(&mod->source_list);
	INIT_LIST_HEAD(&mod->target_list);
	INIT_LIST_HEAD(&mod->target_list);


	/* Hold reference count during initialization. */
	/* Hold reference count during initialization. */
	raw_cpu_write(mod->refptr->incs, 1);
	atomic_inc(&mod->refcnt);


	return 0;
	return 0;
}
}
@@ -721,8 +725,6 @@ static void module_unload_free(struct module *mod)
		kfree(use);
		kfree(use);
	}
	}
	mutex_unlock(&module_mutex);
	mutex_unlock(&module_mutex);

	free_percpu(mod->refptr);
}
}


#ifdef CONFIG_MODULE_FORCE_UNLOAD
#ifdef CONFIG_MODULE_FORCE_UNLOAD
@@ -740,60 +742,39 @@ static inline int try_force_unload(unsigned int flags)
}
}
#endif /* CONFIG_MODULE_FORCE_UNLOAD */
#endif /* CONFIG_MODULE_FORCE_UNLOAD */


struct stopref
/* Try to release refcount of module, 0 means success. */
static int try_release_module_ref(struct module *mod)
{
{
	struct module *mod;
	int ret;
	int flags;
	int *forced;
};


/* Whole machine is stopped with interrupts off when this runs. */
	/* Try to decrement refcnt which we set at loading */
static int __try_stop_module(void *_sref)
	ret = atomic_sub_return(MODULE_REF_BASE, &mod->refcnt);
{
	BUG_ON(ret < 0);
	struct stopref *sref = _sref;
	if (ret)
		/* Someone can put this right now, recover with checking */
		ret = atomic_add_unless(&mod->refcnt, MODULE_REF_BASE, 0);

	return ret;
}


static int try_stop_module(struct module *mod, int flags, int *forced)
{
	/* If it's not unused, quit unless we're forcing. */
	/* If it's not unused, quit unless we're forcing. */
	if (module_refcount(sref->mod) != 0) {
	if (try_release_module_ref(mod) != 0) {
		if (!(*sref->forced = try_force_unload(sref->flags)))
		*forced = try_force_unload(flags);
		if (!(*forced))
			return -EWOULDBLOCK;
			return -EWOULDBLOCK;
	}
	}


	/* Mark it as dying. */
	/* Mark it as dying. */
	sref->mod->state = MODULE_STATE_GOING;
	mod->state = MODULE_STATE_GOING;
	return 0;
}

static int try_stop_module(struct module *mod, int flags, int *forced)
{
	struct stopref sref = { mod, flags, forced };


	return stop_machine(__try_stop_module, &sref, NULL);
	return 0;
}
}


unsigned long module_refcount(struct module *mod)
unsigned long module_refcount(struct module *mod)
{
{
	unsigned long incs = 0, decs = 0;
	return (unsigned long)atomic_read(&mod->refcnt) - MODULE_REF_BASE;
	int cpu;

	for_each_possible_cpu(cpu)
		decs += per_cpu_ptr(mod->refptr, cpu)->decs;
	/*
	 * ensure the incs are added up after the decs.
	 * module_put ensures incs are visible before decs with smp_wmb.
	 *
	 * This 2-count scheme avoids the situation where the refcount
	 * for CPU0 is read, then CPU0 increments the module refcount,
	 * then CPU1 drops that refcount, then the refcount for CPU1 is
	 * read. We would record a decrement but not its corresponding
	 * increment so we would see a low count (disaster).
	 *
	 * Rare situation? But module_refcount can be preempted, and we
	 * might be tallying up 4096+ CPUs. So it is not impossible.
	 */
	smp_rmb();
	for_each_possible_cpu(cpu)
		incs += per_cpu_ptr(mod->refptr, cpu)->incs;
	return incs - decs;
}
}
EXPORT_SYMBOL(module_refcount);
EXPORT_SYMBOL(module_refcount);


@@ -877,8 +858,10 @@ static inline void print_unload_info(struct seq_file *m, struct module *mod)


	seq_printf(m, " %lu ", module_refcount(mod));
	seq_printf(m, " %lu ", module_refcount(mod));


	/* Always include a trailing , so userspace can differentiate
	/*
           between this and the old multi-field proc format. */
	 * Always include a trailing , so userspace can differentiate
	 * between this and the old multi-field proc format.
	 */
	list_for_each_entry(use, &mod->source_list, source_list) {
	list_for_each_entry(use, &mod->source_list, source_list) {
		printed_something = 1;
		printed_something = 1;
		seq_printf(m, "%s,", use->source->name);
		seq_printf(m, "%s,", use->source->name);
@@ -886,11 +869,11 @@ static inline void print_unload_info(struct seq_file *m, struct module *mod)


	if (mod->init != NULL && mod->exit == NULL) {
	if (mod->init != NULL && mod->exit == NULL) {
		printed_something = 1;
		printed_something = 1;
		seq_printf(m, "[permanent],");
		seq_puts(m, "[permanent],");
	}
	}


	if (!printed_something)
	if (!printed_something)
		seq_printf(m, "-");
		seq_puts(m, "-");
}
}


void __symbol_put(const char *symbol)
void __symbol_put(const char *symbol)
@@ -935,7 +918,7 @@ void __module_get(struct module *module)
{
{
	if (module) {
	if (module) {
		preempt_disable();
		preempt_disable();
		__this_cpu_inc(module->refptr->incs);
		atomic_inc(&module->refcnt);
		trace_module_get(module, _RET_IP_);
		trace_module_get(module, _RET_IP_);
		preempt_enable();
		preempt_enable();
	}
	}
@@ -948,11 +931,11 @@ bool try_module_get(struct module *module)


	if (module) {
	if (module) {
		preempt_disable();
		preempt_disable();

		/* Note: here, we can fail to get a reference */
		if (likely(module_is_live(module))) {
		if (likely(module_is_live(module) &&
			__this_cpu_inc(module->refptr->incs);
			   atomic_inc_not_zero(&module->refcnt) != 0))
			trace_module_get(module, _RET_IP_);
			trace_module_get(module, _RET_IP_);
		} else
		else
			ret = false;
			ret = false;


		preempt_enable();
		preempt_enable();
@@ -963,11 +946,12 @@ EXPORT_SYMBOL(try_module_get);


void module_put(struct module *module)
void module_put(struct module *module)
{
{
	int ret;

	if (module) {
	if (module) {
		preempt_disable();
		preempt_disable();
		smp_wmb(); /* see comment in module_refcount */
		ret = atomic_dec_if_positive(&module->refcnt);
		__this_cpu_inc(module->refptr->decs);
		WARN_ON(ret < 0);	/* Failed to put refcount */

		trace_module_put(module, _RET_IP_);
		trace_module_put(module, _RET_IP_);
		preempt_enable();
		preempt_enable();
	}
	}
@@ -978,7 +962,7 @@ EXPORT_SYMBOL(module_put);
static inline void print_unload_info(struct seq_file *m, struct module *mod)
static inline void print_unload_info(struct seq_file *m, struct module *mod)
{
{
	/* We don't know the usage count, or what modules are using. */
	/* We don't know the usage count, or what modules are using. */
	seq_printf(m, " - -");
	seq_puts(m, " - -");
}
}


static inline void module_unload_free(struct module *mod)
static inline void module_unload_free(struct module *mod)
@@ -1165,7 +1149,7 @@ static int check_version(Elf_Shdr *sechdrs,
	return 0;
	return 0;


bad_version:
bad_version:
	printk("%s: disagrees about version of symbol %s\n",
	pr_warn("%s: disagrees about version of symbol %s\n",
	       mod->name, symname);
	       mod->name, symname);
	return 0;
	return 0;
}
}
@@ -1288,15 +1272,13 @@ static inline bool sect_empty(const Elf_Shdr *sect)
	return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0;
	return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0;
}
}


struct module_sect_attr
struct module_sect_attr {
{
	struct module_attribute mattr;
	struct module_attribute mattr;
	char *name;
	char *name;
	unsigned long address;
	unsigned long address;
};
};


struct module_sect_attrs
struct module_sect_attrs {
{
	struct attribute_group grp;
	struct attribute_group grp;
	unsigned int nsections;
	unsigned int nsections;
	struct module_sect_attr attrs[0];
	struct module_sect_attr attrs[0];
@@ -1550,7 +1532,8 @@ static int module_add_modinfo_attrs(struct module *mod)
		    (attr->test && attr->test(mod))) {
		    (attr->test && attr->test(mod))) {
			memcpy(temp_attr, attr, sizeof(*temp_attr));
			memcpy(temp_attr, attr, sizeof(*temp_attr));
			sysfs_attr_init(&temp_attr->attr);
			sysfs_attr_init(&temp_attr->attr);
			error = sysfs_create_file(&mod->mkobj.kobj,&temp_attr->attr);
			error = sysfs_create_file(&mod->mkobj.kobj,
					&temp_attr->attr);
			++temp_attr;
			++temp_attr;
		}
		}
	}
	}
@@ -1697,18 +1680,6 @@ static void mod_sysfs_teardown(struct module *mod)
	mod_sysfs_fini(mod);
	mod_sysfs_fini(mod);
}
}


/*
 * unlink the module with the whole machine is stopped with interrupts off
 * - this defends against kallsyms not taking locks
 */
static int __unlink_module(void *_mod)
{
	struct module *mod = _mod;
	list_del(&mod->list);
	module_bug_cleanup(mod);
	return 0;
}

#ifdef CONFIG_DEBUG_SET_MODULE_RONX
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
/*
/*
 * LKM RO/NX protection: protect module's text/ro-data
 * LKM RO/NX protection: protect module's text/ro-data
@@ -1860,7 +1831,12 @@ static void free_module(struct module *mod)


	/* Now we can delete it from the lists */
	/* Now we can delete it from the lists */
	mutex_lock(&module_mutex);
	mutex_lock(&module_mutex);
	stop_machine(__unlink_module, mod, NULL);
	/* Unlink carefully: kallsyms could be walking list. */
	list_del_rcu(&mod->list);
	/* Remove this module from bug list, this uses list_del_rcu */
	module_bug_cleanup(mod);
	/* Wait for RCU synchronizing before releasing mod->list and buglist. */
	synchronize_rcu();
	mutex_unlock(&module_mutex);
	mutex_unlock(&module_mutex);


	/* This may be NULL, but that's OK */
	/* This may be NULL, but that's OK */
@@ -1955,7 +1931,7 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
			/* We compiled with -fno-common.  These are not
			/* We compiled with -fno-common.  These are not
			   supposed to happen.  */
			   supposed to happen.  */
			pr_debug("Common symbol: %s\n", name);
			pr_debug("Common symbol: %s\n", name);
			printk("%s: please compile with -fno-common\n",
			pr_warn("%s: please compile with -fno-common\n",
			       mod->name);
			       mod->name);
			ret = -ENOEXEC;
			ret = -ENOEXEC;
			break;
			break;
@@ -2735,7 +2711,7 @@ static int find_module_sections(struct module *mod, struct load_info *info)
		 * This shouldn't happen with same compiler and binutils
		 * This shouldn't happen with same compiler and binutils
		 * building all parts of the module.
		 * building all parts of the module.
		 */
		 */
		printk(KERN_WARNING "%s: has both .ctors and .init_array.\n",
		pr_warn("%s: has both .ctors and .init_array.\n",
		       mod->name);
		       mod->name);
		return -EINVAL;
		return -EINVAL;
	}
	}
@@ -3023,8 +2999,10 @@ static int do_init_module(struct module *mod)
	if (mod->init != NULL)
	if (mod->init != NULL)
		ret = do_one_initcall(mod->init);
		ret = do_one_initcall(mod->init);
	if (ret < 0) {
	if (ret < 0) {
		/* Init routine failed: abort.  Try to protect us from
		/*
                   buggy refcounters. */
		 * Init routine failed: abort.  Try to protect us from
		 * buggy refcounters.
		 */
		mod->state = MODULE_STATE_GOING;
		mod->state = MODULE_STATE_GOING;
		synchronize_sched();
		synchronize_sched();
		module_put(mod);
		module_put(mod);
@@ -3352,6 +3330,8 @@ static int load_module(struct load_info *info, const char __user *uargs,
	/* Unlink carefully: kallsyms could be walking list. */
	/* Unlink carefully: kallsyms could be walking list. */
	list_del_rcu(&mod->list);
	list_del_rcu(&mod->list);
	wake_up_all(&module_wq);
	wake_up_all(&module_wq);
	/* Wait for RCU synchronizing before releasing mod->list. */
	synchronize_rcu();
	mutex_unlock(&module_mutex);
	mutex_unlock(&module_mutex);
 free_module:
 free_module:
	module_deallocate(mod, info);
	module_deallocate(mod, info);
@@ -3695,7 +3675,7 @@ static int m_show(struct seq_file *m, void *p)
	if (mod->taints)
	if (mod->taints)
		seq_printf(m, " %s", module_flags(mod, buf));
		seq_printf(m, " %s", module_flags(mod, buf));


	seq_printf(m, "\n");
	seq_puts(m, "\n");
	return 0;
	return 0;
}
}


+46 −51
Original line number Original line Diff line number Diff line
@@ -603,73 +603,66 @@ static __modinit int add_sysfs_param(struct module_kobject *mk,
				     const struct kernel_param *kp,
				     const struct kernel_param *kp,
				     const char *name)
				     const char *name)
{
{
	struct module_param_attrs *new;
	struct module_param_attrs *new_mp;
	struct attribute **attrs;
	struct attribute **new_attrs;
	int err, num;
	unsigned int i;


	/* We don't bother calling this with invisible parameters. */
	/* We don't bother calling this with invisible parameters. */
	BUG_ON(!kp->perm);
	BUG_ON(!kp->perm);


	if (!mk->mp) {
	if (!mk->mp) {
		num = 0;
		/* First allocation. */
		attrs = NULL;
		mk->mp = kzalloc(sizeof(*mk->mp), GFP_KERNEL);
	} else {
		if (!mk->mp)
		num = mk->mp->num;
			return -ENOMEM;
		attrs = mk->mp->grp.attrs;
		mk->mp->grp.name = "parameters";
		/* NULL-terminated attribute array. */
		mk->mp->grp.attrs = kzalloc(sizeof(mk->mp->grp.attrs[0]),
					    GFP_KERNEL);
		/* Caller will cleanup via free_module_param_attrs */
		if (!mk->mp->grp.attrs)
			return -ENOMEM;
	}
	}


	/* Enlarge. */
	/* Enlarge allocations. */
	new = krealloc(mk->mp,
	new_mp = krealloc(mk->mp,
		       sizeof(*mk->mp) + sizeof(mk->mp->attrs[0]) * (num+1),
			  sizeof(*mk->mp) +
			  sizeof(mk->mp->attrs[0]) * (mk->mp->num + 1),
			  GFP_KERNEL);
			  GFP_KERNEL);
	if (!new) {
	if (!new_mp)
		kfree(attrs);
		return -ENOMEM;
		err = -ENOMEM;
	mk->mp = new_mp;
		goto fail;

	}
	/* Extra pointer for NULL terminator */
	/* Despite looking like the typical realloc() bug, this is safe.
	new_attrs = krealloc(mk->mp->grp.attrs,
	 * We *want* the old 'attrs' to be freed either way, and we'll store
			     sizeof(mk->mp->grp.attrs[0]) * (mk->mp->num + 2),
	 * the new one in the success case. */
			     GFP_KERNEL);
	attrs = krealloc(attrs, sizeof(new->grp.attrs[0])*(num+2), GFP_KERNEL);
	if (!new_attrs)
	if (!attrs) {
		return -ENOMEM;
		err = -ENOMEM;
	mk->mp->grp.attrs = new_attrs;
		goto fail_free_new;
	}

	/* Sysfs wants everything zeroed. */
	memset(new, 0, sizeof(*new));
	memset(&new->attrs[num], 0, sizeof(new->attrs[num]));
	memset(&attrs[num], 0, sizeof(attrs[num]));
	new->grp.name = "parameters";
	new->grp.attrs = attrs;


	/* Tack new one on the end. */
	/* Tack new one on the end. */
	sysfs_attr_init(&new->attrs[num].mattr.attr);
	sysfs_attr_init(&mk->mp->attrs[mk->mp->num].mattr.attr);
	new->attrs[num].param = kp;
	mk->mp->attrs[mk->mp->num].param = kp;
	new->attrs[num].mattr.show = param_attr_show;
	mk->mp->attrs[mk->mp->num].mattr.show = param_attr_show;
	new->attrs[num].mattr.store = param_attr_store;
	/* Do not allow runtime DAC changes to make param writable. */
	new->attrs[num].mattr.attr.name = (char *)name;
	if ((kp->perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
	new->attrs[num].mattr.attr.mode = kp->perm;
		mk->mp->attrs[mk->mp->num].mattr.store = param_attr_store;
	new->num = num+1;
	mk->mp->attrs[mk->mp->num].mattr.attr.name = (char *)name;
	mk->mp->attrs[mk->mp->num].mattr.attr.mode = kp->perm;
	mk->mp->num++;


	/* Fix up all the pointers, since krealloc can move us */
	/* Fix up all the pointers, since krealloc can move us */
	for (num = 0; num < new->num; num++)
	for (i = 0; i < mk->mp->num; i++)
		new->grp.attrs[num] = &new->attrs[num].mattr.attr;
		mk->mp->grp.attrs[i] = &mk->mp->attrs[i].mattr.attr;
	new->grp.attrs[num] = NULL;
	mk->mp->grp.attrs[mk->mp->num] = NULL;

	mk->mp = new;
	return 0;
	return 0;

fail_free_new:
	kfree(new);
fail:
	mk->mp = NULL;
	return err;
}
}


#ifdef CONFIG_MODULES
#ifdef CONFIG_MODULES
static void free_module_param_attrs(struct module_kobject *mk)
static void free_module_param_attrs(struct module_kobject *mk)
{
{
	if (mk->mp)
		kfree(mk->mp->grp.attrs);
		kfree(mk->mp->grp.attrs);
	kfree(mk->mp);
	kfree(mk->mp);
	mk->mp = NULL;
	mk->mp = NULL;
@@ -695,8 +688,10 @@ int module_param_sysfs_setup(struct module *mod,
		if (kparam[i].perm == 0)
		if (kparam[i].perm == 0)
			continue;
			continue;
		err = add_sysfs_param(&mod->mkobj, &kparam[i], kparam[i].name);
		err = add_sysfs_param(&mod->mkobj, &kparam[i], kparam[i].name);
		if (err)
		if (err) {
			free_module_param_attrs(&mod->mkobj);
			return err;
			return err;
		}
		params = true;
		params = true;
	}
	}


+14 −6
Original line number Original line Diff line number Diff line
@@ -64,16 +64,22 @@ static LIST_HEAD(module_bug_list);
static const struct bug_entry *module_find_bug(unsigned long bugaddr)
static const struct bug_entry *module_find_bug(unsigned long bugaddr)
{
{
	struct module *mod;
	struct module *mod;
	const struct bug_entry *bug = NULL;


	list_for_each_entry(mod, &module_bug_list, bug_list) {
	rcu_read_lock();
		const struct bug_entry *bug = mod->bug_table;
	list_for_each_entry_rcu(mod, &module_bug_list, bug_list) {
		unsigned i;
		unsigned i;


		bug = mod->bug_table;
		for (i = 0; i < mod->num_bugs; ++i, ++bug)
		for (i = 0; i < mod->num_bugs; ++i, ++bug)
			if (bugaddr == bug_addr(bug))
			if (bugaddr == bug_addr(bug))
				return bug;
				goto out;
	}
	}
	return NULL;
	bug = NULL;
out:
	rcu_read_unlock();

	return bug;
}
}


void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
@@ -99,13 +105,15 @@ void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
	 * Strictly speaking this should have a spinlock to protect against
	 * Strictly speaking this should have a spinlock to protect against
	 * traversals, but since we only traverse on BUG()s, a spinlock
	 * traversals, but since we only traverse on BUG()s, a spinlock
	 * could potentially lead to deadlock and thus be counter-productive.
	 * could potentially lead to deadlock and thus be counter-productive.
	 * Thus, this uses RCU to safely manipulate the bug list, since BUG
	 * must run in non-interruptive state.
	 */
	 */
	list_add(&mod->bug_list, &module_bug_list);
	list_add_rcu(&mod->bug_list, &module_bug_list);
}
}


void module_bug_cleanup(struct module *mod)
void module_bug_cleanup(struct module *mod)
{
{
	list_del(&mod->bug_list);
	list_del_rcu(&mod->bug_list);
}
}


#else
#else