aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zijlstra <peterz@infradead.org>2018-03-15 06:40:33 -0400
committerIngo Molnar <mingo@kernel.org>2018-03-20 03:23:17 -0400
commit6b2bb7265f0b62605e8caee3613449ed0db270b9 (patch)
tree62780e9b912f05daccd56f1bab31b0c280bb4043
parentfc4c5a3828bdba157f8ea406e1f4ceb75c13039c (diff)
sched/wait: Introduce wait_var_event()
As a replacement for the wait_on_atomic_t() API provide the wait_var_event() API. The wait_var_event() API is based on the very same hashed-waitqueue idea, but doesn't care about the type (atomic_t) or the specific condition (atomic_read() == 0). IOW. it's much more widely applicable/flexible. It shares all the benefits/disadvantages of a hashed-waitqueue approach with the existing wait_on_atomic_t/wait_on_bit() APIs. The API is modeled after the existing wait_event() API, but instead of taking a wait_queue_head, it takes an address. This addresses is hashed to obtain a wait_queue_head from the bit_wait_table. Similar to the wait_event() API, it takes a condition expression as second argument and will wait until this expression becomes true. The following are (mostly) identical replacements: wait_on_atomic_t(&my_atomic, atomic_t_wait, TASK_UNINTERRUPTIBLE); wake_up_atomic_t(&my_atomic); wait_var_event(&my_atomic, !atomic_read(&my_atomic)); wake_up_var(&my_atomic); The only difference is that wake_up_var() is an unconditional wakeup and doesn't check the previously hard-coded (atomic_read() == 0) condition here. This is of little concequence, since most callers are already conditional on atomic_dec_and_test() and the ones that are not, are trivial to make so. Tested-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: David Howells <dhowells@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--include/linux/wait_bit.h70
-rw-r--r--kernel/sched/wait_bit.c48
2 files changed, 118 insertions, 0 deletions
diff --git a/include/linux/wait_bit.h b/include/linux/wait_bit.h
index 61b39eaf7cad..3fcdb75d69cf 100644
--- a/include/linux/wait_bit.h
+++ b/include/linux/wait_bit.h
@@ -262,4 +262,74 @@ int wait_on_atomic_t(atomic_t *val, wait_atomic_t_action_f action, unsigned mode
262 return out_of_line_wait_on_atomic_t(val, action, mode); 262 return out_of_line_wait_on_atomic_t(val, action, mode);
263} 263}
264 264
265extern void init_wait_var_entry(struct wait_bit_queue_entry *wbq_entry, void *var, int flags);
266extern void wake_up_var(void *var);
267extern wait_queue_head_t *__var_waitqueue(void *p);
268
269#define ___wait_var_event(var, condition, state, exclusive, ret, cmd) \
270({ \
271 __label__ __out; \
272 struct wait_queue_head *__wq_head = __var_waitqueue(var); \
273 struct wait_bit_queue_entry __wbq_entry; \
274 long __ret = ret; /* explicit shadow */ \
275 \
276 init_wait_var_entry(&__wbq_entry, var, \
277 exclusive ? WQ_FLAG_EXCLUSIVE : 0); \
278 for (;;) { \
279 long __int = prepare_to_wait_event(__wq_head, \
280 &__wbq_entry.wq_entry, \
281 state); \
282 if (condition) \
283 break; \
284 \
285 if (___wait_is_interruptible(state) && __int) { \
286 __ret = __int; \
287 goto __out; \
288 } \
289 \
290 cmd; \
291 } \
292 finish_wait(__wq_head, &__wbq_entry.wq_entry); \
293__out: __ret; \
294})
295
296#define __wait_var_event(var, condition) \
297 ___wait_var_event(var, condition, TASK_UNINTERRUPTIBLE, 0, 0, \
298 schedule())
299
300#define wait_var_event(var, condition) \
301do { \
302 might_sleep(); \
303 if (condition) \
304 break; \
305 __wait_var_event(var, condition); \
306} while (0)
307
308#define __wait_var_event_killable(var, condition) \
309 ___wait_var_event(var, condition, TASK_KILLABLE, 0, 0, \
310 schedule())
311
312#define wait_var_event_killable(var, condition) \
313({ \
314 int __ret = 0; \
315 might_sleep(); \
316 if (!(condition)) \
317 __ret = __wait_var_event_killable(var, condition); \
318 __ret; \
319})
320
321#define __wait_var_event_timeout(var, condition, timeout) \
322 ___wait_var_event(var, ___wait_cond_timeout(condition), \
323 TASK_UNINTERRUPTIBLE, 0, timeout, \
324 __ret = schedule_timeout(__ret))
325
326#define wait_var_event_timeout(var, condition, timeout) \
327({ \
328 long __ret = timeout; \
329 might_sleep(); \
330 if (!___wait_cond_timeout(condition)) \
331 __ret = __wait_var_event_timeout(var, condition, timeout); \
332 __ret; \
333})
334
265#endif /* _LINUX_WAIT_BIT_H */ 335#endif /* _LINUX_WAIT_BIT_H */
diff --git a/kernel/sched/wait_bit.c b/kernel/sched/wait_bit.c
index 4239c78f5cd3..ed84ab245a05 100644
--- a/kernel/sched/wait_bit.c
+++ b/kernel/sched/wait_bit.c
@@ -149,6 +149,54 @@ void wake_up_bit(void *word, int bit)
149} 149}
150EXPORT_SYMBOL(wake_up_bit); 150EXPORT_SYMBOL(wake_up_bit);
151 151
152wait_queue_head_t *__var_waitqueue(void *p)
153{
154 if (BITS_PER_LONG == 64) {
155 unsigned long q = (unsigned long)p;
156
157 return bit_waitqueue((void *)(q & ~1), q & 1);
158 }
159 return bit_waitqueue(p, 0);
160}
161EXPORT_SYMBOL(__var_waitqueue);
162
163static int
164var_wake_function(struct wait_queue_entry *wq_entry, unsigned int mode,
165 int sync, void *arg)
166{
167 struct wait_bit_key *key = arg;
168 struct wait_bit_queue_entry *wbq_entry =
169 container_of(wq_entry, struct wait_bit_queue_entry, wq_entry);
170
171 if (wbq_entry->key.flags != key->flags ||
172 wbq_entry->key.bit_nr != key->bit_nr)
173 return 0;
174
175 return autoremove_wake_function(wq_entry, mode, sync, key);
176}
177
178void init_wait_var_entry(struct wait_bit_queue_entry *wbq_entry, void *var, int flags)
179{
180 *wbq_entry = (struct wait_bit_queue_entry){
181 .key = {
182 .flags = (var),
183 .bit_nr = -1,
184 },
185 .wq_entry = {
186 .private = current,
187 .func = var_wake_function,
188 .entry = LIST_HEAD_INIT(wbq_entry->wq_entry.entry),
189 },
190 };
191}
192EXPORT_SYMBOL(init_wait_var_entry);
193
194void wake_up_var(void *var)
195{
196 __wake_up_bit(__var_waitqueue(var), var, -1);
197}
198EXPORT_SYMBOL(wake_up_var);
199
152/* 200/*
153 * Manipulate the atomic_t address to produce a better bit waitqueue table hash 201 * Manipulate the atomic_t address to produce a better bit waitqueue table hash
154 * index (we're keying off bit -1, but that would produce a horrible hash 202 * index (we're keying off bit -1, but that would produce a horrible hash