| From ba9f207c9f82115aba4ce04b22e0081af0ae300f Mon Sep 17 00:00:00 2001 |
| From: Frederic Weisbecker <fweisbec@gmail.com> |
| Date: Fri, 20 May 2011 02:09:54 +0200 |
| Subject: rcu: Fix unpaired rcu_irq_enter() from locking selftests |
| |
| From: Frederic Weisbecker <fweisbec@gmail.com> |
| |
| commit ba9f207c9f82115aba4ce04b22e0081af0ae300f upstream. |
| |
| HARDIRQ_ENTER() maps to irq_enter() which calls rcu_irq_enter(). |
| But HARDIRQ_EXIT() maps to __irq_exit() which doesn't call |
| rcu_irq_exit(). |
| |
| So for every locking selftest that simulates hardirq disabled, |
| we create an imbalance in the rcu extended quiescent state |
| internal state. |
| |
| As a result, after the first missing rcu_irq_exit(), subsequent |
| irqs won't exit dyntick-idle mode after leaving the interrupt |
| handler. This means that RCU won't see the affected CPU as being |
| in an extended quiescent state, resulting in long grace-period |
| delays (as in grace periods extending for hours). |
| |
| To fix this, just use __irq_enter() to simulate the hardirq |
| context. This is sufficient for the locking selftests as we |
| don't need to exit any extended quiescent state or perform |
| any check that irqs normally do when they wake up from idle. |
| |
| As a side effect, this patch makes it possible to restore |
| "rcu: Decrease memory-barrier usage based on semi-formal proof", |
| which eventually helped finding this bug. |
| |
| Reported-and-tested-by: Yinghai Lu <yinghai@kernel.org> |
| Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> |
| Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> |
| Cc: Ingo Molnar <mingo@elte.hu> |
| Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> |
| Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| lib/locking-selftest.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/lib/locking-selftest.c |
| +++ b/lib/locking-selftest.c |
| @@ -144,7 +144,7 @@ static void init_shared_classes(void) |
| |
| #define HARDIRQ_ENTER() \ |
| local_irq_disable(); \ |
| - irq_enter(); \ |
| + __irq_enter(); \ |
| WARN_ON(!in_irq()); |
| |
| #define HARDIRQ_EXIT() \ |