| From 550acb19269d65f32e9ac4ddb26c2b2070e37f1c Mon Sep 17 00:00:00 2001 |
| From: Ido Yariv <ido@wizery.com> |
| Date: Thu, 1 Dec 2011 13:55:08 +0200 |
| Subject: genirq: Fix race condition when stopping the irq thread |
| |
| From: Ido Yariv <ido@wizery.com> |
| |
| 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> |
| |
| --- |
| kernel/irq/manage.c | 5 ++++- |
| 1 file changed, 4 insertions(+), 1 deletion(-) |
| |
| --- a/kernel/irq/manage.c |
| +++ b/kernel/irq/manage.c |
| @@ -475,8 +475,9 @@ static irqreturn_t irq_nested_primary_ha |
| |
| static int irq_wait_for_interrupt(struct irqaction *action) |
| { |
| + set_current_state(TASK_INTERRUPTIBLE); |
| + |
| while (!kthread_should_stop()) { |
| - set_current_state(TASK_INTERRUPTIBLE); |
| |
| if (test_and_clear_bit(IRQTF_RUNTHREAD, |
| &action->thread_flags)) { |
| @@ -484,7 +485,9 @@ static int irq_wait_for_interrupt(struct |
| return 0; |
| } |
| schedule(); |
| + set_current_state(TASK_INTERRUPTIBLE); |
| } |
| + __set_current_state(TASK_RUNNING); |
| return -1; |
| } |
| |