diff options
Diffstat (limited to 'kernel/sched/wait.c')
| -rw-r--r-- | kernel/sched/wait.c | 66 | 
1 files changed, 66 insertions, 0 deletions
diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index 5a62915f47a8..852143a79f36 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c @@ -9,6 +9,7 @@  #include <linux/mm.h>  #include <linux/wait.h>  #include <linux/hash.h> +#include <linux/kthread.h>  void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key)  { @@ -297,6 +298,71 @@ int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *  }  EXPORT_SYMBOL(autoremove_wake_function); +static inline bool is_kthread_should_stop(void) +{ +	return (current->flags & PF_KTHREAD) && kthread_should_stop(); +} + +/* + * DEFINE_WAIT_FUNC(wait, woken_wake_func); + * + * add_wait_queue(&wq, &wait); + * for (;;) { + *     if (condition) + *         break; + * + *     p->state = mode;				condition = true; + *     smp_mb(); // A				smp_wmb(); // C + *     if (!wait->flags & WQ_FLAG_WOKEN)	wait->flags |= WQ_FLAG_WOKEN; + *         schedule()				try_to_wake_up(); + *     p->state = TASK_RUNNING;		    ~~~~~~~~~~~~~~~~~~ + *     wait->flags &= ~WQ_FLAG_WOKEN;		condition = true; + *     smp_mb() // B				smp_wmb(); // C + *						wait->flags |= WQ_FLAG_WOKEN; + * } + * remove_wait_queue(&wq, &wait); + * + */ +long wait_woken(wait_queue_t *wait, unsigned mode, long timeout) +{ +	set_current_state(mode); /* A */ +	/* +	 * The above implies an smp_mb(), which matches with the smp_wmb() from +	 * woken_wake_function() such that if we observe WQ_FLAG_WOKEN we must +	 * also observe all state before the wakeup. +	 */ +	if (!(wait->flags & WQ_FLAG_WOKEN) && !is_kthread_should_stop()) +		timeout = schedule_timeout(timeout); +	__set_current_state(TASK_RUNNING); + +	/* +	 * The below implies an smp_mb(), it too pairs with the smp_wmb() from +	 * woken_wake_function() such that we must either observe the wait +	 * condition being true _OR_ WQ_FLAG_WOKEN such that we will not miss +	 * an event. +	 */ +	set_mb(wait->flags, wait->flags & ~WQ_FLAG_WOKEN); /* B */ + +	return timeout; +} +EXPORT_SYMBOL(wait_woken); + +int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ +	/* +	 * Although this function is called under waitqueue lock, LOCK +	 * doesn't imply write barrier and the users expects write +	 * barrier semantics on wakeup functions.  The following +	 * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up() +	 * and is paired with set_mb() in wait_woken(). +	 */ +	smp_wmb(); /* C */ +	wait->flags |= WQ_FLAG_WOKEN; + +	return default_wake_function(wait, mode, sync, key); +} +EXPORT_SYMBOL(woken_wake_function); +  int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *arg)  {  	struct wait_bit_key *key = arg;  | 
