aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhil Carmody <ext-phil.2.carmody@nokia.com>2010-10-20 18:57:33 -0400
committerThomas Gleixner <tglx@linutronix.de>2010-10-21 11:30:06 -0400
commitdd6414b50fa2b1cd247a8aa8f8bd42414b7453e1 (patch)
treed6572c35cf1997e2d18e451cb44742c89723d804
parent2bf1c05e3c406925e498d06da66b4828f0209ea6 (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.h25
-rw-r--r--include/linux/workqueue.h8
-rw-r--r--kernel/timer.c15
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;
88EXPORT_SYMBOL(boot_tvec_bases); 88EXPORT_SYMBOL(boot_tvec_bases);
89static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases; 89static 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 */
104static inline unsigned int tbase_get_deferrable(struct tvec_base *base) 92static 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
114static inline void timer_set_deferrable(struct timer_list *timer) 102static 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
120static inline void 107static inline void