diff options
author | Peter Zijlstra <peterz@infradead.org> | 2018-03-15 06:40:33 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2018-03-20 03:23:17 -0400 |
commit | 6b2bb7265f0b62605e8caee3613449ed0db270b9 (patch) | |
tree | 62780e9b912f05daccd56f1bab31b0c280bb4043 | |
parent | fc4c5a3828bdba157f8ea406e1f4ceb75c13039c (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.h | 70 | ||||
-rw-r--r-- | kernel/sched/wait_bit.c | 48 |
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 | ||
265 | extern void init_wait_var_entry(struct wait_bit_queue_entry *wbq_entry, void *var, int flags); | ||
266 | extern void wake_up_var(void *var); | ||
267 | extern 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) \ | ||
301 | do { \ | ||
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 | } |
150 | EXPORT_SYMBOL(wake_up_bit); | 150 | EXPORT_SYMBOL(wake_up_bit); |
151 | 151 | ||
152 | wait_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 | } | ||
161 | EXPORT_SYMBOL(__var_waitqueue); | ||
162 | |||
163 | static int | ||
164 | var_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 | |||
178 | void 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 | } | ||
192 | EXPORT_SYMBOL(init_wait_var_entry); | ||
193 | |||
194 | void wake_up_var(void *var) | ||
195 | { | ||
196 | __wake_up_bit(__var_waitqueue(var), var, -1); | ||
197 | } | ||
198 | EXPORT_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 |