If this thread is preempted at(or before) location a, and the other thread
set the condition(kthread_stop). After that when this thread is re-scheduled,
calling set_current_state to set itself as state TASK_INTERRUPTIBLE, if it is
preempted again after that and before __set_current_state(TASK_RUNNING), it
triggers the invalid wakeup problem.
-----------------------
irq_wait_for_interrupt()
-----------------------
...
//location a
set_current_state(TASK_INTERRUPTIBLE);
//location b
while (!kthread_should_stop()) {

        if (test_and_clear_bit(IRQTF_RUNTHREAD,
                                           &action->thread_flags)) {
                        __set_current_state(TASK_RUNNING);
                        return 0;
        }
        schedule();//location c
        set_current_state(TASK_INTERRUPTIBLE);
        //location d
}
__set_current_state(TASK_RUNNING);
...

The following circumstance will also trigger this issue:
At location c, consumer is scheduled out, and be preempted after calling
set_current_state(TASK_INTERRUPTIBLE) when it be re-schdeuled.

To solve this problem, using preempt_disable() to bound the operaion that
setting the task state and the conditions(set by the wake thread) validation.

Signed-off-by: Libin <huawei.li...@huawei.com>
---
 kernel/irq/manage.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 514bcfd..09cb02f 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -655,6 +655,7 @@ static irqreturn_t irq_nested_primary_handler(int irq, void 
*dev_id)
 
 static int irq_wait_for_interrupt(struct irqaction *action)
 {
+       preempt_disable();
        set_current_state(TASK_INTERRUPTIBLE);
 
        while (!kthread_should_stop()) {
@@ -662,12 +663,16 @@ static int irq_wait_for_interrupt(struct irqaction 
*action)
                if (test_and_clear_bit(IRQTF_RUNTHREAD,
                                       &action->thread_flags)) {
                        __set_current_state(TASK_RUNNING);
+                       preempt_enable();
                        return 0;
                }
+               preempt_enable();
                schedule();
+               preempt_disable();
                set_current_state(TASK_INTERRUPTIBLE);
        }
        __set_current_state(TASK_RUNNING);
+       preempt_enable();
        return -1;
 }
 
-- 
1.8.2.1


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to