Commit dd6414b50fa2b1cd247a8aa8f8bd42414b7453e1
Committed by
Thomas Gleixner
1 parent
2bf1c05e3c
Exists in
master
and in
39 other branches
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>
Showing 3 changed files with 34 additions and 14 deletions Side-by-side Diff
include/linux/timer.h
... | ... | @@ -48,6 +48,18 @@ |
48 | 48 | #define __TIMER_LOCKDEP_MAP_INITIALIZER(_kn) |
49 | 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 | 63 | #define TIMER_INITIALIZER(_function, _expires, _data) { \ |
52 | 64 | .entry = { .prev = TIMER_ENTRY_STATIC }, \ |
53 | 65 | .function = (_function), \ |
... | ... | @@ -55,6 +67,19 @@ |
55 | 67 | .data = (_data), \ |
56 | 68 | .base = &boot_tvec_bases, \ |
57 | 69 | .slack = -1, \ |
70 | + __TIMER_LOCKDEP_MAP_INITIALIZER( \ | |
71 | + __FILE__ ":" __stringify(__LINE__)) \ | |
72 | + } | |
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), \ | |
58 | 83 | __TIMER_LOCKDEP_MAP_INITIALIZER( \ |
59 | 84 | __FILE__ ":" __stringify(__LINE__)) \ |
60 | 85 | } |
include/linux/workqueue.h
... | ... | @@ -127,11 +127,19 @@ |
127 | 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 | 135 | #define DECLARE_WORK(n, f) \ |
131 | 136 | struct work_struct n = __WORK_INITIALIZER(n, f) |
132 | 137 | |
133 | 138 | #define DECLARE_DELAYED_WORK(n, f) \ |
134 | 139 | struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f) |
140 | + | |
141 | +#define DECLARE_DEFERRED_WORK(n, f) \ | |
142 | + struct delayed_work n = __DEFERRED_WORK_INITIALIZER(n, f) | |
135 | 143 | |
136 | 144 | /* |
137 | 145 | * initialize a work item's function pointer |
kernel/timer.c
... | ... | @@ -88,18 +88,6 @@ |
88 | 88 | EXPORT_SYMBOL(boot_tvec_bases); |
89 | 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 | 91 | /* Functions below help us manage 'deferrable' flag */ |
104 | 92 | static inline unsigned int tbase_get_deferrable(struct tvec_base *base) |
105 | 93 | { |
... | ... | @@ -113,8 +101,7 @@ |
113 | 101 | |
114 | 102 | static inline void timer_set_deferrable(struct timer_list *timer) |
115 | 103 | { |
116 | - timer->base = ((struct tvec_base *)((unsigned long)(timer->base) | | |
117 | - TBASE_DEFERRABLE_FLAG)); | |
104 | + timer->base = TBASE_MAKE_DEFERRED(timer->base); | |
118 | 105 | } |
119 | 106 | |
120 | 107 | static inline void |