[tip/core/urgent,1/7] rcu: decrease rcu_report_exp_rnp coupling with scheduler

Message ID 1311121103-16978-1-git-send-email-paulmck@linux.vnet.ibm.com
State New
Headers show

Commit Message

Paul E. McKenney July 20, 2011, 12:18 a.m.
PREEMPT_RCU read-side critical sections blocking an expedited grace
period invoke rcu_report_exp_rnp().  When the last such critical section
has completed, rcu_report_exp_rnp() invokes the scheduler to wake up the
task that invoked synchronize_rcu_expedited() -- needlessly holding the
root rcu_node structure's lock while doing so, thus needlessly providing
a way for RCU and the scheduler to deadlock.

This commit therefore releases the root rcu_node structure's lock before
calling wake_up().

Reported-by: Ed Tomlinson <edt@aei.ca>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
---
 kernel/rcutree_plugin.h |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

Comments

Peter Zijlstra July 20, 2011, 2:40 a.m. | #1
On Tue, 2011-07-19 at 17:18 -0700, Paul E. McKenney wrote:
> +++ b/kernel/rcutree_plugin.h
> @@ -696,8 +696,10 @@ static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp)
>         raw_spin_lock_irqsave(&rnp->lock, flags);
>         for (;;) {
>                 if (!sync_rcu_preempt_exp_done(rnp))
> +                       raw_spin_unlock_irqrestore(&rnp->lock, flags);
>                         break;

I bet that'll all work much better if you wrap it in curly braces like:

		if (!sync_rcu_preempt_exp_done(rnp)) {
			raw_spin_unlock_irqrestore(&rnp->lock, flags);
			break;
		}

That might also explain those explosions Ed and Ben have been seeing.

>                 if (rnp->parent == NULL) {
> +                       raw_spin_unlock_irqrestore(&rnp->lock, flags);
>                         wake_up(&sync_rcu_preempt_exp_wq);
>                         break;
>                 }
> @@ -707,7 +709,6 @@ static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp)
>                 raw_spin_lock(&rnp->lock); /* irqs already disabled */
>                 rnp->expmask &= ~mask;
>         }
> -       raw_spin_unlock_irqrestore(&rnp->lock, flags);
>  }

Patch

diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h
index 75113cb..94a674e 100644
--- a/kernel/rcutree_plugin.h
+++ b/kernel/rcutree_plugin.h
@@ -696,8 +696,10 @@  static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp)
 	raw_spin_lock_irqsave(&rnp->lock, flags);
 	for (;;) {
 		if (!sync_rcu_preempt_exp_done(rnp))
+			raw_spin_unlock_irqrestore(&rnp->lock, flags);
 			break;
 		if (rnp->parent == NULL) {
+			raw_spin_unlock_irqrestore(&rnp->lock, flags);
 			wake_up(&sync_rcu_preempt_exp_wq);
 			break;
 		}
@@ -707,7 +709,6 @@  static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp)
 		raw_spin_lock(&rnp->lock); /* irqs already disabled */
 		rnp->expmask &= ~mask;
 	}
-	raw_spin_unlock_irqrestore(&rnp->lock, flags);
 }
 
 /*