| From e44f5886438c05bdb6c793623a40bbbedb9251aa Mon Sep 17 00:00:00 2001 |
| From: Ido Yariv <ido@wizery.com> |
| Date: Thu, 1 Dec 2011 13:55:08 +0200 |
| Subject: [PATCH 22/24] genirq: Fix race condition when stopping the irq |
| thread |
| |
| 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: Paul Gortmaker <paul.gortmaker@windriver.com> |
| --- |
| kernel/irq/manage.c | 5 ++++- |
| 1 file changed, 4 insertions(+), 1 deletion(-) |
| |
| diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c |
| index 9080985..8668523 100644 |
| --- a/kernel/irq/manage.c |
| +++ b/kernel/irq/manage.c |
| @@ -479,8 +479,9 @@ static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id) |
| |
| 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)) { |
| @@ -488,7 +489,9 @@ static int irq_wait_for_interrupt(struct irqaction *action) |
| return 0; |
| } |
| schedule(); |
| + set_current_state(TASK_INTERRUPTIBLE); |
| } |
| + __set_current_state(TASK_RUNNING); |
| return -1; |
| } |
| |
| -- |
| 1.7.12.1 |
| |