Commit e2852ae825dba5ebc159788720baec1a28a57125

Authored by Tejun Heo
Committed by Linus Torvalds
1 parent 066a9be6c0

percpu_counter: add debugobj support

All percpu counters are linked to a global list on initialization and
removed from it on destruction.  The list is walked during CPU up/down.
If a percpu counter is freed without being properly destroyed, the system
will oops only on the next CPU up/down making it pretty nasty to track
down.  This patch adds debugobj support for percpu counters so that such
problems can be found easily.

As percpu counters don't make sense on stack and can't be statically
initialized, debugobj support is pretty simple.  It's initialized and
activated on counter initialization, and deactivatd and destroyed on
counter destruction.  With this patch applied, the bug fixed by commit
602586a83b719df0fbd94196a1359ed35aeb2df3 (shmem: put_super must
percpu_counter_destroy) triggers the following warning on tmpfs unmount
and the system won't oops on the next cpu up/down operation.

 ------------[ cut here ]------------
 WARNING: at lib/debugobjects.c:259 debug_print_object+0x5c/0x70()
 Hardware name: Bochs
 ODEBUG: free active (active state 0) object type: percpu_counter
 Modules linked in:
 Pid: 3999, comm: umount Not tainted 2.6.36-rc2-work+ #5
 Call Trace:
  [<ffffffff81083f7f>] warn_slowpath_common+0x7f/0xc0
  [<ffffffff81084076>] warn_slowpath_fmt+0x46/0x50
  [<ffffffff813b45cc>] debug_print_object+0x5c/0x70
  [<ffffffff813b50e5>] debug_check_no_obj_freed+0x125/0x210
  [<ffffffff811577d3>] kfree+0xb3/0x2f0
  [<ffffffff81132edd>] shmem_put_super+0x1d/0x30
  [<ffffffff81162e96>] generic_shutdown_super+0x56/0xe0
  [<ffffffff81162f86>] kill_anon_super+0x16/0x60
  [<ffffffff81162ff7>] kill_litter_super+0x27/0x30
  [<ffffffff81163295>] deactivate_locked_super+0x45/0x60
  [<ffffffff81163cfa>] deactivate_super+0x4a/0x70
  [<ffffffff8117d446>] mntput_no_expire+0x86/0xe0
  [<ffffffff8117df7f>] sys_umount+0x6f/0x360
  [<ffffffff8103f01b>] system_call_fastpath+0x16/0x1b
 ---[ end trace cce2a341ba3611a7 ]---

Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Thomas Gleixner <tglxlinutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 2 changed files with 56 additions and 0 deletions Side-by-side Diff

... ... @@ -317,6 +317,14 @@
317 317 help
318 318 Enable this to turn on debugging of RCU list heads (call_rcu() usage).
319 319  
  320 +config DEBUG_OBJECTS_PERCPU_COUNTER
  321 + bool "Debug percpu counter objects"
  322 + depends on DEBUG_OBJECTS
  323 + help
  324 + If you say Y here, additional code will be inserted into the
  325 + percpu counter routines to track the life time of percpu counter
  326 + objects and validate the percpu counter operations.
  327 +
320 328 config DEBUG_OBJECTS_ENABLE_DEFAULT
321 329 int "debug_objects bootup default value (0-1)"
322 330 range 0 1
lib/percpu_counter.c
... ... @@ -8,10 +8,53 @@
8 8 #include <linux/init.h>
9 9 #include <linux/cpu.h>
10 10 #include <linux/module.h>
  11 +#include <linux/debugobjects.h>
11 12  
12 13 static LIST_HEAD(percpu_counters);
13 14 static DEFINE_MUTEX(percpu_counters_lock);
14 15  
  16 +#ifdef CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER
  17 +
  18 +static struct debug_obj_descr percpu_counter_debug_descr;
  19 +
  20 +static int percpu_counter_fixup_free(void *addr, enum debug_obj_state state)
  21 +{
  22 + struct percpu_counter *fbc = addr;
  23 +
  24 + switch (state) {
  25 + case ODEBUG_STATE_ACTIVE:
  26 + percpu_counter_destroy(fbc);
  27 + debug_object_free(fbc, &percpu_counter_debug_descr);
  28 + return 1;
  29 + default:
  30 + return 0;
  31 + }
  32 +}
  33 +
  34 +static struct debug_obj_descr percpu_counter_debug_descr = {
  35 + .name = "percpu_counter",
  36 + .fixup_free = percpu_counter_fixup_free,
  37 +};
  38 +
  39 +static inline void debug_percpu_counter_activate(struct percpu_counter *fbc)
  40 +{
  41 + debug_object_init(fbc, &percpu_counter_debug_descr);
  42 + debug_object_activate(fbc, &percpu_counter_debug_descr);
  43 +}
  44 +
  45 +static inline void debug_percpu_counter_deactivate(struct percpu_counter *fbc)
  46 +{
  47 + debug_object_deactivate(fbc, &percpu_counter_debug_descr);
  48 + debug_object_free(fbc, &percpu_counter_debug_descr);
  49 +}
  50 +
  51 +#else /* CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER */
  52 +static inline void debug_percpu_counter_activate(struct percpu_counter *fbc)
  53 +{ }
  54 +static inline void debug_percpu_counter_deactivate(struct percpu_counter *fbc)
  55 +{ }
  56 +#endif /* CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER */
  57 +
15 58 void percpu_counter_set(struct percpu_counter *fbc, s64 amount)
16 59 {
17 60 int cpu;
... ... @@ -75,6 +118,9 @@
75 118 fbc->counters = alloc_percpu(s32);
76 119 if (!fbc->counters)
77 120 return -ENOMEM;
  121 +
  122 + debug_percpu_counter_activate(fbc);
  123 +
78 124 #ifdef CONFIG_HOTPLUG_CPU
79 125 INIT_LIST_HEAD(&fbc->list);
80 126 mutex_lock(&percpu_counters_lock);
... ... @@ -89,6 +135,8 @@
89 135 {
90 136 if (!fbc->counters)
91 137 return;
  138 +
  139 + debug_percpu_counter_deactivate(fbc);
92 140  
93 141 #ifdef CONFIG_HOTPLUG_CPU
94 142 mutex_lock(&percpu_counters_lock);