diff options
| author | Johannes Berg <johannes@sipsolutions.net> | 2007-10-19 02:39:55 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-19 14:53:38 -0400 |
| commit | 4e6045f134784f4b158b3c0f7a282b04bd816887 (patch) | |
| tree | 3304628f666c8524accd10f40da48cfba8b08608 /include/linux/workqueue.h | |
| parent | cf7b708c8d1d7a27736771bcf4c457b332b0f818 (diff) | |
workqueue: debug flushing deadlocks with lockdep
In the following scenario:
code path 1:
my_function() -> lock(L1); ...; flush_workqueue(); ...
code path 2:
run_workqueue() -> my_work() -> ...; lock(L1); ...
you can get a deadlock when my_work() is queued or running
but my_function() has acquired L1 already.
This patch adds a pseudo-lock to each workqueue to make lockdep
warn about this scenario.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Oleg Nesterov <oleg@tv-sign.ru>
Acked-by: Ingo Molnar <mingo@elte.hu>
Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include/linux/workqueue.h')
| -rw-r--r-- | include/linux/workqueue.h | 49 |
1 files changed, 46 insertions, 3 deletions
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index ce6badc98f..7daafdc251 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <linux/timer.h> | 8 | #include <linux/timer.h> |
| 9 | #include <linux/linkage.h> | 9 | #include <linux/linkage.h> |
| 10 | #include <linux/bitops.h> | 10 | #include <linux/bitops.h> |
| 11 | #include <linux/lockdep.h> | ||
| 11 | #include <asm/atomic.h> | 12 | #include <asm/atomic.h> |
| 12 | 13 | ||
| 13 | struct workqueue_struct; | 14 | struct workqueue_struct; |
| @@ -28,6 +29,9 @@ struct work_struct { | |||
| 28 | #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK) | 29 | #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK) |
| 29 | struct list_head entry; | 30 | struct list_head entry; |
| 30 | work_func_t func; | 31 | work_func_t func; |
| 32 | #ifdef CONFIG_LOCKDEP | ||
| 33 | struct lockdep_map lockdep_map; | ||
| 34 | #endif | ||
| 31 | }; | 35 | }; |
| 32 | 36 | ||
| 33 | #define WORK_DATA_INIT() ATOMIC_LONG_INIT(0) | 37 | #define WORK_DATA_INIT() ATOMIC_LONG_INIT(0) |
| @@ -41,10 +45,23 @@ struct execute_work { | |||
| 41 | struct work_struct work; | 45 | struct work_struct work; |
| 42 | }; | 46 | }; |
| 43 | 47 | ||
| 48 | #ifdef CONFIG_LOCKDEP | ||
| 49 | /* | ||
| 50 | * NB: because we have to copy the lockdep_map, setting _key | ||
| 51 | * here is required, otherwise it could get initialised to the | ||
| 52 | * copy of the lockdep_map! | ||
| 53 | */ | ||
| 54 | #define __WORK_INIT_LOCKDEP_MAP(n, k) \ | ||
| 55 | .lockdep_map = STATIC_LOCKDEP_MAP_INIT(n, k), | ||
| 56 | #else | ||
| 57 | #define __WORK_INIT_LOCKDEP_MAP(n, k) | ||
| 58 | #endif | ||
| 59 | |||
| 44 | #define __WORK_INITIALIZER(n, f) { \ | 60 | #define __WORK_INITIALIZER(n, f) { \ |
| 45 | .data = WORK_DATA_INIT(), \ | 61 | .data = WORK_DATA_INIT(), \ |
| 46 | .entry = { &(n).entry, &(n).entry }, \ | 62 | .entry = { &(n).entry, &(n).entry }, \ |
| 47 | .func = (f), \ | 63 | .func = (f), \ |
| 64 | __WORK_INIT_LOCKDEP_MAP(#n, &(n)) \ | ||
| 48 | } | 65 | } |
| 49 | 66 | ||
| 50 | #define __DELAYED_WORK_INITIALIZER(n, f) { \ | 67 | #define __DELAYED_WORK_INITIALIZER(n, f) { \ |
| @@ -76,12 +93,24 @@ struct execute_work { | |||
| 76 | * assignment of the work data initializer allows the compiler | 93 | * assignment of the work data initializer allows the compiler |
| 77 | * to generate better code. | 94 | * to generate better code. |
| 78 | */ | 95 | */ |
| 96 | #ifdef CONFIG_LOCKDEP | ||
| 97 | #define INIT_WORK(_work, _func) \ | ||
| 98 | do { \ | ||
| 99 | static struct lock_class_key __key; \ | ||
| 100 | \ | ||
| 101 | (_work)->data = (atomic_long_t) WORK_DATA_INIT(); \ | ||
| 102 | lockdep_init_map(&(_work)->lockdep_map, #_work, &__key, 0);\ | ||
| 103 | INIT_LIST_HEAD(&(_work)->entry); \ | ||
| 104 | PREPARE_WORK((_work), (_func)); \ | ||
| 105 | } while (0) | ||
| 106 | #else | ||
| 79 | #define INIT_WORK(_work, _func) \ | 107 | #define INIT_WORK(_work, _func) \ |
| 80 | do { \ | 108 | do { \ |
| 81 | (_work)->data = (atomic_long_t) WORK_DATA_INIT(); \ | 109 | (_work)->data = (atomic_long_t) WORK_DATA_INIT(); \ |
| 82 | INIT_LIST_HEAD(&(_work)->entry); \ | 110 | INIT_LIST_HEAD(&(_work)->entry); \ |
| 83 | PREPARE_WORK((_work), (_func)); \ | 111 | PREPARE_WORK((_work), (_func)); \ |
| 84 | } while (0) | 112 | } while (0) |
| 113 | #endif | ||
| 85 | 114 | ||
| 86 | #define INIT_DELAYED_WORK(_work, _func) \ | 115 | #define INIT_DELAYED_WORK(_work, _func) \ |
| 87 | do { \ | 116 | do { \ |
| @@ -118,9 +147,23 @@ struct execute_work { | |||
| 118 | clear_bit(WORK_STRUCT_PENDING, work_data_bits(work)) | 147 | clear_bit(WORK_STRUCT_PENDING, work_data_bits(work)) |
| 119 | 148 | ||
| 120 | 149 | ||
| 121 | extern struct workqueue_struct *__create_workqueue(const char *name, | 150 | extern struct workqueue_struct * |
| 122 | int singlethread, | 151 | __create_workqueue_key(const char *name, int singlethread, |
| 123 | int freezeable); | 152 | int freezeable, struct lock_class_key *key); |
| 153 | |||
| 154 | #ifdef CONFIG_LOCKDEP | ||
| 155 | #define __create_workqueue(name, singlethread, freezeable) \ | ||
| 156 | ({ \ | ||
| 157 | static struct lock_class_key __key; \ | ||
| 158 | \ | ||
| 159 | __create_workqueue_key((name), (singlethread), \ | ||
| 160 | (freezeable), &__key); \ | ||
| 161 | }) | ||
| 162 | #else | ||
| 163 | #define __create_workqueue(name, singlethread, freezeable) \ | ||
| 164 | __create_workqueue_key((name), (singlethread), (freezeable), NULL) | ||
| 165 | #endif | ||
| 166 | |||
| 124 | #define create_workqueue(name) __create_workqueue((name), 0, 0) | 167 | #define create_workqueue(name) __create_workqueue((name), 0, 0) |
| 125 | #define create_freezeable_workqueue(name) __create_workqueue((name), 1, 1) | 168 | #define create_freezeable_workqueue(name) __create_workqueue((name), 1, 1) |
| 126 | #define create_singlethread_workqueue(name) __create_workqueue((name), 1, 0) | 169 | #define create_singlethread_workqueue(name) __create_workqueue((name), 1, 0) |
