Commit dd6414b50fa2b1cd247a8aa8f8bd42414b7453e1

Authored by Phil Carmody
Committed by Thomas Gleixner
1 parent 2bf1c05e3c

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
... ... @@ -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