aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zijlstra <peterz@infradead.org>2014-09-24 04:18:47 -0400
committerIngo Molnar <mingo@kernel.org>2014-10-28 05:55:15 -0400
commit61ada528dea028331e99e8ceaed87c683ad25de2 (patch)
tree0037fc75e3629576238dce932babf7738e80cb86
parent6f942a1f264e875c5f3ad6f505d7b500a3e7fa82 (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.h7
-rw-r--r--kernel/sched/wait.c61
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;
13typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key); 13typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);
14int default_wake_function(wait_queue_t *wait, unsigned mode, int flags, void *key); 14int 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
16struct __wait_queue { 20struct __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
830long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state); 833long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state);
831void finish_wait(wait_queue_head_t *q, wait_queue_t *wait); 834void finish_wait(wait_queue_head_t *q, wait_queue_t *wait);
832void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait, unsigned int mode, void *key); 835void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait, unsigned int mode, void *key);
836long wait_woken(wait_queue_t *wait, unsigned mode, long timeout);
837int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key);
833int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key); 838int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key);
834int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); 839int 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}
298EXPORT_SYMBOL(autoremove_wake_function); 298EXPORT_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 */
321long 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}
343EXPORT_SYMBOL(wait_woken);
344
345int 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}
359EXPORT_SYMBOL(woken_wake_function);
360
300int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *arg) 361int 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;