Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f511fc62 authored by Paul E. McKenney's avatar Paul E. McKenney Committed by Paul E. McKenney
Browse files

rcu: Ensure that RCU_FAST_NO_HZ timers expire on correct CPU



Timers are subject to migration, which can lead to the following
system-hang scenario when CONFIG_RCU_FAST_NO_HZ=y:

1.	CPU 0 executes synchronize_rcu(), which posts an RCU callback.

2.	CPU 0 then goes idle.  It cannot immediately invoke the callback,
	but there is nothing RCU needs from ti, so it enters dyntick-idle
	mode after posting a timer.

3.	The timer gets migrated to CPU 1.

4.	CPU 0 never wakes up, so the synchronize_rcu() never returns, so
	the system hangs.

This commit fixes this problem by using mod_timer_pinned(), as suggested
by Peter Zijlstra, to ensure that the timer is actually posted on the
running CPU.

Reported-by: default avatarDipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: default avatarPaul E. McKenney <paul.mckenney@linaro.org>
Signed-off-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
parent 79b9a75f
Loading
Loading
Loading
Loading
+8 −5
Original line number Original line Diff line number Diff line
@@ -2110,6 +2110,8 @@ static void rcu_cleanup_after_idle(int cpu)
 */
 */
static void rcu_prepare_for_idle(int cpu)
static void rcu_prepare_for_idle(int cpu)
{
{
	struct timer_list *tp;

	/*
	/*
	 * If this is an idle re-entry, for example, due to use of
	 * If this is an idle re-entry, for example, due to use of
	 * RCU_NONIDLE() or the new idle-loop tracing API within the idle
	 * RCU_NONIDLE() or the new idle-loop tracing API within the idle
@@ -2121,9 +2123,10 @@ static void rcu_prepare_for_idle(int cpu)
	if (!per_cpu(rcu_idle_first_pass, cpu) &&
	if (!per_cpu(rcu_idle_first_pass, cpu) &&
	    (per_cpu(rcu_nonlazy_posted, cpu) ==
	    (per_cpu(rcu_nonlazy_posted, cpu) ==
	     per_cpu(rcu_nonlazy_posted_snap, cpu))) {
	     per_cpu(rcu_nonlazy_posted_snap, cpu))) {
		if (rcu_cpu_has_callbacks(cpu))
		if (rcu_cpu_has_callbacks(cpu)) {
			mod_timer(&per_cpu(rcu_idle_gp_timer, cpu),
			tp = &per_cpu(rcu_idle_gp_timer, cpu);
				  per_cpu(rcu_idle_gp_timer_expires, cpu));
			mod_timer_pinned(tp, per_cpu(rcu_idle_gp_timer_expires, cpu));
		}
		return;
		return;
	}
	}
	per_cpu(rcu_idle_first_pass, cpu) = 0;
	per_cpu(rcu_idle_first_pass, cpu) = 0;
@@ -2167,8 +2170,8 @@ static void rcu_prepare_for_idle(int cpu)
		else
		else
			per_cpu(rcu_idle_gp_timer_expires, cpu) =
			per_cpu(rcu_idle_gp_timer_expires, cpu) =
					   jiffies + RCU_IDLE_LAZY_GP_DELAY;
					   jiffies + RCU_IDLE_LAZY_GP_DELAY;
		mod_timer(&per_cpu(rcu_idle_gp_timer, cpu),
		tp = &per_cpu(rcu_idle_gp_timer, cpu);
			  per_cpu(rcu_idle_gp_timer_expires, cpu));
		mod_timer_pinned(tp, per_cpu(rcu_idle_gp_timer_expires, cpu));
		per_cpu(rcu_nonlazy_posted_snap, cpu) =
		per_cpu(rcu_nonlazy_posted_snap, cpu) =
			per_cpu(rcu_nonlazy_posted, cpu);
			per_cpu(rcu_nonlazy_posted, cpu);
		return; /* Nothing more to do immediately. */
		return; /* Nothing more to do immediately. */