aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorNick Piggin <npiggin@suse.de>2010-04-01 04:09:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-04-05 22:50:02 -0400
commit5fbfb18d7a5b846946d52c4a10e3aaa213ec31b6 (patch)
treebcfa13dec8cb2527c3007b3e5f957cb50e571c64 /include
parent7da23b86e14b77c094b11a9fa5ef5b3758fc9193 (diff)
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')
-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
368 void (*exit)(void); 368 void (*exit)(void);
369 369
370 struct module_ref { 370 struct module_ref {
371 int count; 371 unsigned int incs;
372 unsigned int decs;
372 } __percpu *refptr; 373 } __percpu *refptr;
373#endif 374#endif
374 375
@@ -463,9 +464,9 @@ static inline void __module_get(struct module *module)
463{ 464{
464 if (module) { 465 if (module) {
465 preempt_disable(); 466 preempt_disable();
466 __this_cpu_inc(module->refptr->count); 467 __this_cpu_inc(module->refptr->incs);
467 trace_module_get(module, _THIS_IP_, 468 trace_module_get(module, _THIS_IP_,
468 __this_cpu_read(module->refptr->count)); 469 __this_cpu_read(module->refptr->incs));
469 preempt_enable(); 470 preempt_enable();
470 } 471 }
471} 472}
@@ -478,11 +479,10 @@ static inline int try_module_get(struct module *module)
478 preempt_disable(); 479 preempt_disable();
479 480
480 if (likely(module_is_live(module))) { 481 if (likely(module_is_live(module))) {
481 __this_cpu_inc(module->refptr->count); 482 __this_cpu_inc(module->refptr->incs);
482 trace_module_get(module, _THIS_IP_, 483 trace_module_get(module, _THIS_IP_,
483 __this_cpu_read(module->refptr->count)); 484 __this_cpu_read(module->refptr->incs));
484 } 485 } else
485 else
486 ret = 0; 486 ret = 0;
487 487
488 preempt_enable(); 488 preempt_enable();