diff options
author | Peter Zijlstra <peterz@infradead.org> | 2014-09-24 04:18:47 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2014-10-28 05:55:15 -0400 |
commit | 61ada528dea028331e99e8ceaed87c683ad25de2 (patch) | |
tree | 0037fc75e3629576238dce932babf7738e80cb86 | |
parent | 6f942a1f264e875c5f3ad6f505d7b500a3e7fa82 (diff) |
sched/wait: Provide infrastructure to deal with nested blocking
There are a few places that call blocking primitives from wait loops,
provide infrastructure to support this without the typical
task_struct::state collision.
We record the wakeup in wait_queue_t::flags which leaves
task_struct::state free to be used by others.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: tglx@linutronix.de
Cc: ilya.dryomov@inktank.com
Cc: umgwanakikbuti@gmail.com
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: http://lkml.kernel.org/r/20140924082242.051202318@infradead.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | include/linux/wait.h | 7 | ||||
-rw-r--r-- | kernel/sched/wait.c | 61 |
2 files changed, 67 insertions, 1 deletions
diff --git a/include/linux/wait.h b/include/linux/wait.h index e4a8eb9312ea..fc0e99395fbb 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h | |||
@@ -13,9 +13,12 @@ typedef struct __wait_queue wait_queue_t; | |||
13 | typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key); | 13 | typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key); |
14 | int default_wake_function(wait_queue_t *wait, unsigned mode, int flags, void *key); | 14 | int default_wake_function(wait_queue_t *wait, unsigned mode, int flags, void *key); |
15 | 15 | ||
16 | /* __wait_queue::flags */ | ||
17 | #define WQ_FLAG_EXCLUSIVE 0x01 | ||
18 | #define WQ_FLAG_WOKEN 0x02 | ||
19 | |||
16 | struct __wait_queue { | 20 | struct __wait_queue { |
17 | unsigned int flags; | 21 | unsigned int flags; |
18 | #define WQ_FLAG_EXCLUSIVE 0x01 | ||
19 | void *private; | 22 | void *private; |
20 | wait_queue_func_t func; | 23 | wait_queue_func_t func; |
21 | struct list_head task_list; | 24 | struct list_head task_list; |
@@ -830,6 +833,8 @@ void prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int sta | |||
830 | long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state); | 833 | long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state); |
831 | void finish_wait(wait_queue_head_t *q, wait_queue_t *wait); | 834 | void finish_wait(wait_queue_head_t *q, wait_queue_t *wait); |
832 | void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait, unsigned int mode, void *key); | 835 | void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait, unsigned int mode, void *key); |
836 | long wait_woken(wait_queue_t *wait, unsigned mode, long timeout); | ||
837 | int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key); | ||
833 | int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key); | 838 | int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key); |
834 | int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); | 839 | int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); |
835 | 840 | ||
diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index 5a62915f47a8..4dae1885db6f 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c | |||
@@ -297,6 +297,67 @@ int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void * | |||
297 | } | 297 | } |
298 | EXPORT_SYMBOL(autoremove_wake_function); | 298 | EXPORT_SYMBOL(autoremove_wake_function); |
299 | 299 | ||
300 | |||
301 | /* | ||
302 | * DEFINE_WAIT_FUNC(wait, woken_wake_func); | ||
303 | * | ||
304 | * add_wait_queue(&wq, &wait); | ||
305 | * for (;;) { | ||
306 | * if (condition) | ||
307 | * break; | ||
308 | * | ||
309 | * p->state = mode; condition = true; | ||
310 | * smp_mb(); // A smp_wmb(); // C | ||
311 | * if (!wait->flags & WQ_FLAG_WOKEN) wait->flags |= WQ_FLAG_WOKEN; | ||
312 | * schedule() try_to_wake_up(); | ||
313 | * p->state = TASK_RUNNING; ~~~~~~~~~~~~~~~~~~ | ||
314 | * wait->flags &= ~WQ_FLAG_WOKEN; condition = true; | ||
315 | * smp_mb() // B smp_wmb(); // C | ||
316 | * wait->flags |= WQ_FLAG_WOKEN; | ||
317 | * } | ||
318 | * remove_wait_queue(&wq, &wait); | ||
319 | * | ||
320 | */ | ||
321 | long wait_woken(wait_queue_t *wait, unsigned mode, long timeout) | ||
322 | { | ||
323 | set_current_state(mode); /* A */ | ||
324 | /* | ||
325 | * The above implies an smp_mb(), which matches with the smp_wmb() from | ||
326 | * woken_wake_function() such that if we observe WQ_FLAG_WOKEN we must | ||
327 | * also observe all state before the wakeup. | ||
328 | */ | ||
329 | if (!(wait->flags & WQ_FLAG_WOKEN)) | ||
330 | timeout = schedule_timeout(timeout); | ||
331 | __set_current_state(TASK_RUNNING); | ||
332 | |||
333 | /* | ||
334 | * The below implies an smp_mb(), it too pairs with the smp_wmb() from | ||
335 | * woken_wake_function() such that we must either observe the wait | ||
336 | * condition being true _OR_ WQ_FLAG_WOKEN such that we will not miss | ||
337 | * an event. | ||
338 | */ | ||
339 | set_mb(wait->flags, wait->flags & ~WQ_FLAG_WOKEN); /* B */ | ||
340 | |||
341 | return timeout; | ||
342 | } | ||
343 | EXPORT_SYMBOL(wait_woken); | ||
344 | |||
345 | int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key) | ||
346 | { | ||
347 | /* | ||
348 | * Although this function is called under waitqueue lock, LOCK | ||
349 | * doesn't imply write barrier and the users expects write | ||
350 | * barrier semantics on wakeup functions. The following | ||
351 | * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up() | ||
352 | * and is paired with set_mb() in wait_woken(). | ||
353 | */ | ||
354 | smp_wmb(); /* C */ | ||
355 | wait->flags |= WQ_FLAG_WOKEN; | ||
356 | |||
357 | return default_wake_function(wait, mode, sync, key); | ||
358 | } | ||
359 | EXPORT_SYMBOL(woken_wake_function); | ||
360 | |||
300 | int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *arg) | 361 | int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *arg) |
301 | { | 362 | { |
302 | struct wait_bit_key *key = arg; | 363 | struct wait_bit_key *key = arg; |