aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2015-01-28 17:42:09 -0500
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2015-03-12 18:19:38 -0400
commit88428cc5c27c63a4313e213813bc39b9899224d5 (patch)
tree1bfd3c82b87c1c73cf7eeb1fcda4dc1cbe6b622c
parent528a25b00e1f84eaba6c98e63f58ee0a8e472102 (diff)
rcu: Handle outgoing CPUs on exit from idle loop
This commit informs RCU of an outgoing CPU just before that CPU invokes arch_cpu_idle_dead() during its last pass through the idle loop (via a new CPU_DYING_IDLE notifier value). This change means that RCU need not deal with outgoing CPUs passing through the scheduler after informing RCU that they are no longer online. Note that removing the CPU from the rcu_node ->qsmaskinit bit masks is done at CPU_DYING_IDLE time, and orphaning callbacks is still done at CPU_DEAD time, the reason being that at CPU_DEAD time we have another CPU that can adopt them. Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
-rw-r--r--include/linux/cpu.h2
-rw-r--r--include/linux/rcupdate.h2
-rw-r--r--kernel/rcu/tree.c41
-rw-r--r--kernel/sched/idle.c2
4 files changed, 37 insertions, 10 deletions
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 4744ef915acd..d028721748d4 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -95,6 +95,8 @@ enum {
95 * Called on the new cpu, just before 95 * Called on the new cpu, just before
96 * enabling interrupts. Must not sleep, 96 * enabling interrupts. Must not sleep,
97 * must not fail */ 97 * must not fail */
98#define CPU_DYING_IDLE 0x000B /* CPU (unsigned)v dying, reached
99 * idle loop. */
98#define CPU_BROKEN 0x000C /* CPU (unsigned)v did not die properly, 100#define CPU_BROKEN 0x000C /* CPU (unsigned)v did not die properly,
99 * perhaps due to preemption. */ 101 * perhaps due to preemption. */
100 102
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index 78097491cd99..762022f07afd 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -266,6 +266,8 @@ void rcu_idle_enter(void);
266void rcu_idle_exit(void); 266void rcu_idle_exit(void);
267void rcu_irq_enter(void); 267void rcu_irq_enter(void);
268void rcu_irq_exit(void); 268void rcu_irq_exit(void);
269int rcu_cpu_notify(struct notifier_block *self,
270 unsigned long action, void *hcpu);
269 271
270#ifdef CONFIG_RCU_STALL_COMMON 272#ifdef CONFIG_RCU_STALL_COMMON
271void rcu_sysrq_start(void); 273void rcu_sysrq_start(void);
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 79d53399247e..d5247ed44004 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -2476,6 +2476,26 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf)
2476} 2476}
2477 2477
2478/* 2478/*
2479 * The CPU is exiting the idle loop into the arch_cpu_idle_dead()
2480 * function. We now remove it from the rcu_node tree's ->qsmaskinit
2481 * bit masks.
2482 */
2483static void rcu_cleanup_dying_idle_cpu(int cpu, struct rcu_state *rsp)
2484{
2485 unsigned long flags;
2486 unsigned long mask;
2487 struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
2488 struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */
2489
2490 /* Remove outgoing CPU from mask in the leaf rcu_node structure. */
2491 mask = rdp->grpmask;
2492 raw_spin_lock_irqsave(&rnp->lock, flags);
2493 smp_mb__after_unlock_lock(); /* Enforce GP memory-order guarantee. */
2494 rnp->qsmaskinitnext &= ~mask;
2495 raw_spin_unlock_irqrestore(&rnp->lock, flags);
2496}
2497
2498/*
2479 * The CPU has been completely removed, and some other CPU is reporting 2499 * The CPU has been completely removed, and some other CPU is reporting
2480 * this fact from process context. Do the remainder of the cleanup, 2500 * this fact from process context. Do the remainder of the cleanup,
2481 * including orphaning the outgoing CPU's RCU callbacks, and also 2501 * including orphaning the outgoing CPU's RCU callbacks, and also
@@ -2485,7 +2505,6 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf)
2485static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) 2505static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
2486{ 2506{
2487 unsigned long flags; 2507 unsigned long flags;
2488 unsigned long mask;
2489 struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); 2508 struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
2490 struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */ 2509 struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */
2491 2510
@@ -2498,13 +2517,6 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
2498 rcu_adopt_orphan_cbs(rsp, flags); 2517 rcu_adopt_orphan_cbs(rsp, flags);
2499 raw_spin_unlock_irqrestore(&rsp->orphan_lock, flags); 2518 raw_spin_unlock_irqrestore(&rsp->orphan_lock, flags);
2500 2519
2501 /* Remove outgoing CPU from mask in the leaf rcu_node structure. */
2502 mask = rdp->grpmask;
2503 raw_spin_lock_irqsave(&rnp->lock, flags);
2504 smp_mb__after_unlock_lock(); /* Enforce GP memory-order guarantee. */
2505 rnp->qsmaskinitnext &= ~mask;
2506 raw_spin_unlock_irqrestore(&rnp->lock, flags);
2507
2508 WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL, 2520 WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL,
2509 "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n", 2521 "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n",
2510 cpu, rdp->qlen, rdp->nxtlist); 2522 cpu, rdp->qlen, rdp->nxtlist);
@@ -2520,6 +2532,10 @@ static void __maybe_unused rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf)
2520{ 2532{
2521} 2533}
2522 2534
2535static void rcu_cleanup_dying_idle_cpu(int cpu, struct rcu_state *rsp)
2536{
2537}
2538
2523static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) 2539static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
2524{ 2540{
2525} 2541}
@@ -3733,8 +3749,8 @@ static void rcu_prepare_cpu(int cpu)
3733/* 3749/*
3734 * Handle CPU online/offline notification events. 3750 * Handle CPU online/offline notification events.
3735 */ 3751 */
3736static int rcu_cpu_notify(struct notifier_block *self, 3752int rcu_cpu_notify(struct notifier_block *self,
3737 unsigned long action, void *hcpu) 3753 unsigned long action, void *hcpu)
3738{ 3754{
3739 long cpu = (long)hcpu; 3755 long cpu = (long)hcpu;
3740 struct rcu_data *rdp = per_cpu_ptr(rcu_state_p->rda, cpu); 3756 struct rcu_data *rdp = per_cpu_ptr(rcu_state_p->rda, cpu);
@@ -3760,6 +3776,11 @@ static int rcu_cpu_notify(struct notifier_block *self,
3760 for_each_rcu_flavor(rsp) 3776 for_each_rcu_flavor(rsp)
3761 rcu_cleanup_dying_cpu(rsp); 3777 rcu_cleanup_dying_cpu(rsp);
3762 break; 3778 break;
3779 case CPU_DYING_IDLE:
3780 for_each_rcu_flavor(rsp) {
3781 rcu_cleanup_dying_idle_cpu(cpu, rsp);
3782 }
3783 break;
3763 case CPU_DEAD: 3784 case CPU_DEAD:
3764 case CPU_DEAD_FROZEN: 3785 case CPU_DEAD_FROZEN:
3765 case CPU_UP_CANCELED: 3786 case CPU_UP_CANCELED:
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index e99e361ade20..b0090accfb5b 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -225,6 +225,8 @@ static void cpu_idle_loop(void)
225 rmb(); 225 rmb();
226 226
227 if (cpu_is_offline(smp_processor_id())) { 227 if (cpu_is_offline(smp_processor_id())) {
228 rcu_cpu_notify(NULL, CPU_DYING_IDLE,
229 (void *)(long)smp_processor_id());
228 smp_mb(); /* all activity before dead. */ 230 smp_mb(); /* all activity before dead. */
229 this_cpu_write(cpu_dead_idle, true); 231 this_cpu_write(cpu_dead_idle, true);
230 arch_cpu_idle_dead(); 232 arch_cpu_idle_dead();