diff options
author | Paul E. McKenney <paul.mckenney@linaro.org> | 2012-08-03 16:16:15 -0400 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2012-09-23 10:43:55 -0400 |
commit | 0d8ee37e2fcb7b77b9c5dee784beca5a215cad4c (patch) | |
tree | 6e7661bf5231b4c48a6681a9fa8e8ede7915d0a7 /kernel/rcutree.c | |
parent | 1331e7a1bbe1f11b19c4327ba0853bee2a606543 (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.c | 10 |
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); |