Commit 6f2b9b9a9d750a9175dc79c74bfed5add840983c
Committed by
Ingo Molnar
1 parent
673f820591
Exists in
master
and in
39 other branches
timer: implement lockdep deadlock detection
This modifies the timer code in a way to allow lockdep to detect deadlocks resulting from a lock being taken in the timer function as well as around the del_timer_sync() call. Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Showing 2 changed files with 141 additions and 20 deletions Side-by-side Diff
include/linux/timer.h
... | ... | @@ -5,6 +5,7 @@ |
5 | 5 | #include <linux/ktime.h> |
6 | 6 | #include <linux/stddef.h> |
7 | 7 | #include <linux/debugobjects.h> |
8 | +#include <linux/stringify.h> | |
8 | 9 | |
9 | 10 | struct tvec_base; |
10 | 11 | |
11 | 12 | |
12 | 13 | |
13 | 14 | |
14 | 15 | |
15 | 16 | |
16 | 17 | |
17 | 18 | |
18 | 19 | |
19 | 20 | |
20 | 21 | |
21 | 22 | |
... | ... | @@ -21,52 +22,126 @@ |
21 | 22 | char start_comm[16]; |
22 | 23 | int start_pid; |
23 | 24 | #endif |
25 | +#ifdef CONFIG_LOCKDEP | |
26 | + struct lockdep_map lockdep_map; | |
27 | +#endif | |
24 | 28 | }; |
25 | 29 | |
26 | 30 | extern struct tvec_base boot_tvec_bases; |
27 | 31 | |
32 | +#ifdef CONFIG_LOCKDEP | |
33 | +/* | |
34 | + * NB: because we have to copy the lockdep_map, setting the lockdep_map key | |
35 | + * (second argument) here is required, otherwise it could be initialised to | |
36 | + * the copy of the lockdep_map later! We use the pointer to and the string | |
37 | + * "<file>:<line>" as the key resp. the name of the lockdep_map. | |
38 | + */ | |
39 | +#define __TIMER_LOCKDEP_MAP_INITIALIZER(_kn) \ | |
40 | + .lockdep_map = STATIC_LOCKDEP_MAP_INIT(_kn, &_kn), | |
41 | +#else | |
42 | +#define __TIMER_LOCKDEP_MAP_INITIALIZER(_kn) | |
43 | +#endif | |
44 | + | |
28 | 45 | #define TIMER_INITIALIZER(_function, _expires, _data) { \ |
29 | 46 | .entry = { .prev = TIMER_ENTRY_STATIC }, \ |
30 | 47 | .function = (_function), \ |
31 | 48 | .expires = (_expires), \ |
32 | 49 | .data = (_data), \ |
33 | 50 | .base = &boot_tvec_bases, \ |
51 | + __TIMER_LOCKDEP_MAP_INITIALIZER( \ | |
52 | + __FILE__ ":" __stringify(__LINE__)) \ | |
34 | 53 | } |
35 | 54 | |
36 | 55 | #define DEFINE_TIMER(_name, _function, _expires, _data) \ |
37 | 56 | struct timer_list _name = \ |
38 | 57 | TIMER_INITIALIZER(_function, _expires, _data) |
39 | 58 | |
40 | -void init_timer(struct timer_list *timer); | |
41 | -void init_timer_deferrable(struct timer_list *timer); | |
59 | +void init_timer_key(struct timer_list *timer, | |
60 | + const char *name, | |
61 | + struct lock_class_key *key); | |
62 | +void init_timer_deferrable_key(struct timer_list *timer, | |
63 | + const char *name, | |
64 | + struct lock_class_key *key); | |
42 | 65 | |
66 | +#ifdef CONFIG_LOCKDEP | |
67 | +#define init_timer(timer) \ | |
68 | + do { \ | |
69 | + static struct lock_class_key __key; \ | |
70 | + init_timer_key((timer), #timer, &__key); \ | |
71 | + } while (0) | |
72 | + | |
73 | +#define init_timer_deferrable(timer) \ | |
74 | + do { \ | |
75 | + static struct lock_class_key __key; \ | |
76 | + init_timer_deferrable_key((timer), #timer, &__key); \ | |
77 | + } while (0) | |
78 | + | |
79 | +#define init_timer_on_stack(timer) \ | |
80 | + do { \ | |
81 | + static struct lock_class_key __key; \ | |
82 | + init_timer_on_stack_key((timer), #timer, &__key); \ | |
83 | + } while (0) | |
84 | + | |
85 | +#define setup_timer(timer, fn, data) \ | |
86 | + do { \ | |
87 | + static struct lock_class_key __key; \ | |
88 | + setup_timer_key((timer), #timer, &__key, (fn), (data));\ | |
89 | + } while (0) | |
90 | + | |
91 | +#define setup_timer_on_stack(timer, fn, data) \ | |
92 | + do { \ | |
93 | + static struct lock_class_key __key; \ | |
94 | + setup_timer_on_stack_key((timer), #timer, &__key, \ | |
95 | + (fn), (data)); \ | |
96 | + } while (0) | |
97 | +#else | |
98 | +#define init_timer(timer)\ | |
99 | + init_timer_key((timer), NULL, NULL) | |
100 | +#define init_timer_deferrable(timer)\ | |
101 | + init_timer_deferrable_key((timer), NULL, NULL) | |
102 | +#define init_timer_on_stack(timer)\ | |
103 | + init_timer_on_stack_key((timer), NULL, NULL) | |
104 | +#define setup_timer(timer, fn, data)\ | |
105 | + setup_timer_key((timer), NULL, NULL, (fn), (data)) | |
106 | +#define setup_timer_on_stack(timer, fn, data)\ | |
107 | + setup_timer_on_stack_key((timer), NULL, NULL, (fn), (data)) | |
108 | +#endif | |
109 | + | |
43 | 110 | #ifdef CONFIG_DEBUG_OBJECTS_TIMERS |
44 | -extern void init_timer_on_stack(struct timer_list *timer); | |
111 | +extern void init_timer_on_stack_key(struct timer_list *timer, | |
112 | + const char *name, | |
113 | + struct lock_class_key *key); | |
45 | 114 | extern void destroy_timer_on_stack(struct timer_list *timer); |
46 | 115 | #else |
47 | 116 | static inline void destroy_timer_on_stack(struct timer_list *timer) { } |
48 | -static inline void init_timer_on_stack(struct timer_list *timer) | |
117 | +static inline void init_timer_on_stack_key(struct timer_list *timer, | |
118 | + const char *name, | |
119 | + struct lock_class_key *key) | |
49 | 120 | { |
50 | - init_timer(timer); | |
121 | + init_timer_key(timer, name, key); | |
51 | 122 | } |
52 | 123 | #endif |
53 | 124 | |
54 | -static inline void setup_timer(struct timer_list * timer, | |
125 | +static inline void setup_timer_key(struct timer_list * timer, | |
126 | + const char *name, | |
127 | + struct lock_class_key *key, | |
55 | 128 | void (*function)(unsigned long), |
56 | 129 | unsigned long data) |
57 | 130 | { |
58 | 131 | timer->function = function; |
59 | 132 | timer->data = data; |
60 | - init_timer(timer); | |
133 | + init_timer_key(timer, name, key); | |
61 | 134 | } |
62 | 135 | |
63 | -static inline void setup_timer_on_stack(struct timer_list *timer, | |
136 | +static inline void setup_timer_on_stack_key(struct timer_list *timer, | |
137 | + const char *name, | |
138 | + struct lock_class_key *key, | |
64 | 139 | void (*function)(unsigned long), |
65 | 140 | unsigned long data) |
66 | 141 | { |
67 | 142 | timer->function = function; |
68 | 143 | timer->data = data; |
69 | - init_timer_on_stack(timer); | |
144 | + init_timer_on_stack_key(timer, name, key); | |
70 | 145 | } |
71 | 146 | |
72 | 147 | /** |
kernel/timer.c
... | ... | @@ -491,14 +491,18 @@ |
491 | 491 | debug_object_free(timer, &timer_debug_descr); |
492 | 492 | } |
493 | 493 | |
494 | -static void __init_timer(struct timer_list *timer); | |
494 | +static void __init_timer(struct timer_list *timer, | |
495 | + const char *name, | |
496 | + struct lock_class_key *key); | |
495 | 497 | |
496 | -void init_timer_on_stack(struct timer_list *timer) | |
498 | +void init_timer_on_stack_key(struct timer_list *timer, | |
499 | + const char *name, | |
500 | + struct lock_class_key *key) | |
497 | 501 | { |
498 | 502 | debug_object_init_on_stack(timer, &timer_debug_descr); |
499 | - __init_timer(timer); | |
503 | + __init_timer(timer, name, key); | |
500 | 504 | } |
501 | -EXPORT_SYMBOL_GPL(init_timer_on_stack); | |
505 | +EXPORT_SYMBOL_GPL(init_timer_on_stack_key); | |
502 | 506 | |
503 | 507 | void destroy_timer_on_stack(struct timer_list *timer) |
504 | 508 | { |
... | ... | @@ -512,7 +516,9 @@ |
512 | 516 | static inline void debug_timer_deactivate(struct timer_list *timer) { } |
513 | 517 | #endif |
514 | 518 | |
515 | -static void __init_timer(struct timer_list *timer) | |
519 | +static void __init_timer(struct timer_list *timer, | |
520 | + const char *name, | |
521 | + struct lock_class_key *key) | |
516 | 522 | { |
517 | 523 | timer->entry.next = NULL; |
518 | 524 | timer->base = __raw_get_cpu_var(tvec_bases); |
... | ... | @@ -521,6 +527,7 @@ |
521 | 527 | timer->start_pid = -1; |
522 | 528 | memset(timer->start_comm, 0, TASK_COMM_LEN); |
523 | 529 | #endif |
530 | + lockdep_init_map(&timer->lockdep_map, name, key, 0); | |
524 | 531 | } |
525 | 532 | |
526 | 533 | /** |
527 | 534 | |
528 | 535 | |
529 | 536 | |
530 | 537 | |
531 | 538 | |
... | ... | @@ -530,19 +537,23 @@ |
530 | 537 | * init_timer() must be done to a timer prior calling *any* of the |
531 | 538 | * other timer functions. |
532 | 539 | */ |
533 | -void init_timer(struct timer_list *timer) | |
540 | +void init_timer_key(struct timer_list *timer, | |
541 | + const char *name, | |
542 | + struct lock_class_key *key) | |
534 | 543 | { |
535 | 544 | debug_timer_init(timer); |
536 | - __init_timer(timer); | |
545 | + __init_timer(timer, name, key); | |
537 | 546 | } |
538 | -EXPORT_SYMBOL(init_timer); | |
547 | +EXPORT_SYMBOL(init_timer_key); | |
539 | 548 | |
540 | -void init_timer_deferrable(struct timer_list *timer) | |
549 | +void init_timer_deferrable_key(struct timer_list *timer, | |
550 | + const char *name, | |
551 | + struct lock_class_key *key) | |
541 | 552 | { |
542 | - init_timer(timer); | |
553 | + init_timer_key(timer, name, key); | |
543 | 554 | timer_set_deferrable(timer); |
544 | 555 | } |
545 | -EXPORT_SYMBOL(init_timer_deferrable); | |
556 | +EXPORT_SYMBOL(init_timer_deferrable_key); | |
546 | 557 | |
547 | 558 | static inline void detach_timer(struct timer_list *timer, |
548 | 559 | int clear_pending) |
... | ... | @@ -789,6 +800,15 @@ |
789 | 800 | */ |
790 | 801 | int del_timer_sync(struct timer_list *timer) |
791 | 802 | { |
803 | +#ifdef CONFIG_LOCKDEP | |
804 | + unsigned long flags; | |
805 | + | |
806 | + local_irq_save(flags); | |
807 | + lock_map_acquire(&timer->lockdep_map); | |
808 | + lock_map_release(&timer->lockdep_map); | |
809 | + local_irq_restore(flags); | |
810 | +#endif | |
811 | + | |
792 | 812 | for (;;) { |
793 | 813 | int ret = try_to_del_timer_sync(timer); |
794 | 814 | if (ret >= 0) |
795 | 815 | |
796 | 816 | |
... | ... | @@ -861,10 +881,36 @@ |
861 | 881 | |
862 | 882 | set_running_timer(base, timer); |
863 | 883 | detach_timer(timer, 1); |
884 | + | |
864 | 885 | spin_unlock_irq(&base->lock); |
865 | 886 | { |
866 | 887 | int preempt_count = preempt_count(); |
888 | + | |
889 | +#ifdef CONFIG_LOCKDEP | |
890 | + /* | |
891 | + * It is permissible to free the timer from | |
892 | + * inside the function that is called from | |
893 | + * it, this we need to take into account for | |
894 | + * lockdep too. To avoid bogus "held lock | |
895 | + * freed" warnings as well as problems when | |
896 | + * looking into timer->lockdep_map, make a | |
897 | + * copy and use that here. | |
898 | + */ | |
899 | + struct lockdep_map lockdep_map = | |
900 | + timer->lockdep_map; | |
901 | +#endif | |
902 | + /* | |
903 | + * Couple the lock chain with the lock chain at | |
904 | + * del_timer_sync() by acquiring the lock_map | |
905 | + * around the fn() call here and in | |
906 | + * del_timer_sync(). | |
907 | + */ | |
908 | + lock_map_acquire(&lockdep_map); | |
909 | + | |
867 | 910 | fn(data); |
911 | + | |
912 | + lock_map_release(&lockdep_map); | |
913 | + | |
868 | 914 | if (preempt_count != preempt_count()) { |
869 | 915 | printk(KERN_ERR "huh, entered %p " |
870 | 916 | "with preempt_count %08x, exited" |