aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/rcutree.c
diff options
context:
space:
mode:
authorPaul E. McKenney <paul.mckenney@linaro.org>2012-08-03 16:16:15 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2012-09-23 10:43:55 -0400
commit0d8ee37e2fcb7b77b9c5dee784beca5a215cad4c (patch)
tree6e7661bf5231b4c48a6681a9fa8e8ede7915d0a7 /kernel/rcutree.c
parent1331e7a1bbe1f11b19c4327ba0853bee2a606543 (diff)
rcu: Disallow callback registry on offline CPUs
Posting a callback after the CPU_DEAD notifier effectively leaks that callback unless/until that CPU comes back online. Silence is unhelpful when attempting to track down such leaks, so this commit emits a WARN_ON_ONCE() and unconditionally leaks the callback when an offline CPU attempts to register a callback. The rdp->nxttail[RCU_NEXT_TAIL] is set to NULL in the CPU_DEAD notifier and restored in the CPU_UP_PREPARE notifier, allowing _call_rcu() to determine exactly when posting callbacks is illegal. Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Reviewed-by: Josh Triplett <josh@joshtriplett.org>
Diffstat (limited to 'kernel/rcutree.c')
-rw-r--r--kernel/rcutree.c10
1 files changed, 10 insertions, 0 deletions
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
index c45d3f745302..be76c80a14d1 100644
--- a/kernel/rcutree.c
+++ b/kernel/rcutree.c
@@ -1505,6 +1505,9 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
1505 WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL, 1505 WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL,
1506 "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n", 1506 "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n",
1507 cpu, rdp->qlen, rdp->nxtlist); 1507 cpu, rdp->qlen, rdp->nxtlist);
1508 init_callback_list(rdp);
1509 /* Disallow further callbacks on this CPU. */
1510 rdp->nxttail[RCU_NEXT_TAIL] = NULL;
1508} 1511}
1509 1512
1510#else /* #ifdef CONFIG_HOTPLUG_CPU */ 1513#else /* #ifdef CONFIG_HOTPLUG_CPU */
@@ -1927,6 +1930,12 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
1927 rdp = this_cpu_ptr(rsp->rda); 1930 rdp = this_cpu_ptr(rsp->rda);
1928 1931
1929 /* Add the callback to our list. */ 1932 /* Add the callback to our list. */
1933 if (unlikely(rdp->nxttail[RCU_NEXT_TAIL] == NULL)) {
1934 /* _call_rcu() is illegal on offline CPU; leak the callback. */
1935 WARN_ON_ONCE(1);
1936 local_irq_restore(flags);
1937 return;
1938 }
1930 ACCESS_ONCE(rdp->qlen)++; 1939 ACCESS_ONCE(rdp->qlen)++;
1931 if (lazy) 1940 if (lazy)
1932 rdp->qlen_lazy++; 1941 rdp->qlen_lazy++;
@@ -2464,6 +2473,7 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptible)
2464 rdp->qlen_last_fqs_check = 0; 2473 rdp->qlen_last_fqs_check = 0;
2465 rdp->n_force_qs_snap = rsp->n_force_qs; 2474 rdp->n_force_qs_snap = rsp->n_force_qs;
2466 rdp->blimit = blimit; 2475 rdp->blimit = blimit;
2476 init_callback_list(rdp); /* Re-enable callbacks on this CPU. */
2467 rdp->dynticks->dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; 2477 rdp->dynticks->dynticks_nesting = DYNTICK_TASK_EXIT_IDLE;
2468 atomic_set(&rdp->dynticks->dynticks, 2478 atomic_set(&rdp->dynticks->dynticks,
2469 (atomic_read(&rdp->dynticks->dynticks) & ~0x1) + 1); 2479 (atomic_read(&rdp->dynticks->dynticks) & ~0x1) + 1);