diff options
author | Christine Chan <cschan@codeaurora.org> | 2011-11-07 22:48:28 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2011-11-23 12:49:23 -0500 |
commit | dc4218bd0fe499fce2896f88101ea42dac1f60fc (patch) | |
tree | 7cca2a9939118f657f9fb557d6da283931e9dc40 /kernel/timer.c | |
parent | fb16b8cf0b66386134b09e7b8b7056450272d159 (diff) |
timer: Use debugobjects to catch deletion of uninitialized timers
del_timer_sync() calls debug_object_assert_init() to assert that
a timer has been initialized before calling lock_timer_base().
lock_timer_base() would spin forever on a NULL(uninit-ed) base.
The check is added to del_timer() to prevent silent failure, even
though it would not get stuck in an infinite loop.
[ sboyd@codeaurora.org: Remove WARN, intialize timer function]
Signed-off-by: Christine Chan <cschan@codeaurora.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Cc: John Stultz <john.stultz@linaro.org>
Link: http://lkml.kernel.org/r/1320724108-20788-4-git-send-email-sboyd@codeaurora.org
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/timer.c')
-rw-r--r-- | kernel/timer.c | 53 |
1 files changed, 48 insertions, 5 deletions
diff --git a/kernel/timer.c b/kernel/timer.c index 317087d5d5fc..5fc5a766ba1d 100644 --- a/kernel/timer.c +++ b/kernel/timer.c | |||
@@ -487,12 +487,40 @@ static int timer_fixup_free(void *addr, enum debug_obj_state state) | |||
487 | } | 487 | } |
488 | } | 488 | } |
489 | 489 | ||
490 | /* | ||
491 | * fixup_assert_init is called when: | ||
492 | * - an untracked/uninit-ed object is found | ||
493 | */ | ||
494 | static int timer_fixup_assert_init(void *addr, enum debug_obj_state state) | ||
495 | { | ||
496 | struct timer_list *timer = addr; | ||
497 | |||
498 | switch (state) { | ||
499 | case ODEBUG_STATE_NOTAVAILABLE: | ||
500 | if (timer->entry.prev == TIMER_ENTRY_STATIC) { | ||
501 | /* | ||
502 | * This is not really a fixup. The timer was | ||
503 | * statically initialized. We just make sure that it | ||
504 | * is tracked in the object tracker. | ||
505 | */ | ||
506 | debug_object_init(timer, &timer_debug_descr); | ||
507 | return 0; | ||
508 | } else { | ||
509 | setup_timer(timer, stub_timer, 0); | ||
510 | return 1; | ||
511 | } | ||
512 | default: | ||
513 | return 0; | ||
514 | } | ||
515 | } | ||
516 | |||
490 | static struct debug_obj_descr timer_debug_descr = { | 517 | static struct debug_obj_descr timer_debug_descr = { |
491 | .name = "timer_list", | 518 | .name = "timer_list", |
492 | .debug_hint = timer_debug_hint, | 519 | .debug_hint = timer_debug_hint, |
493 | .fixup_init = timer_fixup_init, | 520 | .fixup_init = timer_fixup_init, |
494 | .fixup_activate = timer_fixup_activate, | 521 | .fixup_activate = timer_fixup_activate, |
495 | .fixup_free = timer_fixup_free, | 522 | .fixup_free = timer_fixup_free, |
523 | .fixup_assert_init = timer_fixup_assert_init, | ||
496 | }; | 524 | }; |
497 | 525 | ||
498 | static inline void debug_timer_init(struct timer_list *timer) | 526 | static inline void debug_timer_init(struct timer_list *timer) |
@@ -515,6 +543,11 @@ static inline void debug_timer_free(struct timer_list *timer) | |||
515 | debug_object_free(timer, &timer_debug_descr); | 543 | debug_object_free(timer, &timer_debug_descr); |
516 | } | 544 | } |
517 | 545 | ||
546 | static inline void debug_timer_assert_init(struct timer_list *timer) | ||
547 | { | ||
548 | debug_object_assert_init(timer, &timer_debug_descr); | ||
549 | } | ||
550 | |||
518 | static void __init_timer(struct timer_list *timer, | 551 | static void __init_timer(struct timer_list *timer, |
519 | const char *name, | 552 | const char *name, |
520 | struct lock_class_key *key); | 553 | struct lock_class_key *key); |
@@ -538,6 +571,7 @@ EXPORT_SYMBOL_GPL(destroy_timer_on_stack); | |||
538 | static inline void debug_timer_init(struct timer_list *timer) { } | 571 | static inline void debug_timer_init(struct timer_list *timer) { } |
539 | static inline void debug_timer_activate(struct timer_list *timer) { } | 572 | static inline void debug_timer_activate(struct timer_list *timer) { } |
540 | static inline void debug_timer_deactivate(struct timer_list *timer) { } | 573 | static inline void debug_timer_deactivate(struct timer_list *timer) { } |
574 | static inline void debug_timer_assert_init(struct timer_list *timer) { } | ||
541 | #endif | 575 | #endif |
542 | 576 | ||
543 | static inline void debug_init(struct timer_list *timer) | 577 | static inline void debug_init(struct timer_list *timer) |
@@ -559,6 +593,11 @@ static inline void debug_deactivate(struct timer_list *timer) | |||
559 | trace_timer_cancel(timer); | 593 | trace_timer_cancel(timer); |
560 | } | 594 | } |
561 | 595 | ||
596 | static inline void debug_assert_init(struct timer_list *timer) | ||
597 | { | ||
598 | debug_timer_assert_init(timer); | ||
599 | } | ||
600 | |||
562 | static void __init_timer(struct timer_list *timer, | 601 | static void __init_timer(struct timer_list *timer, |
563 | const char *name, | 602 | const char *name, |
564 | struct lock_class_key *key) | 603 | struct lock_class_key *key) |
@@ -909,6 +948,8 @@ int del_timer(struct timer_list *timer) | |||
909 | unsigned long flags; | 948 | unsigned long flags; |
910 | int ret = 0; | 949 | int ret = 0; |
911 | 950 | ||
951 | debug_assert_init(timer); | ||
952 | |||
912 | timer_stats_timer_clear_start_info(timer); | 953 | timer_stats_timer_clear_start_info(timer); |
913 | if (timer_pending(timer)) { | 954 | if (timer_pending(timer)) { |
914 | base = lock_timer_base(timer, &flags); | 955 | base = lock_timer_base(timer, &flags); |
@@ -939,6 +980,8 @@ int try_to_del_timer_sync(struct timer_list *timer) | |||
939 | unsigned long flags; | 980 | unsigned long flags; |
940 | int ret = -1; | 981 | int ret = -1; |
941 | 982 | ||
983 | debug_assert_init(timer); | ||
984 | |||
942 | base = lock_timer_base(timer, &flags); | 985 | base = lock_timer_base(timer, &flags); |
943 | 986 | ||
944 | if (base->running_timer == timer) | 987 | if (base->running_timer == timer) |