| From 6e91f8cb138625be96070b778d9ba71ce520ea7e Mon Sep 17 00:00:00 2001 |
| From: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> |
| Date: Mon, 11 May 2015 11:13:05 -0700 |
| Subject: rcu: Correctly handle non-empty Tiny RCU callback list with none ready |
| |
| From: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> |
| |
| commit 6e91f8cb138625be96070b778d9ba71ce520ea7e upstream. |
| |
| If, at the time __rcu_process_callbacks() is invoked, there are callbacks |
| in Tiny RCU's callback list, but none of them are ready to be invoked, |
| the current list-management code will knit the non-ready callbacks out |
| of the list. This can result in hangs and possibly worse. This commit |
| therefore inserts a check for there being no callbacks that can be |
| invoked immediately. |
| |
| This bug is unlikely to occur -- you have to get a new callback between |
| the time rcu_sched_qs() or rcu_bh_qs() was called, but before we get to |
| __rcu_process_callbacks(). It was detected by the addition of RCU-bh |
| testing to rcutorture, which in turn was instigated by Iftekhar Ahmed's |
| mutation testing. Although this bug was made much more likely by |
| 915e8a4fe45e (rcu: Remove fastpath from __rcu_process_callbacks()), this |
| did not cause the bug, but rather made it much more probable. That |
| said, it takes more than 40 hours of rcutorture testing, on average, |
| for this bug to appear, so this fix cannot be considered an emergency. |
| |
| Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> |
| Reviewed-by: Josh Triplett <josh@joshtriplett.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| kernel/rcu/tiny.c | 5 +++++ |
| 1 file changed, 5 insertions(+) |
| |
| --- a/kernel/rcu/tiny.c |
| +++ b/kernel/rcu/tiny.c |
| @@ -182,6 +182,11 @@ static void __rcu_process_callbacks(stru |
| |
| /* Move the ready-to-invoke callbacks to a local list. */ |
| local_irq_save(flags); |
| + if (rcp->donetail == &rcp->rcucblist) { |
| + /* No callbacks ready, so just leave. */ |
| + local_irq_restore(flags); |
| + return; |
| + } |
| RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, rcp->qlen, -1)); |
| list = rcp->rcucblist; |
| rcp->rcucblist = *rcp->donetail; |