diff options
| -rw-r--r-- | include/trace/events/rcu.h | 18 | ||||
| -rw-r--r-- | kernel/cpu.c | 14 | ||||
| -rw-r--r-- | kernel/rcu/tree.c | 15 | ||||
| -rw-r--r-- | kernel/rcu/tree.h | 1 | ||||
| -rw-r--r-- | kernel/rcu/tree_plugin.h | 33 |
5 files changed, 66 insertions, 15 deletions
diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index 9b56f37148cf..e335e7d8c6c2 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h | |||
| @@ -660,18 +660,18 @@ TRACE_EVENT(rcu_torture_read, | |||
| 660 | /* | 660 | /* |
| 661 | * Tracepoint for _rcu_barrier() execution. The string "s" describes | 661 | * Tracepoint for _rcu_barrier() execution. The string "s" describes |
| 662 | * the _rcu_barrier phase: | 662 | * the _rcu_barrier phase: |
| 663 | * "Begin": rcu_barrier_callback() started. | 663 | * "Begin": _rcu_barrier() started. |
| 664 | * "Check": rcu_barrier_callback() checking for piggybacking. | 664 | * "Check": _rcu_barrier() checking for piggybacking. |
| 665 | * "EarlyExit": rcu_barrier_callback() piggybacked, thus early exit. | 665 | * "EarlyExit": _rcu_barrier() piggybacked, thus early exit. |
| 666 | * "Inc1": rcu_barrier_callback() piggyback check counter incremented. | 666 | * "Inc1": _rcu_barrier() piggyback check counter incremented. |
| 667 | * "Offline": rcu_barrier_callback() found offline CPU | 667 | * "OfflineNoCB": _rcu_barrier() found callback on never-online CPU |
| 668 | * "OnlineNoCB": rcu_barrier_callback() found online no-CBs CPU. | 668 | * "OnlineNoCB": _rcu_barrier() found online no-CBs CPU. |
| 669 | * "OnlineQ": rcu_barrier_callback() found online CPU with callbacks. | 669 | * "OnlineQ": _rcu_barrier() found online CPU with callbacks. |
| 670 | * "OnlineNQ": rcu_barrier_callback() found online CPU, no callbacks. | 670 | * "OnlineNQ": _rcu_barrier() found online CPU, no callbacks. |
| 671 | * "IRQ": An rcu_barrier_callback() callback posted on remote CPU. | 671 | * "IRQ": An rcu_barrier_callback() callback posted on remote CPU. |
| 672 | * "CB": An rcu_barrier_callback() invoked a callback, not the last. | 672 | * "CB": An rcu_barrier_callback() invoked a callback, not the last. |
| 673 | * "LastCB": An rcu_barrier_callback() invoked the last callback. | 673 | * "LastCB": An rcu_barrier_callback() invoked the last callback. |
| 674 | * "Inc2": rcu_barrier_callback() piggyback check counter incremented. | 674 | * "Inc2": _rcu_barrier() piggyback check counter incremented. |
| 675 | * The "cpu" argument is the CPU or -1 if meaningless, the "cnt" argument | 675 | * The "cpu" argument is the CPU or -1 if meaningless, the "cnt" argument |
| 676 | * is the count of remaining callbacks, and "done" is the piggybacking count. | 676 | * is the count of remaining callbacks, and "done" is the piggybacking count. |
| 677 | */ | 677 | */ |
diff --git a/kernel/cpu.c b/kernel/cpu.c index 356450f09c1f..90a3d017b90c 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
| @@ -64,6 +64,8 @@ static struct { | |||
| 64 | * an ongoing cpu hotplug operation. | 64 | * an ongoing cpu hotplug operation. |
| 65 | */ | 65 | */ |
| 66 | int refcount; | 66 | int refcount; |
| 67 | /* And allows lockless put_online_cpus(). */ | ||
| 68 | atomic_t puts_pending; | ||
| 67 | 69 | ||
| 68 | #ifdef CONFIG_DEBUG_LOCK_ALLOC | 70 | #ifdef CONFIG_DEBUG_LOCK_ALLOC |
| 69 | struct lockdep_map dep_map; | 71 | struct lockdep_map dep_map; |
| @@ -113,7 +115,11 @@ void put_online_cpus(void) | |||
| 113 | { | 115 | { |
| 114 | if (cpu_hotplug.active_writer == current) | 116 | if (cpu_hotplug.active_writer == current) |
| 115 | return; | 117 | return; |
| 116 | mutex_lock(&cpu_hotplug.lock); | 118 | if (!mutex_trylock(&cpu_hotplug.lock)) { |
| 119 | atomic_inc(&cpu_hotplug.puts_pending); | ||
| 120 | cpuhp_lock_release(); | ||
| 121 | return; | ||
| 122 | } | ||
| 117 | 123 | ||
| 118 | if (WARN_ON(!cpu_hotplug.refcount)) | 124 | if (WARN_ON(!cpu_hotplug.refcount)) |
| 119 | cpu_hotplug.refcount++; /* try to fix things up */ | 125 | cpu_hotplug.refcount++; /* try to fix things up */ |
| @@ -155,6 +161,12 @@ void cpu_hotplug_begin(void) | |||
| 155 | cpuhp_lock_acquire(); | 161 | cpuhp_lock_acquire(); |
| 156 | for (;;) { | 162 | for (;;) { |
| 157 | mutex_lock(&cpu_hotplug.lock); | 163 | mutex_lock(&cpu_hotplug.lock); |
| 164 | if (atomic_read(&cpu_hotplug.puts_pending)) { | ||
| 165 | int delta; | ||
| 166 | |||
| 167 | delta = atomic_xchg(&cpu_hotplug.puts_pending, 0); | ||
| 168 | cpu_hotplug.refcount -= delta; | ||
| 169 | } | ||
| 158 | if (likely(!cpu_hotplug.refcount)) | 170 | if (likely(!cpu_hotplug.refcount)) |
| 159 | break; | 171 | break; |
| 160 | __set_current_state(TASK_UNINTERRUPTIBLE); | 172 | __set_current_state(TASK_UNINTERRUPTIBLE); |
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 133e47223095..9815447d22e0 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c | |||
| @@ -3299,11 +3299,16 @@ static void _rcu_barrier(struct rcu_state *rsp) | |||
| 3299 | continue; | 3299 | continue; |
| 3300 | rdp = per_cpu_ptr(rsp->rda, cpu); | 3300 | rdp = per_cpu_ptr(rsp->rda, cpu); |
| 3301 | if (rcu_is_nocb_cpu(cpu)) { | 3301 | if (rcu_is_nocb_cpu(cpu)) { |
| 3302 | _rcu_barrier_trace(rsp, "OnlineNoCB", cpu, | 3302 | if (!rcu_nocb_cpu_needs_barrier(rsp, cpu)) { |
| 3303 | rsp->n_barrier_done); | 3303 | _rcu_barrier_trace(rsp, "OfflineNoCB", cpu, |
| 3304 | atomic_inc(&rsp->barrier_cpu_count); | 3304 | rsp->n_barrier_done); |
| 3305 | __call_rcu(&rdp->barrier_head, rcu_barrier_callback, | 3305 | } else { |
| 3306 | rsp, cpu, 0); | 3306 | _rcu_barrier_trace(rsp, "OnlineNoCB", cpu, |
| 3307 | rsp->n_barrier_done); | ||
| 3308 | atomic_inc(&rsp->barrier_cpu_count); | ||
| 3309 | __call_rcu(&rdp->barrier_head, | ||
| 3310 | rcu_barrier_callback, rsp, cpu, 0); | ||
| 3311 | } | ||
| 3307 | } else if (ACCESS_ONCE(rdp->qlen)) { | 3312 | } else if (ACCESS_ONCE(rdp->qlen)) { |
| 3308 | _rcu_barrier_trace(rsp, "OnlineQ", cpu, | 3313 | _rcu_barrier_trace(rsp, "OnlineQ", cpu, |
| 3309 | rsp->n_barrier_done); | 3314 | rsp->n_barrier_done); |
diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index d03764652d91..bbdc45d8d74f 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h | |||
| @@ -587,6 +587,7 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu); | |||
| 587 | static void print_cpu_stall_info_end(void); | 587 | static void print_cpu_stall_info_end(void); |
| 588 | static void zero_cpu_stall_ticks(struct rcu_data *rdp); | 588 | static void zero_cpu_stall_ticks(struct rcu_data *rdp); |
| 589 | static void increment_cpu_stall_ticks(void); | 589 | static void increment_cpu_stall_ticks(void); |
| 590 | static bool rcu_nocb_cpu_needs_barrier(struct rcu_state *rsp, int cpu); | ||
| 590 | static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq); | 591 | static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq); |
| 591 | static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp); | 592 | static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp); |
| 592 | static void rcu_init_one_nocb(struct rcu_node *rnp); | 593 | static void rcu_init_one_nocb(struct rcu_node *rnp); |
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 387dd4599344..c1d7f27bd38f 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h | |||
| @@ -2050,6 +2050,33 @@ static void wake_nocb_leader(struct rcu_data *rdp, bool force) | |||
| 2050 | } | 2050 | } |
| 2051 | 2051 | ||
| 2052 | /* | 2052 | /* |
| 2053 | * Does the specified CPU need an RCU callback for the specified flavor | ||
| 2054 | * of rcu_barrier()? | ||
| 2055 | */ | ||
| 2056 | static bool rcu_nocb_cpu_needs_barrier(struct rcu_state *rsp, int cpu) | ||
| 2057 | { | ||
| 2058 | struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); | ||
| 2059 | struct rcu_head *rhp; | ||
| 2060 | |||
| 2061 | /* No-CBs CPUs might have callbacks on any of three lists. */ | ||
| 2062 | rhp = ACCESS_ONCE(rdp->nocb_head); | ||
| 2063 | if (!rhp) | ||
| 2064 | rhp = ACCESS_ONCE(rdp->nocb_gp_head); | ||
| 2065 | if (!rhp) | ||
| 2066 | rhp = ACCESS_ONCE(rdp->nocb_follower_head); | ||
| 2067 | |||
| 2068 | /* Having no rcuo kthread but CBs after scheduler starts is bad! */ | ||
| 2069 | if (!ACCESS_ONCE(rdp->nocb_kthread) && rhp) { | ||
| 2070 | /* RCU callback enqueued before CPU first came online??? */ | ||
| 2071 | pr_err("RCU: Never-onlined no-CBs CPU %d has CB %p\n", | ||
| 2072 | cpu, rhp->func); | ||
| 2073 | WARN_ON_ONCE(1); | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | return !!rhp; | ||
| 2077 | } | ||
| 2078 | |||
| 2079 | /* | ||
| 2053 | * Enqueue the specified string of rcu_head structures onto the specified | 2080 | * Enqueue the specified string of rcu_head structures onto the specified |
| 2054 | * CPU's no-CBs lists. The CPU is specified by rdp, the head of the | 2081 | * CPU's no-CBs lists. The CPU is specified by rdp, the head of the |
| 2055 | * string by rhp, and the tail of the string by rhtp. The non-lazy/lazy | 2082 | * string by rhp, and the tail of the string by rhtp. The non-lazy/lazy |
| @@ -2642,6 +2669,12 @@ static bool init_nocb_callback_list(struct rcu_data *rdp) | |||
| 2642 | 2669 | ||
| 2643 | #else /* #ifdef CONFIG_RCU_NOCB_CPU */ | 2670 | #else /* #ifdef CONFIG_RCU_NOCB_CPU */ |
| 2644 | 2671 | ||
| 2672 | static bool rcu_nocb_cpu_needs_barrier(struct rcu_state *rsp, int cpu) | ||
| 2673 | { | ||
| 2674 | WARN_ON_ONCE(1); /* Should be dead code. */ | ||
| 2675 | return false; | ||
| 2676 | } | ||
| 2677 | |||
| 2645 | static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) | 2678 | static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) |
| 2646 | { | 2679 | { |
| 2647 | } | 2680 | } |
