diff options
author | Ido Yariv <ido@wizery.com> | 2011-12-01 06:55:08 -0500 |
---|---|---|
committer | Herton Ronaldo Krzesinski <herton.krzesinski@canonical.com> | 2011-12-12 08:07:09 -0500 |
commit | b2c9c2abf0192a0b15cd914a4d7e554c99c2680d (patch) | |
tree | ec19965386fb3e755f9ee4ca11da8b784e892c44 /kernel/irq/manage.c | |
parent | 33cf3ca3ccd5f4c9a533da10c5f299c33d8727b8 (diff) |
genirq: Fix race condition when stopping the irq thread
BugLink: http://bugs.launchpad.net/bugs/902312
commit 550acb19269d65f32e9ac4ddb26c2b2070e37f1c upstream.
In irq_wait_for_interrupt(), the should_stop member is verified before
setting the task's state to TASK_INTERRUPTIBLE and calling schedule().
In case kthread_stop sets should_stop and wakes up the process after
should_stop is checked by the irq thread but before the task's state
is changed, the irq thread might never exit:
kthread_stop irq_wait_for_interrupt
------------ ----------------------
...
... while (!kthread_should_stop()) {
kthread->should_stop = 1;
wake_up_process(k);
wait_for_completion(&kthread->exited);
...
set_current_state(TASK_INTERRUPTIBLE);
...
schedule();
}
Fix this by checking if the thread should stop after modifying the
task's state.
[ tglx: Simplified it a bit ]
Signed-off-by: Ido Yariv <ido@wizery.com>
Link: http://lkml.kernel.org/r/1322740508-22640-1-git-send-email-ido@wizery.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
Diffstat (limited to 'kernel/irq/manage.c')
-rw-r--r-- | kernel/irq/manage.c | 5 |
1 files changed, 4 insertions, 1 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 0a7840aeb0f..a1aadab09aa 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -620,8 +620,9 @@ static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id) | |||
620 | 620 | ||
621 | static int irq_wait_for_interrupt(struct irqaction *action) | 621 | static int irq_wait_for_interrupt(struct irqaction *action) |
622 | { | 622 | { |
623 | set_current_state(TASK_INTERRUPTIBLE); | ||
624 | |||
623 | while (!kthread_should_stop()) { | 625 | while (!kthread_should_stop()) { |
624 | set_current_state(TASK_INTERRUPTIBLE); | ||
625 | 626 | ||
626 | if (test_and_clear_bit(IRQTF_RUNTHREAD, | 627 | if (test_and_clear_bit(IRQTF_RUNTHREAD, |
627 | &action->thread_flags)) { | 628 | &action->thread_flags)) { |
@@ -629,7 +630,9 @@ static int irq_wait_for_interrupt(struct irqaction *action) | |||
629 | return 0; | 630 | return 0; |
630 | } | 631 | } |
631 | schedule(); | 632 | schedule(); |
633 | set_current_state(TASK_INTERRUPTIBLE); | ||
632 | } | 634 | } |
635 | __set_current_state(TASK_RUNNING); | ||
633 | return -1; | 636 | return -1; |
634 | } | 637 | } |
635 | 638 | ||