diff options
author | Peter Zijlstra <peterz@infradead.org> | 2014-10-31 06:57:30 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2014-11-04 01:17:44 -0500 |
commit | cb6538e740d7543cd989128625cf8cac4b471e0a (patch) | |
tree | c2b2c8d97c12fe06ead2f810471bd6f0317da2d0 /kernel/sched/wait.c | |
parent | 3427445afd26bd2395f29241319283a93f362cd0 (diff) |
sched/wait: Fix a kthread race with wait_woken()
There is a race between kthread_stop() and the new wait_woken() that
can result in a lack of progress.
CPU 0 | CPU 1
|
rfcomm_run() | kthread_stop()
... |
if (!test_bit(KTHREAD_SHOULD_STOP)) |
| set_bit(KTHREAD_SHOULD_STOP)
| wake_up_process()
wait_woken() | wait_for_completion()
set_current_state(INTERRUPTIBLE) |
if (!WQ_FLAG_WOKEN) |
schedule_timeout() |
|
After which both tasks will wait.. forever.
Fix this by having wait_woken() check for kthread_should_stop() but
only for kthreads (obviously).
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Peter Hurley <peter@hurleysoftware.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/sched/wait.c')
-rw-r--r-- | kernel/sched/wait.c | 7 |
1 files changed, 6 insertions, 1 deletions
diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index 4dae1885db6f..852143a79f36 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/mm.h> | 9 | #include <linux/mm.h> |
10 | #include <linux/wait.h> | 10 | #include <linux/wait.h> |
11 | #include <linux/hash.h> | 11 | #include <linux/hash.h> |
12 | #include <linux/kthread.h> | ||
12 | 13 | ||
13 | void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key) | 14 | void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key) |
14 | { | 15 | { |
@@ -297,6 +298,10 @@ int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void * | |||
297 | } | 298 | } |
298 | EXPORT_SYMBOL(autoremove_wake_function); | 299 | EXPORT_SYMBOL(autoremove_wake_function); |
299 | 300 | ||
301 | static inline bool is_kthread_should_stop(void) | ||
302 | { | ||
303 | return (current->flags & PF_KTHREAD) && kthread_should_stop(); | ||
304 | } | ||
300 | 305 | ||
301 | /* | 306 | /* |
302 | * DEFINE_WAIT_FUNC(wait, woken_wake_func); | 307 | * DEFINE_WAIT_FUNC(wait, woken_wake_func); |
@@ -326,7 +331,7 @@ long wait_woken(wait_queue_t *wait, unsigned mode, long timeout) | |||
326 | * woken_wake_function() such that if we observe WQ_FLAG_WOKEN we must | 331 | * woken_wake_function() such that if we observe WQ_FLAG_WOKEN we must |
327 | * also observe all state before the wakeup. | 332 | * also observe all state before the wakeup. |
328 | */ | 333 | */ |
329 | if (!(wait->flags & WQ_FLAG_WOKEN)) | 334 | if (!(wait->flags & WQ_FLAG_WOKEN) && !is_kthread_should_stop()) |
330 | timeout = schedule_timeout(timeout); | 335 | timeout = schedule_timeout(timeout); |
331 | __set_current_state(TASK_RUNNING); | 336 | __set_current_state(TASK_RUNNING); |
332 | 337 | ||