Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Phil Carmody and committed by
Thomas Gleixner
dd6414b5 2bf1c05e

+34 -14
+25
include/linux/timer.h
··· 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), \ ··· 67 55 .data = (_data), \ 68 56 .base = &boot_tvec_bases, \ 69 57 .slack = -1, \ 58 + __TIMER_LOCKDEP_MAP_INITIALIZER( \ 59 + __FILE__ ":" __stringify(__LINE__)) \ 60 + } 61 + 62 + #define TBASE_MAKE_DEFERRED(ptr) ((struct tvec_base *) \ 63 + ((unsigned char *)(ptr) + TBASE_DEFERRABLE_FLAG)) 64 + 65 + #define TIMER_DEFERRED_INITIALIZER(_function, _expires, _data) {\ 66 + .entry = { .prev = TIMER_ENTRY_STATIC }, \ 67 + .function = (_function), \ 68 + .expires = (_expires), \ 69 + .data = (_data), \ 70 + .base = TBASE_MAKE_DEFERRED(&boot_tvec_bases), \ 70 71 __TIMER_LOCKDEP_MAP_INITIALIZER( \ 71 72 __FILE__ ":" __stringify(__LINE__)) \ 72 73 }
+8
include/linux/workqueue.h
··· 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
+1 -14
kernel/timer.c
··· 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 { ··· 101 113 102 114 static inline void timer_set_deferrable(struct timer_list *timer) 103 115 { 104 - timer->base = ((struct tvec_base *)((unsigned long)(timer->base) | 105 - TBASE_DEFERRABLE_FLAG)); 116 + timer->base = TBASE_MAKE_DEFERRED(timer->base); 106 117 } 107 118 108 119 static inline void