aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/rcutree.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/rcutree.c')
-rw-r--r--kernel/rcutree.c36
1 files changed, 35 insertions, 1 deletions
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
index 44a8fda9be86..6bb5d562253f 100644
--- a/kernel/rcutree.c
+++ b/kernel/rcutree.c
@@ -2294,13 +2294,41 @@ static void _rcu_barrier(struct rcu_state *rsp)
2294 unsigned long flags; 2294 unsigned long flags;
2295 struct rcu_data *rdp; 2295 struct rcu_data *rdp;
2296 struct rcu_data rd; 2296 struct rcu_data rd;
2297 unsigned long snap = ACCESS_ONCE(rsp->n_barrier_done);
2298 unsigned long snap_done;
2297 2299
2298 init_rcu_head_on_stack(&rd.barrier_head); 2300 init_rcu_head_on_stack(&rd.barrier_head);
2299 2301
2300 /* Take mutex to serialize concurrent rcu_barrier() requests. */ 2302 /* Take mutex to serialize concurrent rcu_barrier() requests. */
2301 mutex_lock(&rsp->barrier_mutex); 2303 mutex_lock(&rsp->barrier_mutex);
2302 2304
2303 smp_mb(); /* Prevent any prior operations from leaking in. */ 2305 /*
2306 * Ensure that all prior references, including to ->n_barrier_done,
2307 * are ordered before the _rcu_barrier() machinery.
2308 */
2309 smp_mb(); /* See above block comment. */
2310
2311 /*
2312 * Recheck ->n_barrier_done to see if others did our work for us.
2313 * This means checking ->n_barrier_done for an even-to-odd-to-even
2314 * transition. The "if" expression below therefore rounds the old
2315 * value up to the next even number and adds two before comparing.
2316 */
2317 snap_done = ACCESS_ONCE(rsp->n_barrier_done);
2318 if (ULONG_CMP_GE(snap_done, ((snap + 1) & ~0x1) + 2)) {
2319 smp_mb(); /* caller's subsequent code after above check. */
2320 mutex_unlock(&rsp->barrier_mutex);
2321 return;
2322 }
2323
2324 /*
2325 * Increment ->n_barrier_done to avoid duplicate work. Use
2326 * ACCESS_ONCE() to prevent the compiler from speculating
2327 * the increment to precede the early-exit check.
2328 */
2329 ACCESS_ONCE(rsp->n_barrier_done)++;
2330 WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 1);
2331 smp_mb(); /* Order ->n_barrier_done increment with below mechanism. */
2304 2332
2305 /* 2333 /*
2306 * Initialize the count to one rather than to zero in order to 2334 * Initialize the count to one rather than to zero in order to
@@ -2371,6 +2399,12 @@ static void _rcu_barrier(struct rcu_state *rsp)
2371 if (atomic_dec_and_test(&rsp->barrier_cpu_count)) 2399 if (atomic_dec_and_test(&rsp->barrier_cpu_count))
2372 complete(&rsp->barrier_completion); 2400 complete(&rsp->barrier_completion);
2373 2401
2402 /* Increment ->n_barrier_done to prevent duplicate work. */
2403 smp_mb(); /* Keep increment after above mechanism. */
2404 ACCESS_ONCE(rsp->n_barrier_done)++;
2405 WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 0);
2406 smp_mb(); /* Keep increment before caller's subsequent code. */
2407
2374 /* Wait for all rcu_barrier_callback() callbacks to be invoked. */ 2408 /* Wait for all rcu_barrier_callback() callbacks to be invoked. */
2375 wait_for_completion(&rsp->barrier_completion); 2409 wait_for_completion(&rsp->barrier_completion);
2376 2410