diff options
| author | Phil Carmody <ext-phil.2.carmody@nokia.com> | 2010-10-20 18:57:33 -0400 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2010-10-21 11:30:06 -0400 |
| commit | dd6414b50fa2b1cd247a8aa8f8bd42414b7453e1 (patch) | |
| tree | d6572c35cf1997e2d18e451cb44742c89723d804 | |
| parent | 2bf1c05e3c406925e498d06da66b4828f0209ea6 (diff) | |
timer: Permit statically-declared work with deferrable timers
Currently, you have to just define a delayed_work uninitialised, and then
initialise it before first use. That's a tad clumsy. At risk of playing
mind-games with the compiler, fooling it into doing pointer arithmetic
with compile-time-constants, this lets clients properly initialise delayed
work with deferrable timers statically.
This patch was inspired by the issues which lead Artem Bityutskiy to
commit 8eab945c5616fc984 ("sunrpc: make the cache cleaner workqueue
deferrable").
Signed-off-by: Phil Carmody <ext-phil.2.carmody@nokia.com>
Acked-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Cc: Arjan van de Ven <arjan@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
| -rw-r--r-- | include/linux/timer.h | 25 | ||||
| -rw-r--r-- | include/linux/workqueue.h | 8 | ||||
| -rw-r--r-- | kernel/timer.c | 15 |
3 files changed, 34 insertions, 14 deletions
diff --git a/include/linux/timer.h b/include/linux/timer.h index 1794674c1a52..cbfb7a355d30 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h | |||
| @@ -48,6 +48,18 @@ extern struct tvec_base boot_tvec_bases; | |||
| 48 | #define __TIMER_LOCKDEP_MAP_INITIALIZER(_kn) | 48 | #define __TIMER_LOCKDEP_MAP_INITIALIZER(_kn) |
| 49 | #endif | 49 | #endif |
| 50 | 50 | ||
| 51 | /* | ||
| 52 | * Note that all tvec_bases are 2 byte aligned and lower bit of | ||
| 53 | * base in timer_list is guaranteed to be zero. Use the LSB to | ||
| 54 | * indicate whether the timer is deferrable. | ||
| 55 | * | ||
| 56 | * A deferrable timer will work normally when the system is busy, but | ||
| 57 | * will not cause a CPU to come out of idle just to service it; instead, | ||
| 58 | * the timer will be serviced when the CPU eventually wakes up with a | ||
| 59 | * subsequent non-deferrable timer. | ||
| 60 | */ | ||
| 61 | #define TBASE_DEFERRABLE_FLAG (0x1) | ||
| 62 | |||
| 51 | #define TIMER_INITIALIZER(_function, _expires, _data) { \ | 63 | #define TIMER_INITIALIZER(_function, _expires, _data) { \ |
| 52 | .entry = { .prev = TIMER_ENTRY_STATIC }, \ | 64 | .entry = { .prev = TIMER_ENTRY_STATIC }, \ |
| 53 | .function = (_function), \ | 65 | .function = (_function), \ |
| @@ -59,6 +71,19 @@ extern struct tvec_base boot_tvec_bases; | |||
| 59 | __FILE__ ":" __stringify(__LINE__)) \ | 71 | __FILE__ ":" __stringify(__LINE__)) \ |
| 60 | } | 72 | } |
| 61 | 73 | ||
| 74 | #define TBASE_MAKE_DEFERRED(ptr) ((struct tvec_base *) \ | ||
| 75 | ((unsigned char *)(ptr) + TBASE_DEFERRABLE_FLAG)) | ||
| 76 | |||
| 77 | #define TIMER_DEFERRED_INITIALIZER(_function, _expires, _data) {\ | ||
| 78 | .entry = { .prev = TIMER_ENTRY_STATIC }, \ | ||
| 79 | .function = (_function), \ | ||
| 80 | .expires = (_expires), \ | ||
| 81 | .data = (_data), \ | ||
| 82 | .base = TBASE_MAKE_DEFERRED(&boot_tvec_bases), \ | ||
| 83 | __TIMER_LOCKDEP_MAP_INITIALIZER( \ | ||
| 84 | __FILE__ ":" __stringify(__LINE__)) \ | ||
| 85 | } | ||
| 86 | |||
| 62 | #define DEFINE_TIMER(_name, _function, _expires, _data) \ | 87 | #define DEFINE_TIMER(_name, _function, _expires, _data) \ |
| 63 | struct timer_list _name = \ | 88 | struct timer_list _name = \ |
| 64 | TIMER_INITIALIZER(_function, _expires, _data) | 89 | TIMER_INITIALIZER(_function, _expires, _data) |
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index f11100f96482..88238c15ec3e 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h | |||
| @@ -127,12 +127,20 @@ struct execute_work { | |||
| 127 | .timer = TIMER_INITIALIZER(NULL, 0, 0), \ | 127 | .timer = TIMER_INITIALIZER(NULL, 0, 0), \ |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | #define __DEFERRED_WORK_INITIALIZER(n, f) { \ | ||
| 131 | .work = __WORK_INITIALIZER((n).work, (f)), \ | ||
| 132 | .timer = TIMER_DEFERRED_INITIALIZER(NULL, 0, 0), \ | ||
| 133 | } | ||
| 134 | |||
| 130 | #define DECLARE_WORK(n, f) \ | 135 | #define DECLARE_WORK(n, f) \ |
| 131 | struct work_struct n = __WORK_INITIALIZER(n, f) | 136 | struct work_struct n = __WORK_INITIALIZER(n, f) |
| 132 | 137 | ||
| 133 | #define DECLARE_DELAYED_WORK(n, f) \ | 138 | #define DECLARE_DELAYED_WORK(n, f) \ |
| 134 | struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f) | 139 | struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f) |
| 135 | 140 | ||
| 141 | #define DECLARE_DEFERRED_WORK(n, f) \ | ||
| 142 | struct delayed_work n = __DEFERRED_WORK_INITIALIZER(n, f) | ||
| 143 | |||
| 136 | /* | 144 | /* |
| 137 | * initialize a work item's function pointer | 145 | * initialize a work item's function pointer |
| 138 | */ | 146 | */ |
diff --git a/kernel/timer.c b/kernel/timer.c index 97bf05baade7..72853b256ff2 100644 --- a/kernel/timer.c +++ b/kernel/timer.c | |||
| @@ -88,18 +88,6 @@ struct tvec_base boot_tvec_bases; | |||
| 88 | EXPORT_SYMBOL(boot_tvec_bases); | 88 | EXPORT_SYMBOL(boot_tvec_bases); |
| 89 | static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases; | 89 | static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases; |
| 90 | 90 | ||
| 91 | /* | ||
| 92 | * Note that all tvec_bases are 2 byte aligned and lower bit of | ||
| 93 | * base in timer_list is guaranteed to be zero. Use the LSB to | ||
| 94 | * indicate whether the timer is deferrable. | ||
| 95 | * | ||
| 96 | * A deferrable timer will work normally when the system is busy, but | ||
| 97 | * will not cause a CPU to come out of idle just to service it; instead, | ||
| 98 | * the timer will be serviced when the CPU eventually wakes up with a | ||
| 99 | * subsequent non-deferrable timer. | ||
| 100 | */ | ||
| 101 | #define TBASE_DEFERRABLE_FLAG (0x1) | ||
| 102 | |||
| 103 | /* Functions below help us manage 'deferrable' flag */ | 91 | /* Functions below help us manage 'deferrable' flag */ |
| 104 | static inline unsigned int tbase_get_deferrable(struct tvec_base *base) | 92 | static inline unsigned int tbase_get_deferrable(struct tvec_base *base) |
| 105 | { | 93 | { |
| @@ -113,8 +101,7 @@ static inline struct tvec_base *tbase_get_base(struct tvec_base *base) | |||
| 113 | 101 | ||
| 114 | static inline void timer_set_deferrable(struct timer_list *timer) | 102 | static inline void timer_set_deferrable(struct timer_list *timer) |
| 115 | { | 103 | { |
| 116 | timer->base = ((struct tvec_base *)((unsigned long)(timer->base) | | 104 | timer->base = TBASE_MAKE_DEFERRED(timer->base); |
| 117 | TBASE_DEFERRABLE_FLAG)); | ||
| 118 | } | 105 | } |
| 119 | 106 | ||
| 120 | static inline void | 107 | static inline void |
