Commit d790be38 authored by Linus Torvalds's avatar Linus Torvalds

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
...@@ -210,20 +210,6 @@ enum module_state { ...@@ -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 { ...@@ -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
......
...@@ -80,7 +80,7 @@ DECLARE_EVENT_CLASS(module_refcnt, ...@@ -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);
), ),
......
This diff is collapsed.
...@@ -603,74 +603,67 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, ...@@ -603,74 +603,67 @@ 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) +
GFP_KERNEL); sizeof(mk->mp->attrs[0]) * (mk->mp->num + 1),
if (!new) { GFP_KERNEL);
kfree(attrs); if (!new_mp)
err = -ENOMEM; return -ENOMEM;
goto fail; mk->mp = new_mp;
}
/* Despite looking like the typical realloc() bug, this is safe.
* We *want* the old 'attrs' to be freed either way, and we'll store
* the new one in the success case. */
attrs = krealloc(attrs, sizeof(new->grp.attrs[0])*(num+2), GFP_KERNEL);
if (!attrs) {
err = -ENOMEM;
goto fail_free_new;
}
/* Sysfs wants everything zeroed. */ /* Extra pointer for NULL terminator */
memset(new, 0, sizeof(*new)); new_attrs = krealloc(mk->mp->grp.attrs,
memset(&new->attrs[num], 0, sizeof(new->attrs[num])); sizeof(mk->mp->grp.attrs[0]) * (mk->mp->num + 2),
memset(&attrs[num], 0, sizeof(attrs[num])); GFP_KERNEL);
new->grp.name = "parameters"; if (!new_attrs)
new->grp.attrs = attrs; return -ENOMEM;
mk->mp->grp.attrs = new_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)
{ {
kfree(mk->mp->grp.attrs); if (mk->mp)
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, ...@@ -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;
} }
......
...@@ -64,16 +64,22 @@ static LIST_HEAD(module_bug_list); ...@@ -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, ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment