rcu: Inform RCU of irq_exit() activity
authorPaul E. McKenney <paul.mckenney@linaro.org>
Thu, 12 Jan 2012 00:59:01 +0000 (16:59 -0800)
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Tue, 21 Feb 2012 17:03:42 +0000 (09:03 -0800)
This is a port to TINY_RCU of Peter Zijlstra's commit #ec433f0c5

The rcu_read_unlock_special() function relies on in_irq() to exclude
scheduler activity from interrupt level.  This fails because exit_irq()
can invoke the scheduler after clearing the preempt_count() bits that
in_irq() uses to determine that it is at interrupt level.  This situation
can result in failures as follows:

     $task IRQ SoftIRQ

     rcu_read_lock()

     /* do stuff */

     <preempt> |= UNLOCK_BLOCKED

     rcu_read_unlock()
       --t->rcu_read_lock_nesting

     irq_enter();
     /* do stuff, don't use RCU */
     irq_exit();
       sub_preempt_count(IRQ_EXIT_OFFSET);
       invoke_softirq()

     ttwu();
       spin_lock_irq(&pi->lock)
       rcu_read_lock();
       /* do stuff */
       rcu_read_unlock();
         rcu_read_unlock_special()
           rcu_report_exp_rnp()
             ttwu()
               spin_lock_irq(&pi->lock) /* deadlock */

       rcu_read_unlock_special(t);

This can be triggered 'easily' because invoke_softirq() immediately does
a ttwu() of ksoftirqd/# instead of doing the in-place softirq stuff first,
but even without that the above happens.

Cure this by also excluding softirqs from the rcu_read_unlock_special()
handler and ensuring the force_irqthreads ksoftirqd/# wakeup is done
from full softirq context.

It is also necessary to delay the ->rcu_read_lock_nesting decrement until
after rcu_read_unlock_special().  This delay is handled by the commit
"Protect __rcu_read_unlock() against scheduler-using irq handlers".

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
kernel/rcutiny_plugin.h

index 95df60ebe363ec6cd2f3390e4474eff52e12e00d..387c2759e1b04b6c833835b627110121d79aad63 100644 (file)
@@ -570,7 +570,7 @@ static noinline void rcu_read_unlock_special(struct task_struct *t)
                rcu_preempt_cpu_qs();
 
        /* Hardware IRQ handlers cannot block. */
-       if (in_irq()) {
+       if (in_irq() || in_serving_softirq()) {
                local_irq_restore(flags);
                return;
        }
This page took 0.04522 seconds and 5 git commands to generate.