summaryrefslogtreecommitdiffstats
path: root/include/linux/module.h
diff options
context:
space:
mode:
authorNick Piggin <npiggin@suse.de>2010-04-01 19:09:40 +1100
committerLinus Torvalds <torvalds@linux-foundation.org>2010-04-05 19:50:02 -0700
commit5fbfb18d7a5b846946d52c4a10e3aaa213ec31b6 (patch)
treebcfa13dec8cb2527c3007b3e5f957cb50e571c64 /include/linux/module.h
parent7da23b86e14b77c094b11a9fa5ef5b3758fc9193 (diff)
downloadlinux-5fbfb18d7a5b846946d52c4a10e3aaa213ec31b6.tar.gz
linux-5fbfb18d7a5b846946d52c4a10e3aaa213ec31b6.tar.bz2
linux-5fbfb18d7a5b846946d52c4a10e3aaa213ec31b6.zip
Fix up possibly racy module refcounting
Module refcounting is implemented with a per-cpu counter for speed. However there is a race when tallying the counter where a reference may be taken by one CPU and released by another. Reference count summation may then see the decrement without having seen the previous increment, leading to lower than expected count. A module which never has its actual reference drop below 1 may return a reference count of 0 due to this race. Module removal generally runs under stop_machine, which prevents this race causing bugs due to removal of in-use modules. However there are other real bugs in module.c code and driver code (module_refcount is exported) where the callers do not run under stop_machine. Fix this by maintaining running per-cpu counters for the number of module refcount increments and the number of refcount decrements. The increments are tallied after the decrements, so any decrement seen will always have its corresponding increment counted. The final refcount is the difference of the total increments and decrements, preventing a low-refcount from being returned. Signed-off-by: Nick Piggin <npiggin@suse.de> Acked-by: Rusty Russell <rusty@rustcorp.com.au> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include/linux/module.h')
-rw-r--r--include/linux/module.h14
1 files changed, 7 insertions, 7 deletions
diff --git a/include/linux/module.h b/include/linux/module.h
index 8bd399a00343..515d53ae6a79 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -368,7 +368,8 @@ struct module
void (*exit)(void);
struct module_ref {
- int count;
+ unsigned int incs;
+ unsigned int decs;
} __percpu *refptr;
#endif
@@ -463,9 +464,9 @@ static inline void __module_get(struct module *module)
{
if (module) {
preempt_disable();
- __this_cpu_inc(module->refptr->count);
+ __this_cpu_inc(module->refptr->incs);
trace_module_get(module, _THIS_IP_,
- __this_cpu_read(module->refptr->count));
+ __this_cpu_read(module->refptr->incs));
preempt_enable();
}
}
@@ -478,11 +479,10 @@ static inline int try_module_get(struct module *module)
preempt_disable();
if (likely(module_is_live(module))) {
- __this_cpu_inc(module->refptr->count);
+ __this_cpu_inc(module->refptr->incs);
trace_module_get(module, _THIS_IP_,
- __this_cpu_read(module->refptr->count));
- }
- else
+ __this_cpu_read(module->refptr->incs));
+ } else
ret = 0;
preempt_enable();