Skip to content

Commit afd4e69

Browse files
Frederic Weisbeckerfbq
authored andcommitted
rcu/nocb: Re-arrange call_rcu() NOCB specific code
Currently the call_rcu() function interleaves NOCB and !NOCB enqueue code in a complicated way such that: * The bypass enqueue code may or may not have enqueued and may or may not have locked the ->nocb_lock. Everything that follows is in a Schrödinger locking state for the unwary reviewer's eyes. * The was_alldone is always set but only used in NOCB related code. * The NOCB wake up is distantly related to the locking hopefully performed by the bypass enqueue code that did not enqueue on the bypass list. Unconfuse the whole and gather NOCB and !NOCB specific enqueue code to their own functions. Signed-off-by: Frederic Weisbecker <frederic@kernel.org> Reviewed-by: Paul E. McKenney <paulmck@kernel.org> Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
1 parent b913c3f commit afd4e69

File tree

3 files changed

+39
-32
lines changed

3 files changed

+39
-32
lines changed

kernel/rcu/tree.c

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2597,12 +2597,26 @@ static int __init rcu_spawn_core_kthreads(void)
25972597
return 0;
25982598
}
25992599

2600+
static void rcutree_enqueue(struct rcu_data *rdp, struct rcu_head *head, rcu_callback_t func)
2601+
{
2602+
rcu_segcblist_enqueue(&rdp->cblist, head);
2603+
if (__is_kvfree_rcu_offset((unsigned long)func))
2604+
trace_rcu_kvfree_callback(rcu_state.name, head,
2605+
(unsigned long)func,
2606+
rcu_segcblist_n_cbs(&rdp->cblist));
2607+
else
2608+
trace_rcu_callback(rcu_state.name, head,
2609+
rcu_segcblist_n_cbs(&rdp->cblist));
2610+
trace_rcu_segcb_stats(&rdp->cblist, TPS("SegCBQueued"));
2611+
}
2612+
26002613
/*
26012614
* Handle any core-RCU processing required by a call_rcu() invocation.
26022615
*/
2603-
static void __call_rcu_core(struct rcu_data *rdp, struct rcu_head *head,
2604-
unsigned long flags)
2616+
static void call_rcu_core(struct rcu_data *rdp, struct rcu_head *head,
2617+
rcu_callback_t func, unsigned long flags)
26052618
{
2619+
rcutree_enqueue(rdp, head, func);
26062620
/*
26072621
* If called from an extended quiescent state, invoke the RCU
26082622
* core in order to force a re-evaluation of RCU's idleness.
@@ -2698,7 +2712,6 @@ __call_rcu_common(struct rcu_head *head, rcu_callback_t func, bool lazy_in)
26982712
unsigned long flags;
26992713
bool lazy;
27002714
struct rcu_data *rdp;
2701-
bool was_alldone;
27022715

27032716
/* Misaligned rcu_head! */
27042717
WARN_ON_ONCE((unsigned long)head & (sizeof(void *) - 1));
@@ -2735,28 +2748,11 @@ __call_rcu_common(struct rcu_head *head, rcu_callback_t func, bool lazy_in)
27352748
}
27362749

27372750
check_cb_ovld(rdp);
2738-
if (rcu_nocb_try_bypass(rdp, head, &was_alldone, flags, lazy)) {
2739-
local_irq_restore(flags);
2740-
return; // Enqueued onto ->nocb_bypass, so just leave.
2741-
}
2742-
// If no-CBs CPU gets here, rcu_nocb_try_bypass() acquired ->nocb_lock.
2743-
rcu_segcblist_enqueue(&rdp->cblist, head);
2744-
if (__is_kvfree_rcu_offset((unsigned long)func))
2745-
trace_rcu_kvfree_callback(rcu_state.name, head,
2746-
(unsigned long)func,
2747-
rcu_segcblist_n_cbs(&rdp->cblist));
2748-
else
2749-
trace_rcu_callback(rcu_state.name, head,
2750-
rcu_segcblist_n_cbs(&rdp->cblist));
2751-
2752-
trace_rcu_segcb_stats(&rdp->cblist, TPS("SegCBQueued"));
27532751

2754-
/* Go handle any RCU core processing required. */
2755-
if (unlikely(rcu_rdp_is_offloaded(rdp))) {
2756-
__call_rcu_nocb_wake(rdp, was_alldone, flags); /* unlocks */
2757-
} else {
2758-
__call_rcu_core(rdp, head, flags);
2759-
}
2752+
if (unlikely(rcu_rdp_is_offloaded(rdp)))
2753+
call_rcu_nocb(rdp, head, func, flags, lazy);
2754+
else
2755+
call_rcu_core(rdp, head, func, flags);
27602756
local_irq_restore(flags);
27612757
}
27622758

kernel/rcu/tree.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -467,11 +467,10 @@ static void rcu_init_one_nocb(struct rcu_node *rnp);
467467
static bool wake_nocb_gp(struct rcu_data *rdp, bool force);
468468
static bool rcu_nocb_flush_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
469469
unsigned long j, bool lazy);
470-
static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
471-
bool *was_alldone, unsigned long flags,
472-
bool lazy);
473-
static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_empty,
474-
unsigned long flags);
470+
static void call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *head,
471+
rcu_callback_t func, unsigned long flags, bool lazy);
472+
static void __maybe_unused __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_empty,
473+
unsigned long flags);
475474
static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp, int level);
476475
static bool do_nocb_deferred_wakeup(struct rcu_data *rdp);
477476
static void rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp);

kernel/rcu/tree_nocb.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,18 @@ static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_alldone,
622622
}
623623
}
624624

625+
static void call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *head,
626+
rcu_callback_t func, unsigned long flags, bool lazy)
627+
{
628+
bool was_alldone;
629+
630+
if (!rcu_nocb_try_bypass(rdp, head, &was_alldone, flags, lazy)) {
631+
/* Not enqueued on bypass but locked, do regular enqueue */
632+
rcutree_enqueue(rdp, head, func);
633+
__call_rcu_nocb_wake(rdp, was_alldone, flags); /* unlocks */
634+
}
635+
}
636+
625637
static int nocb_gp_toggle_rdp(struct rcu_data *rdp,
626638
bool *wake_state)
627639
{
@@ -1764,10 +1776,10 @@ static bool rcu_nocb_flush_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
17641776
return true;
17651777
}
17661778

1767-
static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
1768-
bool *was_alldone, unsigned long flags, bool lazy)
1779+
static void call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *head,
1780+
rcu_callback_t func, unsigned long flags, bool lazy)
17691781
{
1770-
return false;
1782+
WARN_ON_ONCE(1); /* Should be dead code! */
17711783
}
17721784

17731785
static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_empty,

0 commit comments

Comments
 (0)