Contention reduction for v4.18
cover Contention reduction for v4.18
Hello!
This series reduces lock contention on the root rcu_node structure, and is also the first precursor to TBD changes to consolidate the three RCU flavors (RCU-bh, RCU-preempt, and RCU-sched) into one.
1. Improve non-root rcu_cbs_completed() accuracy, thus reducing the
need to acquire the root rcu_node structure's ->lock. This also eliminates the need to reassign callbacks to an earlier grace period, which enables introduction of funnel locking in a later commit, which further reduces contention.
2. Make rcu_start_future_gp()'s grace-period check more precise,
eliminating one need for forward-progress failsafe checks that acquire the root rcu_node structure's ->lock.
3. Create (and make use of) accessors for the ->need_future_gp[]
array to enable easy changes in size.
4. Make rcu_gp_kthread() check for early-boot activity, which was
another situation needing failsafe checks.
5. Make rcu_gp_cleanup() more accurately predict need for new GP.
This eliminates the need for both failsafe checks and extra grace-period kthread wakeups.
6. Avoid losing ->need_future_gp[] values due to GP start/end races
by expanding this array from two elements to four.
7. Make rcu_future_needs_gp() check all ->need_future_gps[] elements,
again to eliminate a need for both failsafe checks and extra grace-period kthread wakeups.
8. Convert ->need_future_gp[] array to boolean, given that there
is no longer a need to count the number of requests for a future grace period.
9. Make rcu_migrate_callbacks wake GP kthread when needed, which
again eliminates a need for failsafe checks.
10. Avoid __call_rcu_core() root rcu_node ->lock acquisition, which
was one of the failsafe checks that many of the above patches were making safe to remove.
11. Switch __rcu_process_callbacks() to rcu_accelerate_cbs(), which
was one of the failsafe checks that many of the above patches were making safe to remove. (Yes, this one also acquired the root rcu_node structure's ->lock, and was in fact the lock acquisition that was showing up in Nick Piggin's traces.)
12. Put ->completed into an unsigned long instead of an int. (The
"int" was harmless because only the low-order bits were used, but it was still an accident waiting to happen.)
13. Clear requests other than RCU_GP_FLAG_INIT at grace-period end.
This prevents premature quiescent-state forcing that might otherwise occur due to requests posted when the grace period was already almost done.
14. Inline rcu_start_gp_advanced() into rcu_start_future_gp().
This brings RCU down to only one function to start a grace period, in happen contrast to the need to choose correctly between three of them before this patch series.
15. Make rcu_start_future_gp() caller select grace period to avoid
duplicate grace-period selection. (We are going to like this grace period so much that we selected it twice!)
16. Add funnel locking to rcu_start_this_gp(), the point being to
reduce lock contention, especially on large systems.
17. Make rcu_start_this_gp() check for out-of-range requests.
If this check triggers, that indicates a bug in a caller of rcu_start_this_gp() or that the ->need_future_gp[] array needs to be even bigger, most likely the former. More importantly, it avoids one possible cause of otherwise silent grace-period hangs.
18. The rcu_gp_cleanup() function does not need cpu_needs_another_gp()
because funnel locking summarizes the need for future grace periods in the root rcu_node structure's ->lock, which rcu_gp_cleanup() already holds for other reasons.
19. Simplify and inline cpu_needs_another_gp(), which used to be a key
part of the no-longer-required forward-progress failsafe checks.
20. Drop early GP request check from rcu_gp_kthread(). Yes, it
was added above in order avoid grace-period hangs, but at this point in the series is no longer needed. All in the name of bisectability.
21. Update list of rcu_future_grace_period() trace events to reflect
strings added above.
Thanx, Paul
------------------------------------------------------------------------
include/trace/events/rcu.h | 13 - kernel/rcu/rcu_segcblist.c | 18 - kernel/rcu/rcu_segcblist.h | 2 kernel/rcu/tree.c | 406 ++++++++++++++++----------------------------- kernel/rcu/tree.h | 24 ++ kernel/rcu/tree_plugin.h | 28 --- 6 files changed, 182 insertions(+), 309 deletions(-)
PATCH 6/21 rcu: Avoid losing ->need_future_gp[] values due to GP start/end races
The rcu_cbs_completed() function provides the value of ->completed at which new callbacks can safely be invoked. This is recorded in two-element ->need_future_gp[] arrays in the rcu_node structure, and the elements of these arrays corresponding to the just-completed grace period are zeroed at the end of that grace period. However, the rcu_cbs_completed() function can return the current ->completed value plus either one or two, so it is possible for the corresponding ->need_future_gp[] entry to be cleared just after it was set, thus losing a request for a future grace period.
This commit avoids this race by expanding ->need_future_gp[] to four elements.
--- kernel/rcu/tree.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
kernel/rcu/tree.h +1 -2
@@ -150,8 +150,7 @@ struct rcu_node {struct swait_queue_head nocb_gp_wq[2 ];/* Place for rcu_nocb_kthread() to wait GP. */ #endif /* #ifdef CONFIG_RCU_NOCB_CPU */ -int need_future_gp[2 ]; -/* Counts of upcoming no-CB GP requests. */ +int need_future_gp[4 ];/* Counts of upcoming no-CB GP requests. */ raw_spinlock_t fqslock____cacheline_internodealigned_in_smp ;spinlock_t exp_lock____cacheline_internodealigned_in_smp ;
Signature · 1 line
2.5.2
PATCH 10/21 rcu: Avoid __call_rcu_core() root rcu_node ->lock acquisition
In reply to Paul E. McKenneyWhen __call_rcu_core() notices excessive numbers of callbacks pending on the current CPU, we know that at least one of them is not yet classified, namely the one that was just now queued. Therefore, it is not necessary to invoke rcu_start_gp() and thus not necessary to acquire the root rcu_node structure's ->lock. This commit therefore replaces the rcu_start_gp() with rcu_accelerate_cbs(), thus replacing an acquisition of the root rcu_node structure's ->lock with that of this CPU's leaf rcu_node structure.
This decreases contention on the root rcu_node structure's ->lock.
--- kernel/rcu/tree.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
kernel/rcu/tree.c +4 -4
@@ -2988,11 +2988,11 @@ static void __call_rcu_core(struct rcu_state *rsp, struct rcu_data *rdp,/* Start a new grace period if one not already started. */ if (!rcu_gp_in_progress (rsp)) { -struct rcu_node *rnp_root =rcu_get_root (rsp); +struct rcu_node *rnp = rdp->mynode; -raw_spin_lock_rcu_node (rnp_root); - needwake =rcu_start_gp (rsp); -raw_spin_unlock_rcu_node (rnp_root); +raw_spin_lock_rcu_node (rnp); + needwake =rcu_accelerate_cbs (rsp, rnp, rdp); +raw_spin_unlock_rcu_node (rnp);if (needwake)rcu_gp_kthread_wake (rsp); }else {
Signature · 1 line
2.5.2
PATCH 4/21 rcu: Make rcu_gp_kthread() check for early-boot activity
In reply to Paul E. McKenneyThe rcu_gp_kthread() function immediately sleeps waiting to be notified of the need for a new grace period, which currently works because there are a number of code sequences that will provide the needed wakeup later. However, some of these code sequences need to acquire the root rcu_node structure's ->lock, and contention on that lock has started manifesting. This commit therefore makes rcu_gp_kthread() check for early-boot activity when it starts up, omitting the initial sleep in that case.
--- kernel/rcu/tree.c | 6 ++++++ 1 file changed, 6 insertions(+)
kernel/rcu/tree.c +6 -0
@@ -2192,6 +2192,12 @@ static int __noreturn rcu_gp_kthread(void *arg)struct rcu_state *rsp = arg;struct rcu_node *rnp =rcu_get_root (rsp); +/* Check for early-boot work. */ +raw_spin_lock_irq_rcu_node (rnp); +if (need_any_future_gp (rnp)) +WRITE_ONCE (rsp->gp_flags,RCU_GP_FLAG_INIT ); +raw_spin_unlock_irq_rcu_node (rnp); +rcu_bind_gp_kthread ();for (;;) {
Signature · 1 line
2.5.2
PATCH 15/21 rcu: Make rcu_start_future_gp() caller select grace period
In reply to Paul E. McKenneyThe rcu_accelerate_cbs() function selects a grace-period target, which it uses to have rcu_segcblist_accelerate() assign numbers to recently queued callbacks. Then it invokes rcu_start_future_gp(), which selects a grace-period target again, which is a bit pointless. This commit therefore changes rcu_start_future_gp() to take the grace-period target as a parameter, thus avoiding double selection. This commit also changes the name of rcu_start_future_gp() to rcu_start_this_gp() to reflect this change in functionality, and also makes a similar change to the name of trace_rcu_future_gp().
--- kernel/rcu/tree.c | 53 ++++++++++++++++++++---------------------------- kernel/rcu/tree_plugin.h | 9 ++++---- 2 files changed, 27 insertions(+), 35 deletions(-)
kernel/rcu/tree.c +22 -31
@@ -1659,12 +1659,9 @@ static unsigned long rcu_cbs_completed(struct rcu_state *rsp,return rnp->completed +2 ; } -/* -* Trace-event helper function for rcu_start_future_gp() and -* rcu_nocb_wait_gp(). -*/ -static void trace_rcu_future_gp (struct rcu_node *rnp,struct rcu_data *rdp, -unsigned long c,const char *s) +/* Trace-event wrapper function for trace_rcu_future_grace_period. */ +static void trace_rcu_this_gp (struct rcu_node *rnp,struct rcu_data *rdp, +unsigned long c,const char *s) { trace_rcu_future_grace_period(rdp->rsp->name, rnp->gpnum, rnp->completed, c, rnp->level,
@@ -1672,33 +1669,27 @@ static void trace_rcu_future_gp(struct rcu_node *rnp, struct rcu_data *rdp, }/* -* Start some future grace period, as needed to handle newly arrived +* Start the specified grace period, as needed to handle newly arrived * callbacks. The required future grace periods are recorded in each -* rcu_node structure's ->need_future_gp field. Returns true if there +* rcu_node structure's ->need_future_gp[] field. Returns true if there * is reason to awaken the grace-period kthread. * * The caller must hold the specified rcu_node structure's ->lock, which * is why the caller is responsible for waking the grace-period kthread. */ -static bool __maybe_unused -rcu_start_future_gp (struct rcu_node *rnp,struct rcu_data *rdp, -unsigned long *c_out) +static bool rcu_start_this_gp (struct rcu_node *rnp,struct rcu_data *rdp, +unsigned long c) { -unsigned long c;bool ret = false;struct rcu_state *rsp = rdp->rsp;struct rcu_node *rnp_root =rcu_get_root (rsp);raw_lockdep_assert_held_rcu_node (rnp); -/* -* Pick up grace-period number for new callbacks. If this -* grace period is already marked as needed, return to the caller. -*/ - c =rcu_cbs_completed (rsp, rnp); -trace_rcu_future_gp (rnp, rdp, c,TPS ("Startleaf" )); +/* If the specified GP is already known needed, return to caller. */ +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" ));if (need_future_gp_element (rnp, c)) { -trace_rcu_future_gp (rnp, rdp, c,TPS ("Prestartleaf" )); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); goto out; }
@@ -1710,7 +1701,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,*/ if (rnp->gpnum != rnp->completed) {need_future_gp_element (rnp, c) = true; -trace_rcu_future_gp (rnp, rdp, c,TPS ("Startedleaf" )); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); goto out; }
@@ -1736,7 +1727,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* recorded, trace and leave. */ if (need_future_gp_element (rnp_root, c)) { -trace_rcu_future_gp (rnp, rdp, c,TPS ("Prestartedroot" )); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartedroot" )); goto unlock_out; }
@@ -1745,9 +1736,9 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,/* If a grace period is not already in progress, start one. */ if (rnp_root->gpnum != rnp_root->completed) { -trace_rcu_future_gp (rnp, rdp, c,TPS ("Startedleafroot" )); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleafroot" )); }else { -trace_rcu_future_gp (rnp, rdp, c,TPS ("Startedroot" )); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedroot" ));if (!rsp->gp_kthread) goto unlock_out;/* No grace-period kthread yet! */ WRITE_ONCE (rsp->gp_flags, rsp->gp_flags |RCU_GP_FLAG_INIT );
@@ -1759,8 +1750,6 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,if (rnp != rnp_root)raw_spin_unlock_rcu_node (rnp_root); out: -if (c_out !=NULL ) - *c_out = c;return ret; }
@@ -1776,8 +1765,8 @@ static bool rcu_future_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp)need_future_gp_element (rnp, c) = false; needmore =need_any_future_gp (rnp); -trace_rcu_future_gp (rnp, rdp, c, - needmore ?TPS ("CleanupMore" ) :TPS ("Cleanup" )); +trace_rcu_this_gp (rnp, rdp, c, + needmore ?TPS ("CleanupMore" ) :TPS ("Cleanup" ));return needmore; }
@@ -1812,6 +1801,7 @@ static void rcu_gp_kthread_wake(struct rcu_state *rsp)static bool rcu_accelerate_cbs (struct rcu_state *rsp,struct rcu_node *rnp,struct rcu_data *rdp) { +unsigned long c;bool ret = false;raw_lockdep_assert_held_rcu_node (rnp);
@@ -1830,8 +1820,9 @@ static bool rcu_accelerate_cbs(struct rcu_state *rsp, struct rcu_node *rnp,* accelerating callback invocation to an earlier grace-period * number. */ -if (rcu_segcblist_accelerate (&rdp->cblist,rcu_cbs_completed (rsp, rnp))) - ret =rcu_start_future_gp (rnp, rdp,NULL ); + c =rcu_cbs_completed (rsp, rnp); +if (rcu_segcblist_accelerate (&rdp->cblist, c)) + ret =rcu_start_this_gp (rnp, rdp, c);/* Trace depending on how much we were able to accelerate. */ if (rcu_segcblist_restempty (&rdp->cblist,RCU_WAIT_TAIL ))
@@ -2174,8 +2165,8 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)/* Check for GP requests since above loop. */ rdp =this_cpu_ptr (rsp->rda);if (need_any_future_gp (rnp)) { -trace_rcu_future_gp (rnp, rdp, rsp->completed -1 , -TPS ("CleanupMore" )); +trace_rcu_this_gp (rnp, rdp, rsp->completed -1 , +TPS ("CleanupMore" )); needgp = true; }/* Advance CBs to reduce false positives below. */
kernel/rcu/tree_plugin.h +5 -4
@@ -2035,7 +2035,8 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp)struct rcu_node *rnp = rdp->mynode;raw_spin_lock_irqsave_rcu_node (rnp, flags); - needwake =rcu_start_future_gp (rnp, rdp, &c); + c =rcu_cbs_completed (rdp->rsp, rnp); + needwake =rcu_start_this_gp (rnp, rdp, c);raw_spin_unlock_irqrestore_rcu_node (rnp, flags);if (needwake)rcu_gp_kthread_wake (rdp->rsp);
@@ -2044,7 +2045,7 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp)* Wait for the grace period. Do so interruptibly to avoid messing * up the load average. */ -trace_rcu_future_gp (rnp, rdp, c,TPS ("StartWait" )); +trace_rcu_this_gp (rnp, rdp, c,TPS ("StartWait" ));for (;;) { swait_event_interruptible( rnp->nocb_gp_wq[c & 0x1],
@@ -2052,9 +2053,9 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp)if (likely (d))break ;WARN_ON (signal_pending (current)); -trace_rcu_future_gp (rnp, rdp, c,TPS ("ResumeWait" )); +trace_rcu_this_gp (rnp, rdp, c,TPS ("ResumeWait" )); } -trace_rcu_future_gp (rnp, rdp, c,TPS ("EndWait" )); +trace_rcu_this_gp (rnp, rdp, c,TPS ("EndWait" ));smp_mb ();/* Ensure that CB invocation happens after GP end. */ }
Signature · 1 line
2.5.2
PATCH 21/21 rcu: Update list of rcu_future_grace_period() trace events
In reply to Paul E. McKenneyReworking grace-period initiation and funnel locking added new rcu_future_grace_period() trace events, so this commit updates the rcu_future_grace_period() trace event's header comment accordingly.
--- include/trace/events/rcu.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
include/trace/events/rcu.h +7 -6
@@ -84,20 +84,21 @@ TRACE_EVENT(rcu_grace_period, );/* -* Tracepoint for future grace-period events, including those for no-callbacks -* CPUs. The caller should pull the data from the rcu_node structure, -* other than rcuname, which comes from the rcu_state structure, and event, -* which is one of the following: +* Tracepoint for future grace-period events. The caller should pull +* the data from the rcu_node structure, other than rcuname, which comes +* from the rcu_state structure, and event, which is one of the following: * -* "Startleaf": Request a nocb grace period based on leaf-node data. +* "Startleaf": Request a grace period based on leaf-node data. +* "Prestarted": Someone beat us to the request * "Startedleaf": Leaf-node start proved sufficient. * "Startedleafroot": Leaf-node start proved sufficient after checking root. * "Startedroot": Requested a nocb grace period based on root-node data. +* "NoGPkthread": The RCU grace-period kthread has not yet started. * "StartWait": Start waiting for the requested grace period. * "ResumeWait": Resume waiting after signal. * "EndWait": Complete wait. * "Cleanup": Clean up rcu_node structure after previous GP. -* "CleanupMore": Clean up, and another no-CB GP is needed. +* "CleanupMore": Clean up, and another GP is needed. */ TRACE_EVENT(rcu_future_grace_period,
Signature · 1 line
2.5.2
PATCH 16/21 rcu: Add funnel locking to rcu_start_this_gp()
In reply to Paul E. McKenneyThe rcu_start_this_gp() function had a simple form of funnel locking that used only the leaves and root of the rcu_node tree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking.
This variant of funnel locking is unusual in the following ways:
1. The leaf-level rcu_node structure's ->lock is held throughout.
Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree.
2. Funnel locking can be started at the root, which is convenient
for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves.
3. If an rcu_node structure other than the initial one believes
that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress.
4. It is possible that the RCU grace-period kthread has not yet
started, and this case must be handled appropriately.
However, the general approach of using a tree to control lock contention is still in place.
--- kernel/rcu/tree.c | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-)
kernel/rcu/tree.c +35 -57
@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, {bool ret = false;struct rcu_state *rsp = rdp->rsp; -struct rcu_node *rnp_root =rcu_get_root (rsp); - -raw_lockdep_assert_held_rcu_node (rnp); - -/* If the specified GP is already known needed, return to caller. */ -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (need_future_gp_element (rnp, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); - goto out; - } +struct rcu_node *rnp_root;/* -* If this rcu_node structure believes that a grace period is in -* progress, then we must wait for the one following, which is in -* "c". Because our request will be noticed at the end of the -* current grace period, we don't need to explicitly start one. +* Use funnel locking to either acquire the root rcu_node +* structure's lock or bail out if the need for this grace period +* has already been recorded -- or has already started. If there +* is already a grace period in progress in a non-leaf node, no +* recording is needed because the end of the grace period will +* scan the leaf rcu_node structures. Note that rnp->lock must +* not be released. */ -if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c) = true; -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); - goto out; +raw_lockdep_assert_held_rcu_node (rnp); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); +for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) { +if (rnp_root != rnp) +raw_spin_lock_rcu_node (rnp_root); +if (need_future_gp_element (rnp_root, c) || +ULONG_CMP_GE (rnp_root->gpnum, c) || + (rnp != rnp_root && + rnp_root->gpnum != rnp_root->completed)) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Prestarted" )); + goto unlock_out; + } +need_future_gp_element (rnp_root, c) = true; +if (rnp_root != rnp && rnp_root->parent !=NULL ) +raw_spin_unlock_rcu_node (rnp_root); +if (!rnp_root->parent) +break ;/* At root, and perhaps also leaf. */ } -/* -* There might be no grace period in progress. If we don't already -* hold it, acquire the root rcu_node structure's lock in order to -* start one (if needed). -*/ -if (rnp != rnp_root) -raw_spin_lock_rcu_node (rnp_root); - -/* -* Get a new grace-period number. If there really is no grace -* period in progress, it will be smaller than the one we obtained -* earlier. Adjust callbacks as needed. -*/ - c =rcu_cbs_completed (rsp, rnp_root); -if (!rcu_is_nocb_cpu (rdp->cpu)) - (void )rcu_segcblist_accelerate (&rdp->cblist, c); - -/* -* If the needed for the required grace period is already -* recorded, trace and leave. -*/ -if (need_future_gp_element (rnp_root, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartedroot" )); +/* If GP already in progress, just leave, otherwise start one. */ +if (rnp_root->gpnum != rnp_root->completed) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Startedleafroot" )); goto unlock_out; } - -/* Record the need for the future grace period. */ -need_future_gp_element (rnp_root, c) = true; - -/* If a grace period is not already in progress, start one. */ -if (rnp_root->gpnum != rnp_root->completed) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleafroot" )); - }else { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedroot" )); -if (!rsp->gp_kthread) - goto unlock_out;/* No grace-period kthread yet! */ -WRITE_ONCE (rsp->gp_flags, rsp->gp_flags |RCU_GP_FLAG_INIT ); -trace_rcu_grace_period (rsp->name,READ_ONCE (rsp->gpnum), -TPS ("newreq" )); - ret = true;/* Caller must wake GP kthread. */ +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Startedroot" )); +WRITE_ONCE (rsp->gp_flags, rsp->gp_flags |RCU_GP_FLAG_INIT ); +if (!rsp->gp_kthread) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("NoGPkthread" )); + goto unlock_out; } +trace_rcu_grace_period (rsp->name,READ_ONCE (rsp->gpnum),TPS ("newreq" )); + ret = true;/* Caller must wake GP kthread. */ unlock_out:if (rnp != rnp_root)raw_spin_unlock_rcu_node (rnp_root); -out:return ret; }
Signature · 1 line
2.5.2
PATCH 20/21 rcu: Drop early GP request check from rcu_gp_kthread()
In reply to Paul E. McKenneyNow that grace-period requests use funnel locking and now that they set ->gp_flags to RCU_GP_FLAG_INIT even when the RCU grace-period kthread has not yet started, rcu_gp_kthread() no longer needs to check need_any_future_gp() at startup time. This commit therefore removes this check.
--- kernel/rcu/tree.c | 6 ------ 1 file changed, 6 deletions(-)
kernel/rcu/tree.c +0 -6
@@ -2135,12 +2135,6 @@ static int __noreturn rcu_gp_kthread(void *arg)struct rcu_state *rsp = arg;struct rcu_node *rnp =rcu_get_root (rsp); -/* Check for early-boot work. */ -raw_spin_lock_irq_rcu_node (rnp); -if (need_any_future_gp (rnp)) -WRITE_ONCE (rsp->gp_flags,RCU_GP_FLAG_INIT ); -raw_spin_unlock_irq_rcu_node (rnp); -rcu_bind_gp_kthread ();for (;;) {
Signature · 1 line
2.5.2
PATCH 19/21 rcu: Simplify and inline cpu_needs_another_gp()
In reply to Paul E. McKenneyNow that RCU no longer relies on failsafe checks, cpu_needs_another_gp() can be greatly simplified. This simplification eliminates the last call to rcu_future_needs_gp() and to rcu_segcblist_future_gp_needed(), both of which which can then be eliminated. And then, because cpu_needs_another_gp() is called only from __rcu_pending(), it can be inlined and eliminated.
This commit carries out the simplification, inlining, and elimination called out above.
--- kernel/rcu/rcu_segcblist.c | 18 ------------------ kernel/rcu/rcu_segcblist.h | 2 -- kernel/rcu/tree.c | 40 +++------------------------------------- 3 files changed, 3 insertions(+), 57 deletions(-)
kernel/rcu/rcu_segcblist.c +0 -18
@@ -404,24 +404,6 @@ bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq) }/* -* Scan the specified rcu_segcblist structure for callbacks that need -* a grace period later than the one specified by "seq". We don't look -* at the RCU_DONE_TAIL or RCU_NEXT_TAIL segments because they don't -* have a grace-period sequence number. -*/ -bool rcu_segcblist_future_gp_needed (struct rcu_segcblist *rsclp, -unsigned long seq) -{ -int i; - -for (i =RCU_WAIT_TAIL ; i <RCU_NEXT_TAIL ; i++) -if (rsclp->tails[i -1 ] != rsclp->tails[i] && -ULONG_CMP_LT (seq, rsclp->gp_seq[i])) -return true; -return false; -} - -/* * Merge the source rcu_segcblist structure into the destination * rcu_segcblist structure, then initialize the source. Any pending * callbacks from the source get to start over. It is best to
kernel/rcu/rcu_segcblist.h +0 -2
@@ -134,7 +134,5 @@ void rcu_segcblist_insert_pend_cbs(struct rcu_segcblist *rsclp, struct rcu_cblist *rclp);void rcu_segcblist_advance (struct rcu_segcblist *rsclp,unsigned long seq);bool rcu_segcblist_accelerate (struct rcu_segcblist *rsclp,unsigned long seq); -bool rcu_segcblist_future_gp_needed (struct rcu_segcblist *rsclp, -unsigned long seq);void rcu_segcblist_merge (struct rcu_segcblist *dst_rsclp,struct rcu_segcblist *src_rsclp);
kernel/rcu/tree.c +3 -37
@@ -709,42 +709,6 @@ static struct rcu_node *rcu_get_root(struct rcu_state *rsp) }/* -* Is there any need for future grace periods? -* Interrupts must be disabled. If the caller does not hold the root -* rnp_node structure's ->lock, the results are advisory only. -*/ -static int rcu_future_needs_gp (struct rcu_state *rsp) -{ -struct rcu_node *rnp =rcu_get_root (rsp); - -lockdep_assert_irqs_disabled (); -return need_any_future_gp (rnp); -} - -/* -* Does the current CPU require a not-yet-started grace period? -* The caller must have disabled interrupts to prevent races with -* normal callback registry. -*/ -static bool -cpu_needs_another_gp (struct rcu_state *rsp,struct rcu_data *rdp) -{ -lockdep_assert_irqs_disabled (); -if (rcu_gp_in_progress (rsp)) -return false;/* No, a grace period is already in progress. */ -if (rcu_future_needs_gp (rsp)) -return true;/* Yes, a no-CBs CPU needs one. */ -if (!rcu_segcblist_is_enabled (&rdp->cblist)) -return false;/* No, this is a no-CBs (or offline) CPU. */ -if (!rcu_segcblist_restempty (&rdp->cblist,RCU_NEXT_READY_TAIL )) -return true;/* Yes, CPU has newly registered callbacks. */ -if (rcu_segcblist_future_gp_needed (&rdp->cblist, -READ_ONCE (rsp->completed))) -return true;/* Yes, CBs for future grace period. */ -return false;/* No grace period needed. */ -} - -/* * Enter an RCU extended quiescent state, which can be either the * idle loop or adaptive-tickless usermode execution. *
@@ -3298,7 +3262,9 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp)return 1 ;/* Has RCU gone idle with this CPU needing another grace period? */ -if (cpu_needs_another_gp (rsp, rdp)) +if (!rcu_gp_in_progress (rsp) && +rcu_segcblist_is_enabled (&rdp->cblist) && + !rcu_segcblist_restempty (&rdp->cblist,RCU_NEXT_READY_TAIL ))return 1 ;/* Has another RCU grace period completed? */
Signature · 1 line
2.5.2
PATCH 13/21 rcu: Clear request other than RCU_GP_FLAG_INIT at GP end
In reply to Paul E. McKenneyOnce the grace period has ended, any RCU_GP_FLAG_FQS requests are
irrelevant: The grace period has ended, so there is no longer any point in forcing quiescent states in order to try to make it end sooner. This commit therefore causes rcu_gp_cleanup() to clear any bits other than RCU_GP_FLAG_INIT from ->gp_flags at the end of the grace period.
--- kernel/rcu/tree.c | 1 + 1 file changed, 1 insertion(+)
kernel/rcu/tree.c +1 -0
@@ -2181,6 +2181,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)READ_ONCE (rsp->gpnum), TPS("newreq")); } +WRITE_ONCE (rsp->gp_flags, rsp->gp_flags &RCU_GP_FLAG_INIT );raw_spin_unlock_irq_rcu_node (rnp); }
Signature · 1 line
2.5.2
PATCH 17/21 rcu: Make rcu_start_this_gp() check for out-of-range requests
In reply to Paul E. McKenneyIf rcu_start_this_gp() is invoked with a requested grace period more than three in the future, then either the ->need_future_gp[] array needs to be bigger or the caller needs to be repaired. This commit therefore adds a WARN_ON_ONCE() checking for this condition.
--- kernel/rcu/tree.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/rcu/tree.c +2 -0
@@ -1698,6 +1698,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp,for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) {if (rnp_root != rnp)raw_spin_lock_rcu_node (rnp_root); +WARN_ON_ONCE (ULONG_CMP_LT (rnp_root->gpnum + +need_future_gp_mask (), c)); if (need_future_gp_element(rnp_root, c) || ULONG_CMP_GE(rnp_root->gpnum, c) || (rnp != rnp_root &&
Signature · 1 line
2.5.2
PATCH 12/21 rcu: Cleanup, don't put ->completed into an int
In reply to Paul E. McKenneyIt is true that currently only the low-order two bits are used, so there should be no problem given modern machines and compilers, but good hygiene and maintainability dictates use of an unsigned long instead of an int. This commit therefore makes this change.
--- kernel/rcu/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/rcu/tree.c +1 -1
@@ -1765,7 +1765,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,*/ static bool rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) { -int c = rnp->completed; +unsigned long c = rnp->completed;bool needmore;struct rcu_data *rdp =this_cpu_ptr (rsp->rda);
Signature · 1 line
2.5.2
PATCH 1/21 rcu: Improve non-root rcu_cbs_completed() accuracy
In reply to Paul E. McKenneyWhen rcu_cbs_completed() is invoked on a non-root rcu_node structure, it unconditionally assumes that two grace periods must complete before the callbacks at hand can be invoked. This is overly conservative because if that non-root rcu_node structure believes that no grace period is in progress, and if the corresponding rcu_state structure's ->gpnum field has not yet been incremented, then these callbacks may safely be invoked after only one grace period has completed.
This change is required to permit grace-period start requests to use funnel locking, which is in turn permitted to reduce root rcu_node ->lock contention, which has been observed by Nick Piggin. Furthermore, such contention will likely be increased by the merging of RCU-bh, RCU-preempt, and RCU-sched, so it makes sense to take steps to decrease it.
This commit therefore improves the accuracy of rcu_cbs_completed() when invoked on a non-root rcu_node structure as described above.
--- kernel/rcu/tree.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
kernel/rcu/tree.c +15 -0
@@ -1642,6 +1642,21 @@ static unsigned long rcu_cbs_completed(struct rcu_state *rsp,return rnp->completed +1 ;/* +* If the current rcu_node structure believes that RCU is +* idle, and if the rcu_state structure does not yet reflect +* the start of a new grace period, then the next grace period +* will suffice. The memory barrier is needed to accurately +* sample the rsp->gpnum, and pairs with the second lock +* acquisition in rcu_gp_init(), which is augmented with +* smp_mb__after_unlock_lock() for this purpose. +*/ +if (rnp->gpnum == rnp->completed) { +smp_mb ();/* See above block comment. */ +if (READ_ONCE (rsp->gpnum) == rnp->completed) +return rnp->completed +1 ; + } + +/* * Otherwise, wait for a possible partial grace period and * then the subsequent full grace period. */
Signature · 1 line
2.5.2
PATCH 18/21 rcu: The rcu_gp_cleanup() function does not need cpu_needs_another_gp()
In reply to Paul E. McKenneyAll of the cpu_needs_another_gp() function's checks (except for newly arrived callbacks) have been subsumed into the rcu_gp_cleanup() function's scan of the rcu_node tree. This commit therefore drops the call to cpu_needs_another_gp(). The check for newly arrived callbacks is supplied by rcu_accelerate_cbs(). Any needed advancing (as in the earlier rcu_advance_cbs() call) will be supplied when the corresponding CPU becomes aware of the end of the now-completed grace period.
--- kernel/rcu/tree.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
kernel/rcu/tree.c +2 -4
@@ -2150,11 +2150,9 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) needgp = true; }/* Advance CBs to reduce false positives below. */ - needgp =rcu_advance_cbs (rsp, rnp, rdp) || needgp; -if (needgp ||cpu_needs_another_gp (rsp, rdp)) { +if (!rcu_accelerate_cbs (rsp, rnp, rdp) && needgp) {WRITE_ONCE (rsp->gp_flags,RCU_GP_FLAG_INIT ); -trace_rcu_grace_period (rsp->name, -READ_ONCE (rsp->gpnum), +trace_rcu_grace_period (rsp->name,READ_ONCE (rsp->gpnum),TPS ("newreq" )); }WRITE_ONCE (rsp->gp_flags, rsp->gp_flags &RCU_GP_FLAG_INIT );
Signature · 1 line
2.5.2
PATCH 2/21 rcu: Make rcu_start_future_gp()'s grace-period check more precise
In reply to Paul E. McKenneyThe rcu_start_future_gp() function uses a sloppy check for a grace period being in progress, which works today because there are a number of code sequences that resolve the resulting races. However, some of these race-resolution code sequences must acquire the root rcu_node structure's ->lock, and contention on that lock has started manifesting. This commit therefore makes rcu_start_future_gp() check more precise, eliminating the sloppy lockless check of the rcu_state structure's ->gpnum and ->completed fields. The effect is that rcu_start_future_gp() will sometimes unnecessarily attempt to start a new grace period, but this overhead will be reduced later using funnel locking.
--- kernel/rcu/tree.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-)
kernel/rcu/tree.c +5 -13
@@ -1705,20 +1705,12 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp, }/* -* If either this rcu_node structure or the root rcu_node structure -* believe that a grace period is in progress, then we must wait -* for the one following, which is in "c". Because our request -* will be noticed at the end of the current grace period, we don't -* need to explicitly start one. We only do the lockless check -* of rnp_root's fields if the current rcu_node structure thinks -* there is no grace period in flight, and because we hold rnp->lock, -* the only possible change is when rnp_root's two fields are -* equal, in which case rnp_root->gpnum might be concurrently -* incremented. But that is OK, as it will just result in our -* doing some extra useless work. +* If this rcu_node structure believes that a grace period is in +* progress, then we must wait for the one following, which is in +* "c". Because our request will be noticed at the end of the +* current grace period, we don't need to explicitly start one. */ -if (rnp->gpnum != rnp->completed || -READ_ONCE (rnp_root->gpnum) !=READ_ONCE (rnp_root->completed)) { +if (rnp->gpnum != rnp->completed) { rnp->need_future_gp[c &0x1 ]++;trace_rcu_future_gp (rnp, rdp, c,TPS ("Startedleaf" )); goto out;
Signature · 1 line
2.5.2
PATCH 11/21 rcu: Switch __rcu_process_callbacks() to rcu_accelerate_cbs()
In reply to Paul E. McKenneyThe __rcu_process_callbacks() function currently checks to see if the current CPU needs a grace period and also if there is any other reason to kick off a new grace period. This is one of the fail-safe checks that has been rendered unnecessary by the changes that increase the accuracy of rcu_gp_cleanup()'s estimate as to whether another grace period is required. Because this particular fail-safe involved acquiring the root rcu_node structure's ->lock, which has seen excessive contention in real life, this fail-safe needs to go.
However, one check must remain, namely the check for newly arrived RCU callbacks that have not yet been associated with a grace period. One might hope that the checks in __note_gp_changes(), which is invoked indirectly from rcu_check_quiescent_state(), would suffice, but this function won't be invoked at all if RCU is idle. It is therefore necessary to replace the fail-safe checks with a simpler check for newly arrived callbacks during an RCU idle period, which is exactly what this commit does. This change removes the final call to rcu_start_gp(), so this function is removed as well.
Note that lockless use of cpu_needs_another_gp() is racy, but that these races are harmless in this case. If RCU really is idle, the values will not change, so the return value from cpu_needs_another_gp() will be correct. If RCU is not idle, the resulting redundant call to rcu_accelerate_cbs() will be harmless, and might even have the benefit of reducing grace-period latency a bit.
This commit also moves interrupt disabling into the "if" statement to improve real-time response a bit.
--- kernel/rcu/tree.c | 53 +++++++++++++++-------------------------------------- 1 file changed, 15 insertions(+), 38 deletions(-)
kernel/rcu/tree.c +15 -38
@@ -2335,34 +2335,6 @@ rcu_start_gp_advanced(struct rcu_state *rsp, struct rcu_node *rnp, }/* -* Similar to rcu_start_gp_advanced(), but also advance the calling CPU's -* callbacks. Note that rcu_start_gp_advanced() cannot do this because it -* is invoked indirectly from rcu_advance_cbs(), which would result in -* endless recursion -- or would do so if it wasn't for the self-deadlock -* that is encountered beforehand. -* -* Returns true if the grace-period kthread needs to be awakened. -*/ -static bool rcu_start_gp (struct rcu_state *rsp) -{ -struct rcu_data *rdp =this_cpu_ptr (rsp->rda); -struct rcu_node *rnp =rcu_get_root (rsp); -bool ret = false; - -/* -* If there is no grace period in progress right now, any -* callbacks we have up to this point will be satisfied by the -* next grace period. Also, advancing the callbacks reduces the -* probability of false positives from cpu_needs_another_gp() -* resulting in pointless grace periods. So, advance callbacks -* then start the grace period! -*/ - ret =rcu_advance_cbs (rsp, rnp, rdp) || ret; - ret =rcu_start_gp_advanced (rsp, rnp, rdp) || ret; -return ret; -} - -/* * Report a full set of quiescent states to the specified rcu_state data * structure. Invoke rcu_gp_kthread_wake() to awaken the grace-period * kthread if another grace period is required. Whether we wake
@@ -2889,22 +2861,27 @@ __rcu_process_callbacks(struct rcu_state *rsp)unsigned long flags;bool needwake;struct rcu_data *rdp =raw_cpu_ptr (rsp->rda); +struct rcu_node *rnp;WARN_ON_ONCE (!rdp->beenonline);/* Update RCU state based on any recent quiescent states. */ rcu_check_quiescent_state (rsp, rdp); -/* Does this CPU require a not-yet-started grace period? */ -local_irq_save (flags); -if (cpu_needs_another_gp (rsp, rdp)) { -raw_spin_lock_rcu_node (rcu_get_root (rsp));/* irqs disabled. */ - needwake =rcu_start_gp (rsp); -raw_spin_unlock_irqrestore_rcu_node (rcu_get_root (rsp), flags); -if (needwake) -rcu_gp_kthread_wake (rsp); - }else { -local_irq_restore (flags); +/* No grace period and unregistered callbacks? */ +if (!rcu_gp_in_progress (rsp) && +rcu_segcblist_is_enabled (&rdp->cblist)) { +local_irq_save (flags); +if (rcu_segcblist_restempty (&rdp->cblist,RCU_NEXT_READY_TAIL )) { +local_irq_restore (flags); + }else { + rnp = rdp->mynode; +raw_spin_lock_rcu_node (rnp);/* irqs disabled. */ + needwake =rcu_accelerate_cbs (rsp, rnp, rdp); +raw_spin_unlock_irqrestore_rcu_node (rnp, flags); +if (needwake) +rcu_gp_kthread_wake (rsp); + } }/* If there are callbacks ready, invoke them. */
Signature · 1 line
2.5.2
PATCH 14/21 rcu: Inline rcu_start_gp_advanced() into rcu_start_future_gp()
In reply to Paul E. McKenneyThe rcu_start_gp_advanced() is invoked only from rcu_start_future_gp() and much of its code is redundant when invoked from that context. This commit therefore inlines rcu_start_gp_advanced() into rcu_start_future_gp(), then removes rcu_start_gp_advanced().
--- kernel/rcu/tree.c | 56 ++++++++++++------------------------------------------- 1 file changed, 12 insertions(+), 44 deletions(-)
kernel/rcu/tree.c +12 -44
@@ -524,8 +524,6 @@ module_param(rcu_kick_kthreads, bool, 0644);static ulong jiffies_till_sched_qs =HZ /10 ;module_param (jiffies_till_sched_qs, ulong,0444 ); -static bool rcu_start_gp_advanced (struct rcu_state *rsp,struct rcu_node *rnp, -struct rcu_data *rdp);static void force_qs_rnp (struct rcu_state *rsp,int (*f)(struct rcu_data *rsp));static void force_quiescent_state (struct rcu_state *rsp);static int rcu_pending (void );
@@ -1679,7 +1677,8 @@ static void trace_rcu_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* rcu_node structure's ->need_future_gp field. Returns true if there * is reason to awaken the grace-period kthread. * -* The caller must hold the specified rcu_node structure's ->lock. +* The caller must hold the specified rcu_node structure's ->lock, which +* is why the caller is responsible for waking the grace-period kthread. */ static bool __maybe_unused rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,
@@ -1687,7 +1686,8 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp, {unsigned long c;bool ret = false; -struct rcu_node *rnp_root =rcu_get_root (rdp->rsp); +struct rcu_state *rsp = rdp->rsp; +struct rcu_node *rnp_root =rcu_get_root (rsp);raw_lockdep_assert_held_rcu_node (rnp);
@@ -1695,7 +1695,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* Pick up grace-period number for new callbacks. If this * grace period is already marked as needed, return to the caller. */ - c =rcu_cbs_completed (rdp->rsp, rnp); + c =rcu_cbs_completed (rsp, rnp);trace_rcu_future_gp (rnp, rdp, c,TPS ("Startleaf" ));if (need_future_gp_element (rnp, c)) {trace_rcu_future_gp (rnp, rdp, c,TPS ("Prestartleaf" ));
@@ -1727,7 +1727,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* period in progress, it will be smaller than the one we obtained * earlier. Adjust callbacks as needed. */ - c =rcu_cbs_completed (rdp->rsp, rnp_root); + c =rcu_cbs_completed (rsp, rnp_root);if (!rcu_is_nocb_cpu (rdp->cpu)) (void )rcu_segcblist_accelerate (&rdp->cblist, c);
@@ -1748,7 +1748,12 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,trace_rcu_future_gp (rnp, rdp, c,TPS ("Startedleafroot" )); }else {trace_rcu_future_gp (rnp, rdp, c,TPS ("Startedroot" )); - ret =rcu_start_gp_advanced (rdp->rsp, rnp_root, rdp); +if (!rsp->gp_kthread) + goto unlock_out;/* No grace-period kthread yet! */ +WRITE_ONCE (rsp->gp_flags, rsp->gp_flags |RCU_GP_FLAG_INIT ); +trace_rcu_grace_period (rsp->name,READ_ONCE (rsp->gpnum), +TPS ("newreq" )); + ret = true;/* Caller must wake GP kthread. */ } unlock_out:if (rnp != rnp_root)
@@ -2299,43 +2304,6 @@ static int __noreturn rcu_gp_kthread(void *arg) }/* -* Start a new RCU grace period if warranted, re-initializing the hierarchy -* in preparation for detecting the next grace period. The caller must hold -* the root node's ->lock and hard irqs must be disabled. -* -* Note that it is legal for a dying CPU (which is marked as offline) to -* invoke this function. This can happen when the dying CPU reports its -* quiescent state. -* -* Returns true if the grace-period kthread must be awakened. -*/ -static bool -rcu_start_gp_advanced (struct rcu_state *rsp,struct rcu_node *rnp, -struct rcu_data *rdp) -{ -raw_lockdep_assert_held_rcu_node (rnp); -if (!rsp->gp_kthread || !cpu_needs_another_gp (rsp, rdp)) { -/* -* Either we have not yet spawned the grace-period -* task, this CPU does not need another grace period, -* or a grace period is already in progress. -* Either way, don't start a new grace period. -*/ -return false; - } -WRITE_ONCE (rsp->gp_flags,RCU_GP_FLAG_INIT ); -trace_rcu_grace_period (rsp->name,READ_ONCE (rsp->gpnum), -TPS ("newreq" )); - -/* -* We can't do wakeups while holding the rnp->lock, as that -* could cause possible deadlocks with the rq->lock. Defer -* the wakeup to our caller. -*/ -return true; -} - -/* * Report a full set of quiescent states to the specified rcu_state data * structure. Invoke rcu_gp_kthread_wake() to awaken the grace-period * kthread if another grace period is required. Whether we wake
Signature · 1 line
2.5.2
PATCH 5/21 rcu: Make rcu_gp_cleanup() more accurately predict need for new GP
In reply to Paul E. McKenneyCurrently, rcu_gp_cleanup() scans the rcu_node tree in order to reset state to reflect the end of the grace period. It also checks to see whether a new grace period is needed, but in a number of cases, rather than directly cause the new grace period to be immediately started, it instead leaves the grace-period-needed state where various fail-safes can find it. This works fine, but results in higher contention on the root rcu_node structure's ->lock, which is undesirable, and contention on that lock has recently become noticeable.
This commit therefore makes rcu_gp_cleanup() immediately start a new grace period if there is any need for one.
It is quite possible that it will later be necessary to throttle the grace-period rate, but that can be dealt with when and if.
--- kernel/rcu/tree.c | 16 ++++++++++------ kernel/rcu/tree.h | 1 - kernel/rcu/tree_plugin.h | 17 ----------------- 3 files changed, 10 insertions(+), 24 deletions(-)
kernel/rcu/tree.c +10 -6
@@ -1763,14 +1763,14 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* Clean up any old requests for the just-ended grace period. Also return * whether any additional grace periods have been requested. */ -static int rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) +static bool rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) {int c = rnp->completed; -int needmore; +bool needmore;struct rcu_data *rdp =this_cpu_ptr (rsp->rda);need_future_gp_element (rnp, c) =0 ; - needmore =need_future_gp_element (rnp, c +1 ); + needmore =need_any_future_gp (rnp);trace_rcu_future_gp (rnp, rdp, c, needmore ?TPS ("CleanupMore" ) :TPS ("Cleanup" ));return needmore;
@@ -2113,7 +2113,6 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) {unsigned long gp_duration;bool needgp = false; -int nocb =0 ;struct rcu_data *rdp;struct rcu_node *rnp =rcu_get_root (rsp);struct swait_queue_head *sq;
@@ -2152,7 +2151,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)if (rnp == rdp->mynode) needgp =__note_gp_changes (rsp, rnp, rdp) || needgp;/* smp_mb() provided by prior unlock-lock pair. */ - nocb +=rcu_future_gp_cleanup (rsp, rnp); + needgp =rcu_future_gp_cleanup (rsp, rnp) || needgp; sq =rcu_nocb_gp_get (rnp);raw_spin_unlock_irq_rcu_node (rnp);rcu_nocb_gp_cleanup (sq);
@@ -2162,13 +2161,18 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) } rnp =rcu_get_root (rsp);raw_spin_lock_irq_rcu_node (rnp);/* Order GP before ->completed update. */ -rcu_nocb_gp_set (rnp, nocb);/* Declare grace period done. */ WRITE_ONCE (rsp->completed, rsp->gpnum);trace_rcu_grace_period (rsp->name, rsp->completed,TPS ("end" )); rsp->gp_state =RCU_GP_IDLE ; +/* Check for GP requests since above loop. */ rdp =this_cpu_ptr (rsp->rda); +if (need_any_future_gp (rnp)) { +trace_rcu_future_gp (rnp, rdp, rsp->completed -1 , +TPS ("CleanupMore" )); + needgp = true; + }/* Advance CBs to reduce false positives below. */ needgp =rcu_advance_cbs (rsp, rnp, rdp) || needgp;if (needgp ||cpu_needs_another_gp (rsp, rdp)) {
kernel/rcu/tree.h +0 -1
@@ -469,7 +469,6 @@ static void print_cpu_stall_info_end(void);static void zero_cpu_stall_ticks (struct rcu_data *rdp);static void increment_cpu_stall_ticks (void );static bool rcu_nocb_cpu_needs_barrier (struct rcu_state *rsp,int cpu); -static void rcu_nocb_gp_set (struct rcu_node *rnp,int nrq);static struct swait_queue_head *rcu_nocb_gp_get (struct rcu_node *rnp);static void rcu_nocb_gp_cleanup (struct swait_queue_head *sq);static void rcu_init_one_nocb (struct rcu_node *rnp);
kernel/rcu/tree_plugin.h +0 -17
@@ -1780,19 +1780,6 @@ static void rcu_nocb_gp_cleanup(struct swait_queue_head *sq)swake_up_all (sq); } -/* -* Set the root rcu_node structure's ->need_future_gp field -* based on the sum of those of all rcu_node structures. This does -* double-count the root rcu_node structure's requests, but this -* is necessary to handle the possibility of a rcu_nocb_kthread() -* having awakened during the time that the rcu_node structures -* were being updated for the end of the previous grace period. -*/ -static void rcu_nocb_gp_set (struct rcu_node *rnp,int nrq) -{ -need_future_gp_element (rnp, rnp->completed +1 ) += nrq; -} -static struct swait_queue_head *rcu_nocb_gp_get (struct rcu_node *rnp) {return &rnp->nocb_gp_wq[rnp->completed &0x1 ];
@@ -2495,10 +2482,6 @@ static void rcu_nocb_gp_cleanup(struct swait_queue_head *sq) { } -static void rcu_nocb_gp_set (struct rcu_node *rnp,int nrq) -{ -} -static struct swait_queue_head *rcu_nocb_gp_get (struct rcu_node *rnp) {return NULL ;
Signature · 1 line
2.5.2
PATCH 9/21 rcu: Make rcu_migrate_callbacks wake GP kthread when needed
In reply to Paul E. McKenneyThe rcu_migrate_callbacks() function invokes rcu_advance_cbs() twice, ignoring the return value. This is OK at pressent because of failsafe code that does the wakeup when needed. However, this failsafe code acquires the root rcu_node structure's lock frequently, while rcu_migrate_callbacks() does so only once per CPU-offline operation.
This commit therefore makes rcu_migrate_callbacks() wake up the RCU GP kthread when either call to rcu_advance_cbs() returns true, thus removing need for the failsafe code.
--- kernel/rcu/tree.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
kernel/rcu/tree.c +6 -2
@@ -3876,6 +3876,7 @@ static void rcu_migrate_callbacks(int cpu, struct rcu_state *rsp)struct rcu_data *my_rdp;struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu);struct rcu_node *rnp_root =rcu_get_root (rdp->rsp); +bool needwake;if (rcu_is_nocb_cpu (cpu) ||rcu_segcblist_empty (&rdp->cblist))return ;/* No callbacks to migrate. */
@@ -3887,12 +3888,15 @@ static void rcu_migrate_callbacks(int cpu, struct rcu_state *rsp)return ; }raw_spin_lock_rcu_node (rnp_root);/* irqs already disabled. */ -rcu_advance_cbs (rsp, rnp_root, rdp);/* Leverage recent GPs. */ -rcu_advance_cbs (rsp, rnp_root, my_rdp);/* Assign GP to pending CBs. */ +/* Leverage recent GPs and set GP for new callbacks. */ + needwake =rcu_advance_cbs (rsp, rnp_root, rdp) || +rcu_advance_cbs (rsp, rnp_root, my_rdp);rcu_segcblist_merge (&my_rdp->cblist, &rdp->cblist);WARN_ON_ONCE (rcu_segcblist_empty (&my_rdp->cblist) != !rcu_segcblist_n_cbs (&my_rdp->cblist));raw_spin_unlock_irqrestore_rcu_node (rnp_root, flags); +if (needwake) +rcu_gp_kthread_wake (rsp); WARN_ONCE(rcu_segcblist_n_cbs(&rdp->cblist) != 0 || !rcu_segcblist_empty(&rdp->cblist), "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, 1stCB=%p\n",
Signature · 1 line
2.5.2
PATCH 7/21 rcu: Make rcu_future_needs_gp() check all ->need_future_gps[] elements
In reply to Paul E. McKenneyCurrently, the rcu_future_needs_gp() function checks only the current element of the ->need_future_gps[] array, which might miss elements that were offset from the expected element, for example, due to races with the start or the end of a grace period. This commit therefore makes rcu_future_needs_gp() use the need_any_future_gp() macro to check all of the elements of this array.
--- kernel/rcu/tree.c | 2 +- kernel/rcu/tree.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-)
kernel/rcu/tree.c +1 -1
@@ -720,7 +720,7 @@ static int rcu_future_needs_gp(struct rcu_state *rsp)struct rcu_node *rnp =rcu_get_root (rsp);lockdep_assert_irqs_disabled (); -return READ_ONCE (need_future_gp_element (rnp, rnp->completed)); +return need_any_future_gp (rnp); }/*
kernel/rcu/tree.h +2 -1
@@ -169,7 +169,8 @@ struct rcu_node {bool __nonzero = false; \ \for (__i =0 ; __i <ARRAY_SIZE ((rnp)->need_future_gp); __i++) \ - __nonzero = __nonzero || (rnp)->need_future_gp[__i]; \ + __nonzero = __nonzero || \ +READ_ONCE ((rnp)->need_future_gp[__i]); \ __nonzero; \ })
Signature · 1 line
2.5.2
PATCH 8/21 rcu: Convert ->need_future_gp[] array to boolean
In reply to Paul E. McKenneyThere is no longer any need for ->need_future_gp[] to count the number of requests for future grace periods, so this commit converts the additions to assignments to "true" and reduces the size of each element to one byte. While we are in the area, fix an obsolete comment.
--- kernel/rcu/tree.c | 6 +++--- kernel/rcu/tree.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-)
kernel/rcu/tree.c +3 -3
@@ -1709,7 +1709,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* current grace period, we don't need to explicitly start one. */ if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c)++; +need_future_gp_element (rnp, c) = true;trace_rcu_future_gp (rnp, rdp, c,TPS ("Startedleaf" )); goto out; }
@@ -1741,7 +1741,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp, }/* Record the need for the future grace period. */ -need_future_gp_element (rnp_root, c)++; +need_future_gp_element (rnp_root, c) = true;/* If a grace period is not already in progress, start one. */ if (rnp_root->gpnum != rnp_root->completed) {
@@ -1769,7 +1769,7 @@ static bool rcu_future_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp)bool needmore;struct rcu_data *rdp =this_cpu_ptr (rsp->rda); -need_future_gp_element (rnp, c) =0 ; +need_future_gp_element (rnp, c) = false; needmore =need_any_future_gp (rnp);trace_rcu_future_gp (rnp, rdp, c, needmore ?TPS ("CleanupMore" ) :TPS ("Cleanup" ));
kernel/rcu/tree.h +1 -1
@@ -150,7 +150,7 @@ struct rcu_node {struct swait_queue_head nocb_gp_wq[2 ];/* Place for rcu_nocb_kthread() to wait GP. */ #endif /* #ifdef CONFIG_RCU_NOCB_CPU */ -int need_future_gp[4 ];/* Counts of upcoming no-CB GP requests. */ +u8 need_future_gp[4 ];/* Counts of upcoming GP requests. */ raw_spinlock_t fqslock____cacheline_internodealigned_in_smp ;spinlock_t exp_lock____cacheline_internodealigned_in_smp ;
Signature · 1 line
2.5.2
PATCH 3/21 rcu: Add accessor macros for the ->need_future_gp[] array
In reply to Paul E. McKenneyAccessors for the ->need_future_gp[] array are currently open-coded, which makes them difficult to change. To improve maintainability, this commit adds need_future_gp_mask() to compute the indexing mask from the array size, need_future_gp_element() to access the element corresponding to the specified grace-period number, and need_any_future_gp() to determine if any future grace period is needed. This commit also applies need_future_gp_element() to existing open-coded single-element accesses.
--- kernel/rcu/tree.c | 16 +++++++--------- kernel/rcu/tree.h | 15 +++++++++++++++ kernel/rcu/tree_plugin.h | 2 +- 3 files changed, 23 insertions(+), 10 deletions(-)
kernel/rcu/tree.c +7 -9
@@ -718,11 +718,9 @@ static struct rcu_node *rcu_get_root(struct rcu_state *rsp)static int rcu_future_needs_gp (struct rcu_state *rsp) {struct rcu_node *rnp =rcu_get_root (rsp); -int idx = (READ_ONCE (rnp->completed) +1 ) &0x1 ; -int *fp = &rnp->need_future_gp[idx];lockdep_assert_irqs_disabled (); -return READ_ONCE (*fp); +return READ_ONCE (need_future_gp_element (rnp, rnp->completed)); }/*
@@ -1699,7 +1697,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,*/ c =rcu_cbs_completed (rdp->rsp, rnp);trace_rcu_future_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (rnp->need_future_gp[c &0x1 ]) { +if (need_future_gp_element (rnp, c)) {trace_rcu_future_gp (rnp, rdp, c,TPS ("Prestartleaf" )); goto out; }
@@ -1711,7 +1709,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* current grace period, we don't need to explicitly start one. */ if (rnp->gpnum != rnp->completed) { - rnp->need_future_gp[c &0x1 ]++; +need_future_gp_element (rnp, c)++;trace_rcu_future_gp (rnp, rdp, c,TPS ("Startedleaf" )); goto out; }
@@ -1737,13 +1735,13 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* If the needed for the required grace period is already * recorded, trace and leave. */ -if (rnp_root->need_future_gp[c &0x1 ]) { +if (need_future_gp_element (rnp_root, c)) {trace_rcu_future_gp (rnp, rdp, c,TPS ("Prestartedroot" )); goto unlock_out; }/* Record the need for the future grace period. */ - rnp_root->need_future_gp[c &0x1 ]++; +need_future_gp_element (rnp_root, c)++;/* If a grace period is not already in progress, start one. */ if (rnp_root->gpnum != rnp_root->completed) {
@@ -1771,8 +1769,8 @@ static int rcu_future_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp)int needmore;struct rcu_data *rdp =this_cpu_ptr (rsp->rda); - rnp->need_future_gp[c &0x1 ] =0 ; - needmore = rnp->need_future_gp[(c +1 ) &0x1 ]; +need_future_gp_element (rnp, c) =0 ; + needmore =need_future_gp_element (rnp, c +1 );trace_rcu_future_gp (rnp, rdp, c, needmore ?TPS ("CleanupMore" ) :TPS ("Cleanup" ));return needmore;
kernel/rcu/tree.h +15 -0
@@ -159,6 +159,21 @@ struct rcu_node {wait_queue_head_t exp_wq[4 ]; }____cacheline_internodealigned_in_smp ; +/* Accessors for ->need_future_gp[] array. */ +#define need_future_gp_mask () \ + (ARRAY_SIZE(((struct rcu_node *)NULL)->need_future_gp) - 1) +#define need_future_gp_element (rnp, c) \ + ((rnp)->need_future_gp[(c) & need_future_gp_mask()]) +#define need_any_future_gp (rnp) \ +({ \ + int __i; \ + bool __nonzero = false; \ + \ + for (__i = 0; __i < ARRAY_SIZE((rnp)->need_future_gp); __i++) \ + __nonzero = __nonzero || (rnp)->need_future_gp[__i]; \ + __nonzero; \ +}) +/* * Bitmasks in an rcu_node cover the interval [grplo, grphi] of CPU IDs, and * are indexed relative to this interval rather than the global CPU ID space.
kernel/rcu/tree_plugin.h +1 -1
@@ -1790,7 +1790,7 @@ static void rcu_nocb_gp_cleanup(struct swait_queue_head *sq)*/ static void rcu_nocb_gp_set (struct rcu_node *rnp,int nrq) { - rnp->need_future_gp[(rnp->completed +1 ) &0x1 ] += nrq; +need_future_gp_element (rnp, rnp->completed +1 ) += nrq; }static struct swait_queue_head *rcu_nocb_gp_get (struct rcu_node *rnp)
Signature · 1 line
2.5.2
Re: [tip/core/rcu, 05/21] rcu: Make rcu_gp_cleanup() more accurately predict need for new GP
In reply to Paul E. McKenneyHi Paul,
Quoted 68 lines · Paul E. McKenney
Currently,
rcu_gp_cleanup()scans thercu_nodetree in order to reset state to reflect the end of the grace period. It also checks to see whether a new grace period is needed, but in a number of cases, rather than directly cause the new grace period to be immediately started, it instead leaves the grace-period-needed state where various fail-safes can find it. This works fine, but results in higher contention on the rootrcu_nodestructure's ->lock, which is undesirable, and contention on that lock has recently become noticeable.This commit therefore makes
rcu_gp_cleanup()immediately start a new grace period if there is any need for one.It is quite possible that it will later be necessary to throttle the grace-period rate, but that can be dealt with when and if.
Reported-by Nicholas Piggin <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 16 ++++++++++------ kernel/rcu/tree.h | 1 - kernel/rcu/tree_plugin.h | 17 ----------------- 3 files changed, 10 insertions(+), 24 deletions(-)kernel/rcu/tree.c +4
-6@@ -1763,14 +1763,14 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* Clean up any old requests for the just-ended grace period. Also return * whether any additional grace periods have been requested. */ -static int rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) +static bool rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) {int c = rnp->completed; -int needmore; +bool needmore;struct rcu_data *rdp =this_cpu_ptr (rsp->rda);need_future_gp_element (rnp, c) =0 ; - needmore =need_future_gp_element (rnp, c +1 ); + needmore =need_any_future_gp (rnp);trace_rcu_future_gp (rnp, rdp, c, needmore ?TPS ("CleanupMore" ) :TPS ("Cleanup" ));return needmore;@@ -2113,7 +2113,6 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) {unsigned long gp_duration;bool needgp = false; -int nocb =0 ;struct rcu_data *rdp;struct rcu_node *rnp =rcu_get_root (rsp);struct swait_queue_head *sq;@@ -2152,7 +2151,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)if (rnp == rdp->mynode) needgp =__note_gp_changes (rsp, rnp, rdp) || needgp;/* smp_mb() provided by prior unlock-lock pair. */ - nocb +=rcu_future_gp_cleanup (rsp, rnp); + needgp =rcu_future_gp_cleanup (rsp, rnp) || needgp; sq =rcu_nocb_gp_get (rnp);raw_spin_unlock_irq_rcu_node (rnp);rcu_nocb_gp_cleanup (sq);@@ -2162,13 +2161,18 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) } rnp =rcu_get_root (rsp);raw_spin_lock_irq_rcu_node (rnp);/* Order GP before ->completed update. */ -rcu_nocb_gp_set (rnp, nocb);
kernel/rcu/tree.c static void rcu_gp_cleanup(struct rcu_state *rsp) · line 2174
Quoted 10 lines
kernel/rcu/tree.c +5
-0/* Declare grace period done. */ WRITE_ONCE (rsp->completed, rsp->gpnum);trace_rcu_grace_period (rsp->name, rsp->completed,TPS ("end" )); rsp->gp_state =RCU_GP_IDLE ; +/* Check for GP requests since above loop. */ rdp =this_cpu_ptr (rsp->rda); +if (need_any_future_gp (rnp)) { +trace_rcu_future_gp (rnp, rdp, rsp->completed -1 , +TPS ("CleanupMore" )); + needgp = true;
Patch makes sense to me.
I didn't get the "rsp->completed - 1" bit in the call to trace_rcu_future_gp. The grace period that just completed is in rsp->completed. The future one should be completed + 1. What is meaning of the third argument 'c' to the trace event?
Also in rcu_future_gp_cleanup, we call:
trace_rcu_future_gp(rnp, rdp, c,
needmore ? TPS("CleanupMore") : TPS("Cleanup"));For this case, in the final trace event record, rnp->completed and c will be the same, since c is set to rnp->completed before calling trace_rcu_future_gp. I was thinking they should be different, do you expect them to be the same?
thanks!
- Joel
Re: [tip/core/rcu, 05/21] rcu: Make rcu_gp_cleanup() more accurately predict need for new GP
Quoted 84 lines · Joel Fernandes
Hi Paul,
Quoted 68 lines · Paul E. McKenney
Currently,
rcu_gp_cleanup()scans thercu_nodetree in order to reset state to reflect the end of the grace period. It also checks to see whether a new grace period is needed, but in a number of cases, rather than directly cause the new grace period to be immediately started, it instead leaves the grace-period-needed state where various fail-safes can find it. This works fine, but results in higher contention on the rootrcu_nodestructure's ->lock, which is undesirable, and contention on that lock has recently become noticeable.This commit therefore makes
rcu_gp_cleanup()immediately start a new grace period if there is any need for one.It is quite possible that it will later be necessary to throttle the grace-period rate, but that can be dealt with when and if.
Reported-by Nicholas Piggin <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 16 ++++++++++------ kernel/rcu/tree.h | 1 - kernel/rcu/tree_plugin.h | 17 ----------------- 3 files changed, 10 insertions(+), 24 deletions(-)kernel/rcu/tree.c +4
-6@@ -1763,14 +1763,14 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* Clean up any old requests for the just-ended grace period. Also return * whether any additional grace periods have been requested. */ -static int rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) +static bool rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) {int c = rnp->completed; -int needmore; +bool needmore;struct rcu_data *rdp =this_cpu_ptr (rsp->rda);need_future_gp_element (rnp, c) =0 ; - needmore =need_future_gp_element (rnp, c +1 ); + needmore =need_any_future_gp (rnp);trace_rcu_future_gp (rnp, rdp, c, needmore ?TPS ("CleanupMore" ) :TPS ("Cleanup" ));return needmore;@@ -2113,7 +2113,6 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) {unsigned long gp_duration;bool needgp = false; -int nocb =0 ;struct rcu_data *rdp;struct rcu_node *rnp =rcu_get_root (rsp);struct swait_queue_head *sq;@@ -2152,7 +2151,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)if (rnp == rdp->mynode) needgp =__note_gp_changes (rsp, rnp, rdp) || needgp;/* smp_mb() provided by prior unlock-lock pair. */ - nocb +=rcu_future_gp_cleanup (rsp, rnp); + needgp =rcu_future_gp_cleanup (rsp, rnp) || needgp; sq =rcu_nocb_gp_get (rnp);raw_spin_unlock_irq_rcu_node (rnp);rcu_nocb_gp_cleanup (sq);@@ -2162,13 +2161,18 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) } rnp =rcu_get_root (rsp);raw_spin_lock_irq_rcu_node (rnp);/* Order GP before ->completed update. */ -rcu_nocb_gp_set (rnp, nocb);kernel/rcu/tree.c static void rcu_gp_cleanup(struct rcu_state *rsp) · line 2174
Quoted 10 lines
kernel/rcu/tree.c +5
-0/* Declare grace period done. */ WRITE_ONCE (rsp->completed, rsp->gpnum);trace_rcu_grace_period (rsp->name, rsp->completed,TPS ("end" )); rsp->gp_state =RCU_GP_IDLE ; +/* Check for GP requests since above loop. */ rdp =this_cpu_ptr (rsp->rda); +if (need_any_future_gp (rnp)) { +trace_rcu_future_gp (rnp, rdp, rsp->completed -1 , +TPS ("CleanupMore" )); + needgp = true;Patch makes sense to me.
Quoted 4 lines
I didn't get the "
rsp->completed- 1" bit in the call totrace_rcu_future_gp. The grace period that just completed is inrsp->completed. The future one should be completed + 1. What is meaning of the third argument 'c' to the trace event?
The thought was that the grace period must have been requested while rsp->completed was one less than it is now.
In the current code, it uses rnp->gp_seq_needed, which is instead the grace period that is being requested.
Quoted 7 lines
Also in
rcu_future_gp_cleanup, we call:trace_rcu_future_gp(rnp, rdp, c, needmore ? TPS("CleanupMore") : TPS("Cleanup"));For this case, in the final trace event record,
rnp->completedand c will be the same, since c is set tornp->completedbefore callingtrace_rcu_future_gp. I was thinking they should be different, do you expect them to be the same?
Hmmm... That does look a bit inconsistent. And it currently uses rnp->gp_seq instead of rnp->gp_seq_needed despite having the same "CleanupMore" name.
Looks like a review of the calls to trace_rcu_this_gp() is in order. Or did you have suggestions for name/gp assocations for this trace message type?
Thanx, Paul
Re: [tip/core/rcu, 05/21] rcu: Make rcu_gp_cleanup() more accurately predict need for new GP
Quoted 90 lines · Paul E. McKenney
Quoted 84 lines · Joel Fernandes
Hi Paul,
Quoted 68 lines · Paul E. McKenney
Currently,
rcu_gp_cleanup()scans thercu_nodetree in order to reset state to reflect the end of the grace period. It also checks to see whether a new grace period is needed, but in a number of cases, rather than directly cause the new grace period to be immediately started, it instead leaves the grace-period-needed state where various fail-safes can find it. This works fine, but results in higher contention on the rootrcu_nodestructure's ->lock, which is undesirable, and contention on that lock has recently become noticeable.This commit therefore makes
rcu_gp_cleanup()immediately start a new grace period if there is any need for one.It is quite possible that it will later be necessary to throttle the grace-period rate, but that can be dealt with when and if.
Reported-by Nicholas Piggin <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 16 ++++++++++------ kernel/rcu/tree.h | 1 - kernel/rcu/tree_plugin.h | 17 ----------------- 3 files changed, 10 insertions(+), 24 deletions(-)kernel/rcu/tree.c +4
-6@@ -1763,14 +1763,14 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* Clean up any old requests for the just-ended grace period. Also return * whether any additional grace periods have been requested. */ -static int rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) +static bool rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) {int c = rnp->completed; -int needmore; +bool needmore;struct rcu_data *rdp =this_cpu_ptr (rsp->rda);need_future_gp_element (rnp, c) =0 ; - needmore =need_future_gp_element (rnp, c +1 ); + needmore =need_any_future_gp (rnp);trace_rcu_future_gp (rnp, rdp, c, needmore ?TPS ("CleanupMore" ) :TPS ("Cleanup" ));return needmore;@@ -2113,7 +2113,6 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) {unsigned long gp_duration;bool needgp = false; -int nocb =0 ;struct rcu_data *rdp;struct rcu_node *rnp =rcu_get_root (rsp);struct swait_queue_head *sq;@@ -2152,7 +2151,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)if (rnp == rdp->mynode) needgp =__note_gp_changes (rsp, rnp, rdp) || needgp;/* smp_mb() provided by prior unlock-lock pair. */ - nocb +=rcu_future_gp_cleanup (rsp, rnp); + needgp =rcu_future_gp_cleanup (rsp, rnp) || needgp; sq =rcu_nocb_gp_get (rnp);raw_spin_unlock_irq_rcu_node (rnp);rcu_nocb_gp_cleanup (sq);@@ -2162,13 +2161,18 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) } rnp =rcu_get_root (rsp);raw_spin_lock_irq_rcu_node (rnp);/* Order GP before ->completed update. */ -rcu_nocb_gp_set (rnp, nocb);kernel/rcu/tree.c static void rcu_gp_cleanup(struct rcu_state *rsp) · line 2174
Quoted 10 lines
kernel/rcu/tree.c +5
-0/* Declare grace period done. */ WRITE_ONCE (rsp->completed, rsp->gpnum);trace_rcu_grace_period (rsp->name, rsp->completed,TPS ("end" )); rsp->gp_state =RCU_GP_IDLE ; +/* Check for GP requests since above loop. */ rdp =this_cpu_ptr (rsp->rda); +if (need_any_future_gp (rnp)) { +trace_rcu_future_gp (rnp, rdp, rsp->completed -1 , +TPS ("CleanupMore" )); + needgp = true;Patch makes sense to me.
Quoted 4 lines
I didn't get the "
rsp->completed- 1" bit in the call totrace_rcu_future_gp. The grace period that just completed is inrsp->completed. The future one should be completed + 1. What is meaning of the third argument 'c' to the trace event?
Quoted 5 lines
The thought was that the grace period must have been requested while
rsp->completedwas one less than it is now.In the current code, it uses
rnp->gp_seq_needed, which is instead the grace period that is being requested.
Oh ok, IIUC from the code, the 'c' parameter passed to trace_rcu_future_gp is the grace-period number in the future. Perhaps we should clarify this in the include/trace/events/rcu.h code what this parameter means. Probably 'future_gp' or something like that.
Quoted 11 lines
Quoted 7 lines
Also in
rcu_future_gp_cleanup, we call:trace_rcu_future_gp(rnp, rdp, c, needmore ? TPS("CleanupMore") : TPS("Cleanup"));For this case, in the final trace event record,
rnp->completedand c will be the same, since c is set tornp->completedbefore callingtrace_rcu_future_gp. I was thinking they should be different, do you expect them to be the same?Hmmm... That does look a bit inconsistent. And it currently uses
rnp->gp_seqinstead ofrnp->gp_seq_neededdespite having the same "CleanupMore" name.
Yes I was thinking in rcu_future_gp_cleanup, the call to trace_rcu_future_gp should be trace_rcu_future_gp(rnp, rdp, c + 1, needmore...);
This is because in rcu_future_gp_cleanup, c is set to rnp->completed. Just before this point rnp->completed was set to rsp->gpnum, which marks the end of the GP for the node. The next gp would be c + 1 right?
Quoted 1 line
Looks like a review of the calls to
trace_rcu_this_gp()is in order.
Yes, I'll do some tracing and see if something else doesn't make sense to me and let you know.
Quoted 2 lines
Or did you have suggestions for name/gp assocations for this trace message type?
I think the name for this one is fine but also that "CleanupMore" sounds like more clean up is needed. It could be improved to "CleanupNeedgp" or "CleanupAndStart" or something like that.
thanks!
- Joel
Re: [tip/core/rcu, 05/21] rcu: Make rcu_gp_cleanup() more accurately predict need for new GP
In reply to Paul E. McKenneyOn Thu, May 10, 2018 at 06:15:46AM -0700, Paul E. McKenney wrote:
[...]
Quoted 8 lines
Quoted 7 lines
Also in
rcu_future_gp_cleanup, we call:trace_rcu_future_gp(rnp, rdp, c, needmore ? TPS("CleanupMore") : TPS("Cleanup"));For this case, in the final trace event record,
rnp->completedand c will be the same, since c is set tornp->completedbefore callingtrace_rcu_future_gp. I was thinking they should be different, do you expect them to be the same?
Quoted 5 lines
Hmmm... That does look a bit inconsistent. And it currently uses
rnp->gp_seqinstead ofrnp->gp_seq_neededdespite having the same "CleanupMore" name.Looks like a review of the calls to
trace_rcu_this_gp()is in order.
I see you changed trace_rcu_future_gp to use trace_rcu_this_gp in 15/21.. I am not sure if the concern is still valid then since you seem to be correctly getting the future GP in those cases, except for the naming which I suggest be changed from 'c' to 'future_gp' just for clarity / self-documenting code.
thanks,
- Joel
Re: [tip/core/rcu, 05/21] rcu: Make rcu_gp_cleanup() more accurately predict need for new GP
In reply to Joel FernandesQuoted 126 lines · Joel Fernandes
Quoted 90 lines · Paul E. McKenney
Quoted 84 lines · Joel Fernandes
Hi Paul,
Quoted 68 lines · Paul E. McKenney
Currently,
rcu_gp_cleanup()scans thercu_nodetree in order to reset state to reflect the end of the grace period. It also checks to see whether a new grace period is needed, but in a number of cases, rather than directly cause the new grace period to be immediately started, it instead leaves the grace-period-needed state where various fail-safes can find it. This works fine, but results in higher contention on the rootrcu_nodestructure's ->lock, which is undesirable, and contention on that lock has recently become noticeable.This commit therefore makes
rcu_gp_cleanup()immediately start a new grace period if there is any need for one.It is quite possible that it will later be necessary to throttle the grace-period rate, but that can be dealt with when and if.
Reported-by Nicholas Piggin <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 16 ++++++++++------ kernel/rcu/tree.h | 1 - kernel/rcu/tree_plugin.h | 17 ----------------- 3 files changed, 10 insertions(+), 24 deletions(-)kernel/rcu/tree.c +4
-6@@ -1763,14 +1763,14 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,* Clean up any old requests for the just-ended grace period. Also return * whether any additional grace periods have been requested. */ -static int rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) +static bool rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) {int c = rnp->completed; -int needmore; +bool needmore;struct rcu_data *rdp =this_cpu_ptr (rsp->rda);need_future_gp_element (rnp, c) =0 ; - needmore =need_future_gp_element (rnp, c +1 ); + needmore =need_any_future_gp (rnp);trace_rcu_future_gp (rnp, rdp, c, needmore ?TPS ("CleanupMore" ) :TPS ("Cleanup" ));return needmore;@@ -2113,7 +2113,6 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) {unsigned long gp_duration;bool needgp = false; -int nocb =0 ;struct rcu_data *rdp;struct rcu_node *rnp =rcu_get_root (rsp);struct swait_queue_head *sq;@@ -2152,7 +2151,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)if (rnp == rdp->mynode) needgp =__note_gp_changes (rsp, rnp, rdp) || needgp;/* smp_mb() provided by prior unlock-lock pair. */ - nocb +=rcu_future_gp_cleanup (rsp, rnp); + needgp =rcu_future_gp_cleanup (rsp, rnp) || needgp; sq =rcu_nocb_gp_get (rnp);raw_spin_unlock_irq_rcu_node (rnp);rcu_nocb_gp_cleanup (sq);@@ -2162,13 +2161,18 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) } rnp =rcu_get_root (rsp);raw_spin_lock_irq_rcu_node (rnp);/* Order GP before ->completed update. */ -rcu_nocb_gp_set (rnp, nocb);kernel/rcu/tree.c static void rcu_gp_cleanup(struct rcu_state *rsp) · line 2174
Quoted 10 lines
kernel/rcu/tree.c +5
-0/* Declare grace period done. */ WRITE_ONCE (rsp->completed, rsp->gpnum);trace_rcu_grace_period (rsp->name, rsp->completed,TPS ("end" )); rsp->gp_state =RCU_GP_IDLE ; +/* Check for GP requests since above loop. */ rdp =this_cpu_ptr (rsp->rda); +if (need_any_future_gp (rnp)) { +trace_rcu_future_gp (rnp, rdp, rsp->completed -1 , +TPS ("CleanupMore" )); + needgp = true;Patch makes sense to me.
Quoted 4 lines
I didn't get the "
rsp->completed- 1" bit in the call totrace_rcu_future_gp. The grace period that just completed is inrsp->completed. The future one should be completed + 1. What is meaning of the third argument 'c' to the trace event?Quoted 5 lines
The thought was that the grace period must have been requested while
rsp->completedwas one less than it is now.In the current code, it uses
rnp->gp_seq_needed, which is instead the grace period that is being requested.Oh ok, IIUC from the code, the 'c' parameter passed to
trace_rcu_future_gpis the grace-period number in the future. Perhaps we should clarify this in theinclude/trace/events/rcu.hcode what this parameter means. Probably 'future_gp' or something like that.Quoted 11 lines
Quoted 7 lines
Also in
rcu_future_gp_cleanup, we call:trace_rcu_future_gp(rnp, rdp, c, needmore ? TPS("CleanupMore") : TPS("Cleanup"));For this case, in the final trace event record,
rnp->completedand c will be the same, since c is set tornp->completedbefore callingtrace_rcu_future_gp. I was thinking they should be different, do you expect them to be the same?Hmmm... That does look a bit inconsistent. And it currently uses
rnp->gp_seqinstead ofrnp->gp_seq_neededdespite having the same "CleanupMore" name.Yes I was thinking in
rcu_future_gp_cleanup, the call totrace_rcu_future_gpshould betrace_rcu_future_gp(rnp, rdp, c + 1, needmore...);This is because in
rcu_future_gp_cleanup, c is set tornp->completed. Just before this pointrnp->completedwas set torsp->gpnum, which marks the end of the GP for the node. The next gp would be c + 1 right?Quoted 1 line
Looks like a review of the calls to
trace_rcu_this_gp()is in order.Yes, I'll do some tracing and see if something else doesn't make sense to me and let you know.
Quoted 6 lines
Quoted 2 lines
Or did you have suggestions for name/gp assocations for this trace message type?
I think the name for this one is fine but also that "CleanupMore" sounds like more clean up is needed. It could be improved to "CleanupNeedgp" or "CleanupAndStart" or something like that.
Would you be willing to pick a name, check the grace-period numbers, and send a patch relative to rcu/dev?
Thanx, Paul
Re: [tip/core/rcu, 05/21] rcu: Make rcu_gp_cleanup() more accurately predict need for new GP
In reply to Joel FernandesQuoted 14 lines · Joel Fernandes
On Thu, May 10, 2018 at 06:15:46AM -0700, Paul E. McKenney wrote:
[...]Quoted 12 lines
Quoted 7 lines
Also in
rcu_future_gp_cleanup, we call:trace_rcu_future_gp(rnp, rdp, c, needmore ? TPS("CleanupMore") : TPS("Cleanup"));For this case, in the final trace event record,
rnp->completedand c will be the same, since c is set tornp->completedbefore callingtrace_rcu_future_gp. I was thinking they should be different, do you expect them to be the same?Hmmm... That does look a bit inconsistent. And it currently uses
rnp->gp_seqinstead ofrnp->gp_seq_neededdespite having the same "CleanupMore" name.
Quoted 6 lines
Quoted 1 line
Looks like a review of the calls to
trace_rcu_this_gp()is in order.I see you changed
trace_rcu_future_gpto usetrace_rcu_this_gpin 15/21.. I am not sure if the concern is still valid then since you seem to be correctly getting the future GP in those cases, except for the naming which I suggest be changed from 'c' to 'future_gp' just for clarity / self-documenting code.
Indeed, "c" for "->completed" is completely outdated. ;-)
Would you be willing to send a patch providing a better name?
Thanx, Paul
Re: [tip/core/rcu, 05/21] rcu: Make rcu_gp_cleanup() more accurately predict need for new GP
Quoted 17 lines · Paul E. McKenney
Quoted 16 lines · Joel Fernandes
On Thu, May 10, 2018 at 06:15:46AM -0700, Paul E. McKenney wrote:
[...]Quoted 13 lines
Quoted 7 lines
Also in
rcu_future_gp_cleanup, we call:trace_rcu_future_gp(rnp, rdp, c, needmore ? TPS("CleanupMore") : TPS("Cleanup"));For this case, in the final trace event record,
rnp->completedand c will be the same, since c is set tornp->completedbefore callingtrace_rcu_future_gp. I was thinking they should be different, do you expect them to be the same?Hmmm... That does look a bit inconsistent. And it currently uses
rnp->gp_seqinstead ofrnp->gp_seq_neededdespite having the same "CleanupMore" name.Looks like a review of the calls to
trace_rcu_this_gp()is in order.
Quoted 8 lines
Quoted 4 lines
I see you changed
trace_rcu_future_gpto usetrace_rcu_this_gpin 15/21.. I am not sure if the concern is still valid then since you seem to be correctly getting the future GP in those cases, except for the naming which I suggest be changed from 'c' to 'future_gp' just for clarity / self-documenting code.Indeed, "c" for "->completed" is completely outdated. ;-)
Would you be willing to send a patch providing a better name?
Yes for sure, I'll do it soon, and will also review the gp numbers.
thanks,
- Joel
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
In reply to Paul E. McKenneyQuoted 60 lines · Paul E. McKenney
The
rcu_start_this_gp()function had a simple form of funnel locking that used only the leaves and root of thercu_nodetree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking.This variant of funnel locking is unusual in the following ways:
1. The leaf-level
rcu_nodestructure's ->lock is held throughout.
Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree.2. Funnel locking can be started at the root, which is convenient
for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves.3. If an
rcu_nodestructure other than the initial one believes
that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress.4. It is possible that the RCU grace-period kthread has not yet
started, and this case must be handled appropriately.However, the general approach of using a tree to control lock contention is still in place.
Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-)kernel/rcu/tree.c +3
-14@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, {bool ret = false;struct rcu_state *rsp = rdp->rsp; -struct rcu_node *rnp_root =rcu_get_root (rsp); - -raw_lockdep_assert_held_rcu_node (rnp); - -/* If the specified GP is already known needed, return to caller. */ -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (need_future_gp_element (rnp, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); - goto out; - } +struct rcu_node *rnp_root;/* -* If this rcu_node structure believes that a grace period is in -* progress, then we must wait for the one following, which is in -* "c". Because our request will be noticed at the end of the -* current grace period, we don't need to explicitly start one. +* Use funnel locking to either acquire the root rcu_node +* structure's lock or bail out if the need for this grace period
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1705
Quoted 10 lines
kernel/rcu/tree.c +5
-4+* has already been recorded -- or has already started. If there +* is already a grace period in progress in a non-leaf node, no +* recording is needed because the end of the grace period will +* scan the leaf rcu_node structures. Note that rnp->lock must +* not be released. */ -if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c) = true; -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); - goto out;
Referring to the above negative diff as [1] (which I wanted to refer to later in this message..)
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1706
Quoted 11 lines
kernel/rcu/tree.c +11
-0+raw_lockdep_assert_held_rcu_node (rnp); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); +for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) { +if (rnp_root != rnp) +raw_spin_lock_rcu_node (rnp_root); +if (need_future_gp_element (rnp_root, c) || +ULONG_CMP_GE (rnp_root->gpnum, c) || + (rnp != rnp_root && + rnp_root->gpnum != rnp_root->completed)) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Prestarted" )); + goto unlock_out;
I was a bit confused about the implementation of the above for loop:
In the previous code (which I refer to in the negative diff [1]), we were checking the leaf, and if the leaf believed that RCU was not idle, then we were marking the need for the future GP and quitting this function. In the new code, it seems like even if the leaf believes RCU is not-idle, we still go all the way up the tree.
I think the big change is, in the above new for loop, we either bail of if a future GP need was already marked by an intermediate node, or we go marking up the whole tree about the need for one.
If a leaf believes RCU is not idle, can we not just mark the future GP need like before and return? It seems we would otherwise increase the lock contention since now we lock intermediate nodes and then finally even the root. Where as before we were not doing that if the leaf believed RCU was not idle.
I am sorry if I missed something obvious.
The other thing is we now don't have the 'Startedleaf' trace like we did before. I sent a patch to remove it, but I think the removal of that is somehow connected to what I was just talking about.. and I was thinking if we should really remove it. Should we add the case for checking leaves only back or is that a bad thing to do?
thanks,
- Joel
Quoted 8 lines
kernel/rcu/tree.c +6
-0+ } +need_future_gp_element (rnp_root, c) = true; +if (rnp_root != rnp && rnp_root->parent !=NULL ) +raw_spin_unlock_rcu_node (rnp_root); +if (!rnp_root->parent) +break ;/* At root, and perhaps also leaf. */ }
[...]
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
Quoted 99 lines · Joel Fernandes
Quoted 60 lines · Paul E. McKenney
The
rcu_start_this_gp()function had a simple form of funnel locking that used only the leaves and root of thercu_nodetree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking.This variant of funnel locking is unusual in the following ways:
1. The leaf-level
rcu_nodestructure's ->lock is held throughout.
Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree.2. Funnel locking can be started at the root, which is convenient
for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves.3. If an
rcu_nodestructure other than the initial one believes
that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress.4. It is possible that the RCU grace-period kthread has not yet
started, and this case must be handled appropriately.However, the general approach of using a tree to control lock contention is still in place.
Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-)kernel/rcu/tree.c +3
-14@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, {bool ret = false;struct rcu_state *rsp = rdp->rsp; -struct rcu_node *rnp_root =rcu_get_root (rsp); - -raw_lockdep_assert_held_rcu_node (rnp); - -/* If the specified GP is already known needed, return to caller. */ -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (need_future_gp_element (rnp, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); - goto out; - } +struct rcu_node *rnp_root;/* -* If this rcu_node structure believes that a grace period is in -* progress, then we must wait for the one following, which is in -* "c". Because our request will be noticed at the end of the -* current grace period, we don't need to explicitly start one. +* Use funnel locking to either acquire the root rcu_node +* structure's lock or bail out if the need for this grace period kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1705
Quoted 10 lines
kernel/rcu/tree.c +5
-4+* has already been recorded -- or has already started. If there +* is already a grace period in progress in a non-leaf node, no +* recording is needed because the end of the grace period will +* scan the leaf rcu_node structures. Note that rnp->lock must +* not be released. */ -if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c) = true; -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); - goto out;Referring to the above negative diff as [1] (which I wanted to refer to later in this message..)
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1706
Quoted 11 lines
kernel/rcu/tree.c +11
-0+raw_lockdep_assert_held_rcu_node (rnp); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); +for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) { +if (rnp_root != rnp) +raw_spin_lock_rcu_node (rnp_root); +if (need_future_gp_element (rnp_root, c) || +ULONG_CMP_GE (rnp_root->gpnum, c) || + (rnp != rnp_root && + rnp_root->gpnum != rnp_root->completed)) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Prestarted" )); + goto unlock_out;I was a bit confused about the implementation of the above for loop:
In the previous code (which I refer to in the negative diff [1]), we were checking the leaf, and if the leaf believed that RCU was not idle, then we were marking the need for the future GP and quitting this function. In the new code, it seems like even if the leaf believes RCU is not-idle, we still go all the way up the tree.
I think the big change is, in the above new for loop, we either bail of if a future GP need was already marked by an intermediate node, or we go marking up the whole tree about the need for one.
Quoted 7 lines
If a leaf believes RCU is not idle, can we not just mark the future GP need like before and return? It seems we would otherwise increase the lock contention since now we lock intermediate nodes and then finally even the root. Where as before we were not doing that if the leaf believed RCU was not idle.
I am sorry if I missed something obvious.
The trick is that we do the check before we have done the marking. So if we bailed, we would not have marked at all. If we are at an intermediate node and a grace period is in progress, we do bail.
You are right that this means that we (perhaps unnecessarily) acquire the lock of the parent rcu_node, which might or might not be the root. And on systems with default fanout with 1024 CPUs or fewer, yes, it will be the root, and yes, this is the common case. So might be well worth improving.
One way to implement the old mark-and-return approach as you suggest above would be as shown below (untested, probably doesn't build, and against current rcu/dev). What do you think?
Quoted 5 lines
The other thing is we now don't have the 'Startedleaf' trace like we did before. I sent a patch to remove it, but I think the removal of that is somehow connected to what I was just talking about.. and I was thinking if we should really remove it. Should we add the case for checking leaves only back or is that a bad thing to do?
Suppose I got hit by a bus and you were stuck with the job of debugging this. What traces would you want and where would they be? Keeping in mind that too-frequent traces have their problems as well.
(Yes, I will be trying very hard to avoid this scenario for as long as I can, but this might be a good way for you (and everyone else) to be thinking about this.)
Thanx, Paul
------------------------------------------------------------------------
kernel/rcu/tree.c +2 -0
@@ -1585,6 +1585,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, goto unlock_out; } rnp_root->gp_seq_needed = c; +if (rcu_seq_state (rcu_seq_current (&rnp_root->gp_seq))) + goto unlock_out;if (rnp_root != rnp && rnp_root->parent !=NULL )raw_spin_unlock_rcu_node (rnp_root);if (!rnp_root->parent)
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
Quoted 145 lines · Paul E. McKenney
Quoted 99 lines · Joel Fernandes
Quoted 60 lines · Paul E. McKenney
The
rcu_start_this_gp()function had a simple form of funnel locking that used only the leaves and root of thercu_nodetree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking.This variant of funnel locking is unusual in the following ways:
1. The leaf-level
rcu_nodestructure's ->lock is held throughout.
Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree.2. Funnel locking can be started at the root, which is convenient
for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves.3. If an
rcu_nodestructure other than the initial one believes
that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress.4. It is possible that the RCU grace-period kthread has not yet
started, and this case must be handled appropriately.However, the general approach of using a tree to control lock contention is still in place.
Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-)kernel/rcu/tree.c +3
-14@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, {bool ret = false;struct rcu_state *rsp = rdp->rsp; -struct rcu_node *rnp_root =rcu_get_root (rsp); - -raw_lockdep_assert_held_rcu_node (rnp); - -/* If the specified GP is already known needed, return to caller. */ -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (need_future_gp_element (rnp, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); - goto out; - } +struct rcu_node *rnp_root;/* -* If this rcu_node structure believes that a grace period is in -* progress, then we must wait for the one following, which is in -* "c". Because our request will be noticed at the end of the -* current grace period, we don't need to explicitly start one. +* Use funnel locking to either acquire the root rcu_node +* structure's lock or bail out if the need for this grace period kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1705
Quoted 10 lines
kernel/rcu/tree.c +5
-4+* has already been recorded -- or has already started. If there +* is already a grace period in progress in a non-leaf node, no +* recording is needed because the end of the grace period will +* scan the leaf rcu_node structures. Note that rnp->lock must +* not be released. */ -if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c) = true; -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); - goto out;Referring to the above negative diff as [1] (which I wanted to refer to later in this message..)
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1706
Quoted 11 lines
kernel/rcu/tree.c +11
-0+raw_lockdep_assert_held_rcu_node (rnp); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); +for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) { +if (rnp_root != rnp) +raw_spin_lock_rcu_node (rnp_root); +if (need_future_gp_element (rnp_root, c) || +ULONG_CMP_GE (rnp_root->gpnum, c) || + (rnp != rnp_root && + rnp_root->gpnum != rnp_root->completed)) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Prestarted" )); + goto unlock_out;I was a bit confused about the implementation of the above for loop:
In the previous code (which I refer to in the negative diff [1]), we were checking the leaf, and if the leaf believed that RCU was not idle, then we were marking the need for the future GP and quitting this function. In the new code, it seems like even if the leaf believes RCU is not-idle, we still go all the way up the tree.
I think the big change is, in the above new for loop, we either bail of if a future GP need was already marked by an intermediate node, or we go marking up the whole tree about the need for one.
Quoted 7 lines
If a leaf believes RCU is not idle, can we not just mark the future GP need like before and return? It seems we would otherwise increase the lock contention since now we lock intermediate nodes and then finally even the root. Where as before we were not doing that if the leaf believed RCU was not idle.
I am sorry if I missed something obvious.
The trick is that we do the check before we have done the marking. So if we bailed, we would not have marked at all. If we are at an intermediate node and a grace period is in progress, we do bail.
You are right that this means that we (perhaps unnecessarily) acquire the lock of the parent
rcu_node, which might or might not be the root. And on systems with default fanout with 1024 CPUs or fewer, yes, it will be the root, and yes, this is the common case. So might be well worth improving.One way to implement the old mark-and-return approach as you suggest above would be as shown below (untested, probably doesn't build, and against current rcu/dev). What do you think?
Quoted 5 lines
The other thing is we now don't have the 'Startedleaf' trace like we did before. I sent a patch to remove it, but I think the removal of that is somehow connected to what I was just talking about.. and I was thinking if we should really remove it. Should we add the case for checking leaves only back or is that a bad thing to do?
Suppose I got hit by a bus and you were stuck with the job of debugging this. What traces would you want and where would they be? Keeping in mind that too-frequent traces have their problems as well.
(Yes, I will be trying very hard to avoid this scenario for as long as I can, but this might be a good way for you (and everyone else) to be thinking about this.)
Thanx, Paul
------------------------------------------------------------------------
kernel/rcu/tree.c +0
-0
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1588
Quoted 4 lines
kernel/rcu/tree.c +1
-0@@ -1585,6 +1585,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, goto unlock_out; } rnp_root->gp_seq_needed = c; +if (rcu_seq_state (rcu_seq_current (&rnp_root->gp_seq)))
Right... Make that rnp->gp_seq. Memory locality and all that...
Thanx, Paul
Quoted 4 lines
kernel/rcu/tree.c +1
-0+ goto unlock_out;if (rnp_root != rnp && rnp_root->parent !=NULL )raw_spin_unlock_rcu_node (rnp_root);if (!rnp_root->parent)
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
Quoted 141 lines · Paul E. McKenney
Quoted 140 lines · Paul E. McKenney
Quoted 99 lines · Joel Fernandes
Quoted 60 lines · Paul E. McKenney
The
rcu_start_this_gp()function had a simple form of funnel locking that used only the leaves and root of thercu_nodetree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking.This variant of funnel locking is unusual in the following ways:
1. The leaf-level
rcu_nodestructure's ->lock is held throughout.
Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree.2. Funnel locking can be started at the root, which is convenient
for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves.3. If an
rcu_nodestructure other than the initial one believes
that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress.4. It is possible that the RCU grace-period kthread has not yet
started, and this case must be handled appropriately.However, the general approach of using a tree to control lock contention is still in place.
Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-)kernel/rcu/tree.c +3
-14@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, {bool ret = false;struct rcu_state *rsp = rdp->rsp; -struct rcu_node *rnp_root =rcu_get_root (rsp); - -raw_lockdep_assert_held_rcu_node (rnp); - -/* If the specified GP is already known needed, return to caller. */ -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (need_future_gp_element (rnp, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); - goto out; - } +struct rcu_node *rnp_root;/* -* If this rcu_node structure believes that a grace period is in -* progress, then we must wait for the one following, which is in -* "c". Because our request will be noticed at the end of the -* current grace period, we don't need to explicitly start one. +* Use funnel locking to either acquire the root rcu_node +* structure's lock or bail out if the need for this grace period kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1705
Quoted 10 lines
kernel/rcu/tree.c +5
-4+* has already been recorded -- or has already started. If there +* is already a grace period in progress in a non-leaf node, no +* recording is needed because the end of the grace period will +* scan the leaf rcu_node structures. Note that rnp->lock must +* not be released. */ -if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c) = true; -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); - goto out;Referring to the above negative diff as [1] (which I wanted to refer to later in this message..)
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1706
Quoted 11 lines
kernel/rcu/tree.c +11
-0+raw_lockdep_assert_held_rcu_node (rnp); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); +for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) { +if (rnp_root != rnp) +raw_spin_lock_rcu_node (rnp_root); +if (need_future_gp_element (rnp_root, c) || +ULONG_CMP_GE (rnp_root->gpnum, c) || + (rnp != rnp_root && + rnp_root->gpnum != rnp_root->completed)) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Prestarted" )); + goto unlock_out;I was a bit confused about the implementation of the above for loop:
In the previous code (which I refer to in the negative diff [1]), we were checking the leaf, and if the leaf believed that RCU was not idle, then we were marking the need for the future GP and quitting this function. In the new code, it seems like even if the leaf believes RCU is not-idle, we still go all the way up the tree.
I think the big change is, in the above new for loop, we either bail of if a future GP need was already marked by an intermediate node, or we go marking up the whole tree about the need for one.
Quoted 7 lines
If a leaf believes RCU is not idle, can we not just mark the future GP need like before and return? It seems we would otherwise increase the lock contention since now we lock intermediate nodes and then finally even the root. Where as before we were not doing that if the leaf believed RCU was not idle.
I am sorry if I missed something obvious.
The trick is that we do the check before we have done the marking. So if we bailed, we would not have marked at all. If we are at an intermediate node and a grace period is in progress, we do bail.
You are right that this means that we (perhaps unnecessarily) acquire the lock of the parent
rcu_node, which might or might not be the root. And on systems with default fanout with 1024 CPUs or fewer, yes, it will be the root, and yes, this is the common case. So might be well worth improving.One way to implement the old mark-and-return approach as you suggest above would be as shown below (untested, probably doesn't build, and against current rcu/dev). What do you think?
Quoted 5 lines
The other thing is we now don't have the 'Startedleaf' trace like we did before. I sent a patch to remove it, but I think the removal of that is somehow connected to what I was just talking about.. and I was thinking if we should really remove it. Should we add the case for checking leaves only back or is that a bad thing to do?
Suppose I got hit by a bus and you were stuck with the job of debugging this. What traces would you want and where would they be? Keeping in mind that too-frequent traces have their problems as well.
(Yes, I will be trying very hard to avoid this scenario for as long as I can, but this might be a good way for you (and everyone else) to be thinking about this.)
Thanx, Paul
------------------------------------------------------------------------
Quoted 8 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1587
Quoted 8 lines
kernel/rcu/tree.c +0
-0@@ -1585,6 +1585,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, goto unlock_out; } rnp_root->gp_seq_needed = c;
e
Quoted 5 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1589
Quoted 2 lines
kernel/rcu/tree.c +2
-0+if (rcu_seq_statn (rcu_seq_current (&rnp_root->gp_seq))) +if (rcu_seq_state (rcu_seq_current (&rnp_root->gp_seq)))Right... Make that
rnp->gp_seq. Memory locality and all that...Thanx, Paul
Yes, I think this condition would be right to add. I could roll it into my clean up patch.
Also, I think its better if we split the conditions for prestarted into separate if conditions and comment them so its clear, I have started to do that in my tree.
If you don't mind going through the if conditions in the funnel locking loop with me, it would be quite helpful so that I don't mess the code up and would also help me add tracing correctly.
The if condition for prestarted is this:
if (need_future_gp_element(rnp_root, c) ||
ULONG_CMP_GE(rnp_root->gpnum, c) ||
(rnp != rnp_root &&
rnp_root->gpnum != rnp_root->completed)) {
trace_rcu_this_gp(rnp_root, rdp, c, TPS("Prestarted"));
goto unlock_out;
need_future_gp_element(rnp_root, c) = true;As of 16/21, the heart of the loop is the above (excluding the locking bits)
In this what confuses me is the second and the third condition for pre-started.
The second condition is: ULONG_CMP_GE(rnp_root->gpnum, c).
AIUI the goal of this condition is to check whether the requested grace period has already started. I believe then the above check is insufficient. The reason I think its insufficient is I believe we should also check the state of the grace period to augment this check.
IMO the condition should really be:
(ULONG_CMP_GT(rnp_root->gpnum, c) ||
(rnp_root->gpnum == c && rnp_root->gpnum != rnp_root->completed))
In a later patch you replaced this with rseq_done(&rnp_root->gp_seq, c) which kind of accounts for the state, except that rseq_done uses ULONG_CMP_GE, whereas to fix this, rseq_done IMO should be using ULONG_CMP_GT to be equivalent to the above check. Do you agree?
The third condition for pre-started is:
(rnp != rnp_root && rnp_root->gpnum != rnp_root->completed))
This as I followed from your commit message is if an intermediate node thinks RCU is non-idle, then its not necessary to mark the tree and we can bail out since the clean up will scan the whole tree anyway. That makes sense to me but I think I will like to squash the diff in your previous email into this condition as well to handle both conditions together.
thanks,
- Joel
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
Quoted 151 lines · Joel Fernandes
Quoted 141 lines · Paul E. McKenney
Quoted 140 lines · Paul E. McKenney
Quoted 99 lines · Joel Fernandes
Quoted 60 lines · Paul E. McKenney
The
rcu_start_this_gp()function had a simple form of funnel locking that used only the leaves and root of thercu_nodetree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking.This variant of funnel locking is unusual in the following ways:
1. The leaf-level
rcu_nodestructure's ->lock is held throughout.
Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree.2. Funnel locking can be started at the root, which is convenient
for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves.3. If an
rcu_nodestructure other than the initial one believes
that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress.4. It is possible that the RCU grace-period kthread has not yet
started, and this case must be handled appropriately.However, the general approach of using a tree to control lock contention is still in place.
Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-)kernel/rcu/tree.c +3
-14@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, {bool ret = false;struct rcu_state *rsp = rdp->rsp; -struct rcu_node *rnp_root =rcu_get_root (rsp); - -raw_lockdep_assert_held_rcu_node (rnp); - -/* If the specified GP is already known needed, return to caller. */ -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (need_future_gp_element (rnp, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); - goto out; - } +struct rcu_node *rnp_root;/* -* If this rcu_node structure believes that a grace period is in -* progress, then we must wait for the one following, which is in -* "c". Because our request will be noticed at the end of the -* current grace period, we don't need to explicitly start one. +* Use funnel locking to either acquire the root rcu_node +* structure's lock or bail out if the need for this grace period kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1705
Quoted 10 lines
kernel/rcu/tree.c +5
-4+* has already been recorded -- or has already started. If there +* is already a grace period in progress in a non-leaf node, no +* recording is needed because the end of the grace period will +* scan the leaf rcu_node structures. Note that rnp->lock must +* not be released. */ -if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c) = true; -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); - goto out;Referring to the above negative diff as [1] (which I wanted to refer to later in this message..)
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1706
Quoted 11 lines
kernel/rcu/tree.c +11
-0+raw_lockdep_assert_held_rcu_node (rnp); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); +for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) { +if (rnp_root != rnp) +raw_spin_lock_rcu_node (rnp_root); +if (need_future_gp_element (rnp_root, c) || +ULONG_CMP_GE (rnp_root->gpnum, c) || + (rnp != rnp_root && + rnp_root->gpnum != rnp_root->completed)) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Prestarted" )); + goto unlock_out;I was a bit confused about the implementation of the above for loop:
In the previous code (which I refer to in the negative diff [1]), we were checking the leaf, and if the leaf believed that RCU was not idle, then we were marking the need for the future GP and quitting this function. In the new code, it seems like even if the leaf believes RCU is not-idle, we still go all the way up the tree.
I think the big change is, in the above new for loop, we either bail of if a future GP need was already marked by an intermediate node, or we go marking up the whole tree about the need for one.
Quoted 7 lines
If a leaf believes RCU is not idle, can we not just mark the future GP need like before and return? It seems we would otherwise increase the lock contention since now we lock intermediate nodes and then finally even the root. Where as before we were not doing that if the leaf believed RCU was not idle.
I am sorry if I missed something obvious.
The trick is that we do the check before we have done the marking. So if we bailed, we would not have marked at all. If we are at an intermediate node and a grace period is in progress, we do bail.
You are right that this means that we (perhaps unnecessarily) acquire the lock of the parent
rcu_node, which might or might not be the root. And on systems with default fanout with 1024 CPUs or fewer, yes, it will be the root, and yes, this is the common case. So might be well worth improving.One way to implement the old mark-and-return approach as you suggest above would be as shown below (untested, probably doesn't build, and against current rcu/dev). What do you think?
Quoted 5 lines
The other thing is we now don't have the 'Startedleaf' trace like we did before. I sent a patch to remove it, but I think the removal of that is somehow connected to what I was just talking about.. and I was thinking if we should really remove it. Should we add the case for checking leaves only back or is that a bad thing to do?
Suppose I got hit by a bus and you were stuck with the job of debugging this. What traces would you want and where would they be? Keeping in mind that too-frequent traces have their problems as well.
(Yes, I will be trying very hard to avoid this scenario for as long as I can, but this might be a good way for you (and everyone else) to be thinking about this.)
Thanx, Paul
------------------------------------------------------------------------
Quoted 8 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1587
Quoted 8 lines
kernel/rcu/tree.c +0
-0@@ -1585,6 +1585,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, goto unlock_out; } rnp_root->gp_seq_needed = c;e
Quoted 8 lines
Quoted 5 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1589
Quoted 2 lines
kernel/rcu/tree.c +2
-0+if (rcu_seq_statn (rcu_seq_current (&rnp_root->gp_seq))) +if (rcu_seq_state (rcu_seq_current (&rnp_root->gp_seq)))Right... Make that
rnp->gp_seq. Memory locality and all that...Thanx, Paul
Yes, I think this condition would be right to add. I could roll it into my clean up patch.
I already queued it, please see below.
Quoted 3 lines
Also, I think its better if we split the conditions for prestarted into separate if conditions and comment them so its clear, I have started to do that in my tree.
Hmmm... Let's see how this plays out.
Quoted 19 lines
If you don't mind going through the if conditions in the funnel locking loop with me, it would be quite helpful so that I don't mess the code up and would also help me add tracing correctly.
The if condition for prestarted is this:
if (need_future_gp_element(rnp_root, c) || ULONG_CMP_GE(rnp_root->gpnum, c) || (rnp != rnp_root && rnp_root->gpnum != rnp_root->completed)) { trace_rcu_this_gp(rnp_root, rdp, c, TPS("Prestarted")); goto unlock_out; need_future_gp_element(rnp_root, c) = true;As of 16/21, the heart of the loop is the above (excluding the locking bits)
In this what confuses me is the second and the third condition for pre-started.
Quoted 7 lines
The second condition is:
ULONG_CMP_GE(rnp_root->gpnum, c). AIUI the goal of this condition is to check whether the requested grace period has already started. I believe then the above check is insufficient. The reason I think its insufficient is I believe we should also check the state of the grace period to augment this check.
IMO the condition should really be:
(ULONG_CMP_GT(rnp_root->gpnum, c)||
The above asks whether the -next- grace period -after- the requested one had started.
Quoted 1 line
(rnp_root->gpnum == c && rnp_root->gpnum != rnp_root->completed))
This asks that the requested grace period not have completed.
What about the case where the requested grace period has completed, but the one after has not yet started? If you add that in, I bet you will have something that simplifies to my original.
Quoted 4 lines
In a later patch you replaced this with
rseq_done(&rnp_root->gp_seq, c)which kind of accounts for the state, except thatrseq_doneusesULONG_CMP_GE, whereas to fix this,rseq_doneIMO should be usingULONG_CMP_GTto be equivalent to the above check. Do you agree?
I do not believe that I do. The ULONG_CMP_GE() allows for the missing case where the requested grace period completed, but the following grace period has not yet started.
Quoted 7 lines
The third condition for pre-started is:
(rnp != rnp_root && rnp_root->gpnum != rnp_root->completed))This as I followed from your commit message is if an intermediate node thinks RCU is non-idle, then its not necessary to mark the tree and we can bail out since the clean up will scan the whole tree anyway. That makes sense to me but I think I will like to squash the diff in your previous email into this condition as well to handle both conditions together.
Please keep in mind that it is necessary to actually record the request in the leaf case. Or are you advocating use of ?: or similar to make this happen?
Thanx, Paul
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
Quoted 156 lines · Paul E. McKenney
Quoted 155 lines · Joel Fernandes
Quoted 141 lines · Paul E. McKenney
Quoted 140 lines · Paul E. McKenney
Quoted 99 lines · Joel Fernandes
Quoted 60 lines · Paul E. McKenney
The
rcu_start_this_gp()function had a simple form of funnel locking that used only the leaves and root of thercu_nodetree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking.This variant of funnel locking is unusual in the following ways:
1. The leaf-level
rcu_nodestructure's ->lock is held throughout.
Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree.2. Funnel locking can be started at the root, which is convenient
for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves.3. If an
rcu_nodestructure other than the initial one believes
that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress.4. It is possible that the RCU grace-period kthread has not yet
started, and this case must be handled appropriately.However, the general approach of using a tree to control lock contention is still in place.
Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-)kernel/rcu/tree.c +3
-14@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, {bool ret = false;struct rcu_state *rsp = rdp->rsp; -struct rcu_node *rnp_root =rcu_get_root (rsp); - -raw_lockdep_assert_held_rcu_node (rnp); - -/* If the specified GP is already known needed, return to caller. */ -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (need_future_gp_element (rnp, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); - goto out; - } +struct rcu_node *rnp_root;/* -* If this rcu_node structure believes that a grace period is in -* progress, then we must wait for the one following, which is in -* "c". Because our request will be noticed at the end of the -* current grace period, we don't need to explicitly start one. +* Use funnel locking to either acquire the root rcu_node +* structure's lock or bail out if the need for this grace period kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1705
Quoted 10 lines
kernel/rcu/tree.c +5
-4+* has already been recorded -- or has already started. If there +* is already a grace period in progress in a non-leaf node, no +* recording is needed because the end of the grace period will +* scan the leaf rcu_node structures. Note that rnp->lock must +* not be released. */ -if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c) = true; -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); - goto out;Referring to the above negative diff as [1] (which I wanted to refer to later in this message..)
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1706
Quoted 11 lines
kernel/rcu/tree.c +11
-0+raw_lockdep_assert_held_rcu_node (rnp); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); +for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) { +if (rnp_root != rnp) +raw_spin_lock_rcu_node (rnp_root); +if (need_future_gp_element (rnp_root, c) || +ULONG_CMP_GE (rnp_root->gpnum, c) || + (rnp != rnp_root && + rnp_root->gpnum != rnp_root->completed)) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Prestarted" )); + goto unlock_out;I was a bit confused about the implementation of the above for loop:
In the previous code (which I refer to in the negative diff [1]), we were checking the leaf, and if the leaf believed that RCU was not idle, then we were marking the need for the future GP and quitting this function. In the new code, it seems like even if the leaf believes RCU is not-idle, we still go all the way up the tree.
I think the big change is, in the above new for loop, we either bail of if a future GP need was already marked by an intermediate node, or we go marking up the whole tree about the need for one.
Quoted 7 lines
If a leaf believes RCU is not idle, can we not just mark the future GP need like before and return? It seems we would otherwise increase the lock contention since now we lock intermediate nodes and then finally even the root. Where as before we were not doing that if the leaf believed RCU was not idle.
I am sorry if I missed something obvious.
The trick is that we do the check before we have done the marking. So if we bailed, we would not have marked at all. If we are at an intermediate node and a grace period is in progress, we do bail.
You are right that this means that we (perhaps unnecessarily) acquire the lock of the parent
rcu_node, which might or might not be the root. And on systems with default fanout with 1024 CPUs or fewer, yes, it will be the root, and yes, this is the common case. So might be well worth improving.One way to implement the old mark-and-return approach as you suggest above would be as shown below (untested, probably doesn't build, and against current rcu/dev). What do you think?
Quoted 5 lines
The other thing is we now don't have the 'Startedleaf' trace like we did before. I sent a patch to remove it, but I think the removal of that is somehow connected to what I was just talking about.. and I was thinking if we should really remove it. Should we add the case for checking leaves only back or is that a bad thing to do?
Suppose I got hit by a bus and you were stuck with the job of debugging this. What traces would you want and where would they be? Keeping in mind that too-frequent traces have their problems as well.
(Yes, I will be trying very hard to avoid this scenario for as long as I can, but this might be a good way for you (and everyone else) to be thinking about this.)
Thanx, Paul
------------------------------------------------------------------------
Quoted 8 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1587
Quoted 8 lines
kernel/rcu/tree.c +0
-0@@ -1585,6 +1585,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, goto unlock_out; } rnp_root->gp_seq_needed = c;e
Quoted 4 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1589
Quoted 2 lines
kernel/rcu/tree.c +2
-0+if (rcu_seq_statn (rcu_seq_current (&rnp_root->gp_seq))) +if (rcu_seq_state (rcu_seq_current (&rnp_root->gp_seq)))Right... Make that
rnp->gp_seq. Memory locality and all that...
Quoted 6 lines
Quoted 4 lines
Quoted 1 line
Thanx, Paul
Yes, I think this condition would be right to add. I could roll it into my clean up patch.
I already queued it, please see below.
Cool!
Quoted 6 lines
Quoted 3 lines
Also, I think its better if we split the conditions for prestarted into separate if conditions and comment them so its clear, I have started to do that in my tree.
Hmmm... Let's see how this plays out.
Sure.
Quoted 38 lines
Quoted 19 lines
If you don't mind going through the if conditions in the funnel locking loop with me, it would be quite helpful so that I don't mess the code up and would also help me add tracing correctly.
The if condition for prestarted is this:
if (need_future_gp_element(rnp_root, c) || ULONG_CMP_GE(rnp_root->gpnum, c) || (rnp != rnp_root && rnp_root->gpnum != rnp_root->completed)) { trace_rcu_this_gp(rnp_root, rdp, c, TPS("Prestarted")); goto unlock_out; need_future_gp_element(rnp_root, c) = true;As of 16/21, the heart of the loop is the above (excluding the locking bits)
In this what confuses me is the second and the third condition for pre-started.
Quoted 7 lines
The second condition is:
ULONG_CMP_GE(rnp_root->gpnum, c). AIUI the goal of this condition is to check whether the requested grace period has already started. I believe then the above check is insufficient. The reason I think its insufficient is I believe we should also check the state of the grace period to augment this check.
IMO the condition should really be:
(ULONG_CMP_GT(rnp_root->gpnum, c)||The above asks whether the -next- grace period -after- the requested one had started.
Quoted 1 line
(rnp_root->gpnum == c && rnp_root->gpnum != rnp_root->completed))This asks that the requested grace period not have completed.
What about the case where the requested grace period has completed, but the one after has not yet started? If you add that in, I bet you will have something that simplifies to my original.
Quoted 8 lines
Quoted 4 lines
In a later patch you replaced this with
rseq_done(&rnp_root->gp_seq, c)which kind of accounts for the state, except thatrseq_doneusesULONG_CMP_GE, whereas to fix this,rseq_doneIMO should be usingULONG_CMP_GTto be equivalent to the above check. Do you agree?I do not believe that I do. The
ULONG_CMP_GE()allows for the missing case where the requested grace period completed, but the following grace period has not yet started.
Ok thanks that clears it up. For some reason I was thinking if rnp_root->gpnum == c, that could means 'c' has not yet started, unless we also checked the state. Obviously, now I realize gpnum == c can only mean 2 things:
- c has started but not yet completed
- c has completed
Both of these cases should cause a bail out so I agree now with your condition ULONG_CMP_GE, thanks.
Quoted 9 lines
Quoted 8 lines
The third condition for pre-started is:
(rnp != rnp_root && rnp_root->gpnum != rnp_root->completed))This as I followed from your commit message is if an intermediate node thinks RCU is non-idle, then its not necessary to mark the tree and we can bail out since the clean up will scan the whole tree anyway. That makes sense to me but I think I will like to squash the diff in your previous email into this condition as well to handle both conditions together.
Quoted 3 lines
Please keep in mind that it is necessary to actually record the request in the leaf case. Or are you advocating use of ?: or similar to make this happen?
Yes, I realized yesterday you wanted to record it for the leaf that's why you're doing things this way. I'll let you know if I find any other ways of simplifying it once I look at your latest tree.
Btw, I checked your git tree and couldn't see the update that you mentioned you queued above. Could you push those changes?
thanks!
- Joel
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
Quoted 245 lines · Joel Fernandes
Quoted 156 lines · Paul E. McKenney
Quoted 155 lines · Joel Fernandes
Quoted 141 lines · Paul E. McKenney
Quoted 140 lines · Paul E. McKenney
Quoted 99 lines · Joel Fernandes
Quoted 60 lines · Paul E. McKenney
The
rcu_start_this_gp()function had a simple form of funnel locking that used only the leaves and root of thercu_nodetree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking.This variant of funnel locking is unusual in the following ways:
1. The leaf-level
rcu_nodestructure's ->lock is held throughout.
Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree.2. Funnel locking can be started at the root, which is convenient
for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves.3. If an
rcu_nodestructure other than the initial one believes
that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress.4. It is possible that the RCU grace-period kthread has not yet
started, and this case must be handled appropriately.However, the general approach of using a tree to control lock contention is still in place.
Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-)kernel/rcu/tree.c +3
-14@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, {bool ret = false;struct rcu_state *rsp = rdp->rsp; -struct rcu_node *rnp_root =rcu_get_root (rsp); - -raw_lockdep_assert_held_rcu_node (rnp); - -/* If the specified GP is already known needed, return to caller. */ -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (need_future_gp_element (rnp, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); - goto out; - } +struct rcu_node *rnp_root;/* -* If this rcu_node structure believes that a grace period is in -* progress, then we must wait for the one following, which is in -* "c". Because our request will be noticed at the end of the -* current grace period, we don't need to explicitly start one. +* Use funnel locking to either acquire the root rcu_node +* structure's lock or bail out if the need for this grace period kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1705
Quoted 10 lines
kernel/rcu/tree.c +5
-4+* has already been recorded -- or has already started. If there +* is already a grace period in progress in a non-leaf node, no +* recording is needed because the end of the grace period will +* scan the leaf rcu_node structures. Note that rnp->lock must +* not be released. */ -if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c) = true; -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); - goto out;Referring to the above negative diff as [1] (which I wanted to refer to later in this message..)
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1706
Quoted 11 lines
kernel/rcu/tree.c +11
-0+raw_lockdep_assert_held_rcu_node (rnp); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); +for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) { +if (rnp_root != rnp) +raw_spin_lock_rcu_node (rnp_root); +if (need_future_gp_element (rnp_root, c) || +ULONG_CMP_GE (rnp_root->gpnum, c) || + (rnp != rnp_root && + rnp_root->gpnum != rnp_root->completed)) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Prestarted" )); + goto unlock_out;I was a bit confused about the implementation of the above for loop:
In the previous code (which I refer to in the negative diff [1]), we were checking the leaf, and if the leaf believed that RCU was not idle, then we were marking the need for the future GP and quitting this function. In the new code, it seems like even if the leaf believes RCU is not-idle, we still go all the way up the tree.
I think the big change is, in the above new for loop, we either bail of if a future GP need was already marked by an intermediate node, or we go marking up the whole tree about the need for one.
Quoted 7 lines
If a leaf believes RCU is not idle, can we not just mark the future GP need like before and return? It seems we would otherwise increase the lock contention since now we lock intermediate nodes and then finally even the root. Where as before we were not doing that if the leaf believed RCU was not idle.
I am sorry if I missed something obvious.
The trick is that we do the check before we have done the marking. So if we bailed, we would not have marked at all. If we are at an intermediate node and a grace period is in progress, we do bail.
You are right that this means that we (perhaps unnecessarily) acquire the lock of the parent
rcu_node, which might or might not be the root. And on systems with default fanout with 1024 CPUs or fewer, yes, it will be the root, and yes, this is the common case. So might be well worth improving.One way to implement the old mark-and-return approach as you suggest above would be as shown below (untested, probably doesn't build, and against current rcu/dev). What do you think?
Quoted 5 lines
The other thing is we now don't have the 'Startedleaf' trace like we did before. I sent a patch to remove it, but I think the removal of that is somehow connected to what I was just talking about.. and I was thinking if we should really remove it. Should we add the case for checking leaves only back or is that a bad thing to do?
Suppose I got hit by a bus and you were stuck with the job of debugging this. What traces would you want and where would they be? Keeping in mind that too-frequent traces have their problems as well.
(Yes, I will be trying very hard to avoid this scenario for as long as I can, but this might be a good way for you (and everyone else) to be thinking about this.)
Thanx, Paul
------------------------------------------------------------------------
Quoted 8 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1587
Quoted 8 lines
kernel/rcu/tree.c +0
-0@@ -1585,6 +1585,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, goto unlock_out; } rnp_root->gp_seq_needed = c;e
Quoted 4 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1589
Quoted 2 lines
kernel/rcu/tree.c +2
-0+if (rcu_seq_statn (rcu_seq_current (&rnp_root->gp_seq))) +if (rcu_seq_state (rcu_seq_current (&rnp_root->gp_seq)))Right... Make that
rnp->gp_seq. Memory locality and all that...Quoted 6 lines
Quoted 4 lines
Quoted 1 line
Thanx, Paul
Yes, I think this condition would be right to add. I could roll it into my clean up patch.
I already queued it, please see below.
Cool!
Quoted 6 lines
Quoted 3 lines
Also, I think its better if we split the conditions for prestarted into separate if conditions and comment them so its clear, I have started to do that in my tree.
Hmmm... Let's see how this plays out.
Sure.
Quoted 38 lines
Quoted 19 lines
If you don't mind going through the if conditions in the funnel locking loop with me, it would be quite helpful so that I don't mess the code up and would also help me add tracing correctly.
The if condition for prestarted is this:
if (need_future_gp_element(rnp_root, c) || ULONG_CMP_GE(rnp_root->gpnum, c) || (rnp != rnp_root && rnp_root->gpnum != rnp_root->completed)) { trace_rcu_this_gp(rnp_root, rdp, c, TPS("Prestarted")); goto unlock_out; need_future_gp_element(rnp_root, c) = true;As of 16/21, the heart of the loop is the above (excluding the locking bits)
In this what confuses me is the second and the third condition for pre-started.
Quoted 7 lines
The second condition is:
ULONG_CMP_GE(rnp_root->gpnum, c). AIUI the goal of this condition is to check whether the requested grace period has already started. I believe then the above check is insufficient. The reason I think its insufficient is I believe we should also check the state of the grace period to augment this check.
IMO the condition should really be:
(ULONG_CMP_GT(rnp_root->gpnum, c)||The above asks whether the -next- grace period -after- the requested one had started.
Quoted 1 line
(rnp_root->gpnum == c && rnp_root->gpnum != rnp_root->completed))This asks that the requested grace period not have completed.
What about the case where the requested grace period has completed, but the one after has not yet started? If you add that in, I bet you will have something that simplifies to my original.
Quoted 8 lines
Quoted 4 lines
In a later patch you replaced this with
rseq_done(&rnp_root->gp_seq, c)which kind of accounts for the state, except thatrseq_doneusesULONG_CMP_GE, whereas to fix this,rseq_doneIMO should be usingULONG_CMP_GTto be equivalent to the above check. Do you agree?I do not believe that I do. The
ULONG_CMP_GE()allows for the missing case where the requested grace period completed, but the following grace period has not yet started.Ok thanks that clears it up. For some reason I was thinking if
rnp_root->gpnum== c, that could means 'c' has not yet started, unless we also checked the state. Obviously, now I realize gpnum == c can only mean 2 things:
- c has started but not yet completed
- c has completedBoth of these cases should cause a bail out so I agree now with your condition
ULONG_CMP_GE, thanks.Quoted 9 lines
Quoted 8 lines
The third condition for pre-started is:
(rnp != rnp_root && rnp_root->gpnum != rnp_root->completed))This as I followed from your commit message is if an intermediate node thinks RCU is non-idle, then its not necessary to mark the tree and we can bail out since the clean up will scan the whole tree anyway. That makes sense to me but I think I will like to squash the diff in your previous email into this condition as well to handle both conditions together.
Quoted 3 lines
Please keep in mind that it is necessary to actually record the request in the leaf case. Or are you advocating use of ?: or similar to make this happen?
Quoted 6 lines
Yes, I realized yesterday you wanted to record it for the leaf that's why you're doing things this way. I'll let you know if I find any other ways of simplifying it once I look at your latest tree.
Btw, I checked your git tree and couldn't see the update that you mentioned you queued above. Could you push those changes?
Good point, pushed now. And the patch that I forgot to include in the last email is below.
Thanx, Paul
------------------------------------------------------------------------
commit 4deafc6f9c1cddc7cabd0632137368b5de21ff74
Author: Paul E. McKenney <[email protected]> Date: Sat May 12 07:42:20 2018 -0700
rcu: Don't funnel-lock above leaf node if GP in progress
The old grace-period start code would acquire only the leaf's rcu_node structure's ->lock if that structure believed that a grace period was in progress. The new code advances to the leaf's parent in this case, needlessly acquiring then leaf's parent's ->lock. This commit therefore checks the grace-period state after marking the leaf with the need for the specified grace period, and if the leaf believes that a grace period is in progress, takes an early exit.
kernel/rcu/tree.c +2 -0
@@ -1585,6 +1585,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, goto unlock_out; } rnp_root->gp_seq_needed = c; +if (rcu_seq_state (rcu_seq_current (&rnp->gp_seq))) + goto unlock_out;if (rnp_root != rnp && rnp_root->parent !=NULL )raw_spin_unlock_rcu_node (rnp_root);if (!rnp_root->parent)
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
Quoted 250 lines · Paul E. McKenney
Quoted 249 lines · Joel Fernandes
Quoted 156 lines · Paul E. McKenney
Quoted 155 lines · Joel Fernandes
Quoted 141 lines · Paul E. McKenney
Quoted 140 lines · Paul E. McKenney
Quoted 99 lines · Joel Fernandes
Quoted 60 lines · Paul E. McKenney
The
rcu_start_this_gp()function had a simple form of funnel locking that used only the leaves and root of thercu_nodetree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking.This variant of funnel locking is unusual in the following ways:
1. The leaf-level
rcu_nodestructure's ->lock is held throughout.
Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree.2. Funnel locking can be started at the root, which is convenient
for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves.3. If an
rcu_nodestructure other than the initial one believes
that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress.4. It is possible that the RCU grace-period kthread has not yet
started, and this case must be handled appropriately.However, the general approach of using a tree to control lock contention is still in place.
Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-)kernel/rcu/tree.c +3
-14@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, {bool ret = false;struct rcu_state *rsp = rdp->rsp; -struct rcu_node *rnp_root =rcu_get_root (rsp); - -raw_lockdep_assert_held_rcu_node (rnp); - -/* If the specified GP is already known needed, return to caller. */ -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (need_future_gp_element (rnp, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); - goto out; - } +struct rcu_node *rnp_root;/* -* If this rcu_node structure believes that a grace period is in -* progress, then we must wait for the one following, which is in -* "c". Because our request will be noticed at the end of the -* current grace period, we don't need to explicitly start one. +* Use funnel locking to either acquire the root rcu_node +* structure's lock or bail out if the need for this grace period kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1705
Quoted 10 lines
kernel/rcu/tree.c +5
-4+* has already been recorded -- or has already started. If there +* is already a grace period in progress in a non-leaf node, no +* recording is needed because the end of the grace period will +* scan the leaf rcu_node structures. Note that rnp->lock must +* not be released. */ -if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c) = true; -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); - goto out;Referring to the above negative diff as [1] (which I wanted to refer to later in this message..)
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1706
Quoted 11 lines
kernel/rcu/tree.c +11
-0+raw_lockdep_assert_held_rcu_node (rnp); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); +for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) { +if (rnp_root != rnp) +raw_spin_lock_rcu_node (rnp_root); +if (need_future_gp_element (rnp_root, c) || +ULONG_CMP_GE (rnp_root->gpnum, c) || + (rnp != rnp_root && + rnp_root->gpnum != rnp_root->completed)) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Prestarted" )); + goto unlock_out;I was a bit confused about the implementation of the above for loop:
In the previous code (which I refer to in the negative diff [1]), we were checking the leaf, and if the leaf believed that RCU was not idle, then we were marking the need for the future GP and quitting this function. In the new code, it seems like even if the leaf believes RCU is not-idle, we still go all the way up the tree.
I think the big change is, in the above new for loop, we either bail of if a future GP need was already marked by an intermediate node, or we go marking up the whole tree about the need for one.
Quoted 7 lines
If a leaf believes RCU is not idle, can we not just mark the future GP need like before and return? It seems we would otherwise increase the lock contention since now we lock intermediate nodes and then finally even the root. Where as before we were not doing that if the leaf believed RCU was not idle.
I am sorry if I missed something obvious.
The trick is that we do the check before we have done the marking. So if we bailed, we would not have marked at all. If we are at an intermediate node and a grace period is in progress, we do bail.
You are right that this means that we (perhaps unnecessarily) acquire the lock of the parent
rcu_node, which might or might not be the root. And on systems with default fanout with 1024 CPUs or fewer, yes, it will be the root, and yes, this is the common case. So might be well worth improving.One way to implement the old mark-and-return approach as you suggest above would be as shown below (untested, probably doesn't build, and against current rcu/dev). What do you think?
Quoted 5 lines
The other thing is we now don't have the 'Startedleaf' trace like we did before. I sent a patch to remove it, but I think the removal of that is somehow connected to what I was just talking about.. and I was thinking if we should really remove it. Should we add the case for checking leaves only back or is that a bad thing to do?
Suppose I got hit by a bus and you were stuck with the job of debugging this. What traces would you want and where would they be? Keeping in mind that too-frequent traces have their problems as well.
(Yes, I will be trying very hard to avoid this scenario for as long as I can, but this might be a good way for you (and everyone else) to be thinking about this.)
Thanx, Paul
------------------------------------------------------------------------
Quoted 8 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1587
Quoted 8 lines
kernel/rcu/tree.c +0
-0@@ -1585,6 +1585,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, goto unlock_out; } rnp_root->gp_seq_needed = c;e
Quoted 4 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1589
Quoted 2 lines
kernel/rcu/tree.c +2
-0+if (rcu_seq_statn (rcu_seq_current (&rnp_root->gp_seq))) +if (rcu_seq_state (rcu_seq_current (&rnp_root->gp_seq)))Right... Make that
rnp->gp_seq. Memory locality and all that...Quoted 6 lines
Quoted 4 lines
Quoted 1 line
Thanx, Paul
Yes, I think this condition would be right to add. I could roll it into my clean up patch.
I already queued it, please see below.
Cool!
Quoted 6 lines
Quoted 3 lines
Also, I think its better if we split the conditions for prestarted into separate if conditions and comment them so its clear, I have started to do that in my tree.
Hmmm... Let's see how this plays out.
Sure.
Quoted 38 lines
Quoted 19 lines
If you don't mind going through the if conditions in the funnel locking loop with me, it would be quite helpful so that I don't mess the code up and would also help me add tracing correctly.
The if condition for prestarted is this:
if (need_future_gp_element(rnp_root, c) || ULONG_CMP_GE(rnp_root->gpnum, c) || (rnp != rnp_root && rnp_root->gpnum != rnp_root->completed)) { trace_rcu_this_gp(rnp_root, rdp, c, TPS("Prestarted")); goto unlock_out; need_future_gp_element(rnp_root, c) = true;As of 16/21, the heart of the loop is the above (excluding the locking bits)
In this what confuses me is the second and the third condition for pre-started.
Quoted 7 lines
The second condition is:
ULONG_CMP_GE(rnp_root->gpnum, c). AIUI the goal of this condition is to check whether the requested grace period has already started. I believe then the above check is insufficient. The reason I think its insufficient is I believe we should also check the state of the grace period to augment this check.
IMO the condition should really be:
(ULONG_CMP_GT(rnp_root->gpnum, c)||The above asks whether the -next- grace period -after- the requested one had started.
Quoted 1 line
(rnp_root->gpnum == c && rnp_root->gpnum != rnp_root->completed))This asks that the requested grace period not have completed.
What about the case where the requested grace period has completed, but the one after has not yet started? If you add that in, I bet you will have something that simplifies to my original.
Quoted 8 lines
Quoted 4 lines
In a later patch you replaced this with
rseq_done(&rnp_root->gp_seq, c)which kind of accounts for the state, except thatrseq_doneusesULONG_CMP_GE, whereas to fix this,rseq_doneIMO should be usingULONG_CMP_GTto be equivalent to the above check. Do you agree?I do not believe that I do. The
ULONG_CMP_GE()allows for the missing case where the requested grace period completed, but the following grace period has not yet started.Ok thanks that clears it up. For some reason I was thinking if
rnp_root->gpnum== c, that could means 'c' has not yet started, unless we also checked the state. Obviously, now I realize gpnum == c can only mean 2 things:
- c has started but not yet completed
- c has completedBoth of these cases should cause a bail out so I agree now with your condition
ULONG_CMP_GE, thanks.Quoted 9 lines
Quoted 8 lines
The third condition for pre-started is:
(rnp != rnp_root && rnp_root->gpnum != rnp_root->completed))This as I followed from your commit message is if an intermediate node thinks RCU is non-idle, then its not necessary to mark the tree and we can bail out since the clean up will scan the whole tree anyway. That makes sense to me but I think I will like to squash the diff in your previous email into this condition as well to handle both conditions together.
Quoted 3 lines
Please keep in mind that it is necessary to actually record the request in the leaf case. Or are you advocating use of ?: or similar to make this happen?
Yes, I realized yesterday you wanted to record it for the leaf that's why you're doing things this way. I'll let you know if I find any other ways of simplifying it once I look at your latest tree.
Quoted 5 lines
Quoted 2 lines
Btw, I checked your git tree and couldn't see the update that you mentioned you queued above. Could you push those changes?
Good point, pushed now. And the patch that I forgot to include in the last email is below.
Cool, thanks. Also one thing I wanted to discuss, I am a bit unclear about the if (rcu_seq_done..) condition in the loop which decides if the GP requested is pre-started.
Say c is 8 (0b1000) - i.e. gp requested is 2.
I drew some tables with some examples, the result column is what the current code will do.
Say gp_seq is 12 and its not progress (0b1100),
gp_seq gp_num state analysis of gp_seq result
12 3 0 gp 3 not started pre-started
(gp 2 completed)
For this, the "greater than" check in rcu_seq_done will work because 2 already completed (The check essentially does 12 >= 8 which implies prestarted).
Say gp_seq is 9 and it is in progress (0b1001)gp_seq gp_num state state of gp_seq result
9 2 1 gp 2 in progress pre-started
(gp 1 completed)
Here also the "greater than" check is correct (9 >= 8 which implies prestarted).
However, say gp_seq is 8gp_seq gp_num state state of gp_seq result
8 2 0 gp 2 not started pre-started
(gp 1 completed)
In this case, rcu_seq_done will incorrectly say that its pre-started when 2 has not yet started. For this reason, I feel the equal-to check in rcu_seq_done will incorrectly predict prestarted.
I think to fix this, the rseq_done condition could be replaced with:
if (ULONG_CMP_GT(rnp_root->gpseq, c)) {
// pre-started
}I believe the difference arises because one of the patches during the conversion to use gp_seq in the tree replaced rcu_seq_done with ULONG_CMP_GE, where as such a replacement doesn't work in the gp_seq regime because of difference in the way a gp's starte/end is accounted (vs the old way).
Does it make sense or was I way off about something :D ?
thanks,
- Joel
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
Quoted 254 lines · Joel Fernandes
Quoted 253 lines · Paul E. McKenney
Quoted 251 lines · Joel Fernandes
Quoted 156 lines · Paul E. McKenney
Quoted 155 lines · Joel Fernandes
Quoted 141 lines · Paul E. McKenney
Quoted 140 lines · Paul E. McKenney
Quoted 99 lines · Joel Fernandes
Quoted 60 lines · Paul E. McKenney
The
rcu_start_this_gp()function had a simple form of funnel locking that used only the leaves and root of thercu_nodetree, which is fine for systems with only a few hundred CPUs, but sub-optimal for systems having thousands of CPUs. This commit therefore adds full-tree funnel locking.This variant of funnel locking is unusual in the following ways:
1. The leaf-level
rcu_nodestructure's ->lock is held throughout.
Other funnel-locking implementations drop the leaf-level lock before progressing to the next level of the tree.2. Funnel locking can be started at the root, which is convenient
for code that already holds the root rcu_node structure's ->lock. Other funnel-locking implementations start at the leaves.3. If an
rcu_nodestructure other than the initial one believes
that a grace period is in progress, it is not necessary to go further up the tree. This is because grace-period cleanup scans the full tree, so that marking the need for a subsequent grace period anywhere in the tree suffices -- but only if a grace period is currently in progress.4. It is possible that the RCU grace-period kthread has not yet
started, and this case must be handled appropriately.However, the general approach of using a tree to control lock contention is still in place.
Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 92 +++++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-)kernel/rcu/tree.c +3
-14@@ -1682,74 +1682,52 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, {bool ret = false;struct rcu_state *rsp = rdp->rsp; -struct rcu_node *rnp_root =rcu_get_root (rsp); - -raw_lockdep_assert_held_rcu_node (rnp); - -/* If the specified GP is already known needed, return to caller. */ -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); -if (need_future_gp_element (rnp, c)) { -trace_rcu_this_gp (rnp, rdp, c,TPS ("Prestartleaf" )); - goto out; - } +struct rcu_node *rnp_root;/* -* If this rcu_node structure believes that a grace period is in -* progress, then we must wait for the one following, which is in -* "c". Because our request will be noticed at the end of the -* current grace period, we don't need to explicitly start one. +* Use funnel locking to either acquire the root rcu_node +* structure's lock or bail out if the need for this grace period kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1705
Quoted 10 lines
kernel/rcu/tree.c +5
-4+* has already been recorded -- or has already started. If there +* is already a grace period in progress in a non-leaf node, no +* recording is needed because the end of the grace period will +* scan the leaf rcu_node structures. Note that rnp->lock must +* not be released. */ -if (rnp->gpnum != rnp->completed) { -need_future_gp_element (rnp, c) = true; -trace_rcu_this_gp (rnp, rdp, c,TPS ("Startedleaf" )); - goto out;Referring to the above negative diff as [1] (which I wanted to refer to later in this message..)
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1706
Quoted 11 lines
kernel/rcu/tree.c +11
-0+raw_lockdep_assert_held_rcu_node (rnp); +trace_rcu_this_gp (rnp, rdp, c,TPS ("Startleaf" )); +for (rnp_root = rnp;1 ; rnp_root = rnp_root->parent) { +if (rnp_root != rnp) +raw_spin_lock_rcu_node (rnp_root); +if (need_future_gp_element (rnp_root, c) || +ULONG_CMP_GE (rnp_root->gpnum, c) || + (rnp != rnp_root && + rnp_root->gpnum != rnp_root->completed)) { +trace_rcu_this_gp (rnp_root, rdp, c,TPS ("Prestarted" )); + goto unlock_out;I was a bit confused about the implementation of the above for loop:
In the previous code (which I refer to in the negative diff [1]), we were checking the leaf, and if the leaf believed that RCU was not idle, then we were marking the need for the future GP and quitting this function. In the new code, it seems like even if the leaf believes RCU is not-idle, we still go all the way up the tree.
I think the big change is, in the above new for loop, we either bail of if a future GP need was already marked by an intermediate node, or we go marking up the whole tree about the need for one.
Quoted 7 lines
If a leaf believes RCU is not idle, can we not just mark the future GP need like before and return? It seems we would otherwise increase the lock contention since now we lock intermediate nodes and then finally even the root. Where as before we were not doing that if the leaf believed RCU was not idle.
I am sorry if I missed something obvious.
The trick is that we do the check before we have done the marking. So if we bailed, we would not have marked at all. If we are at an intermediate node and a grace period is in progress, we do bail.
You are right that this means that we (perhaps unnecessarily) acquire the lock of the parent
rcu_node, which might or might not be the root. And on systems with default fanout with 1024 CPUs or fewer, yes, it will be the root, and yes, this is the common case. So might be well worth improving.One way to implement the old mark-and-return approach as you suggest above would be as shown below (untested, probably doesn't build, and against current rcu/dev). What do you think?
Quoted 5 lines
The other thing is we now don't have the 'Startedleaf' trace like we did before. I sent a patch to remove it, but I think the removal of that is somehow connected to what I was just talking about.. and I was thinking if we should really remove it. Should we add the case for checking leaves only back or is that a bad thing to do?
Suppose I got hit by a bus and you were stuck with the job of debugging this. What traces would you want and where would they be? Keeping in mind that too-frequent traces have their problems as well.
(Yes, I will be trying very hard to avoid this scenario for as long as I can, but this might be a good way for you (and everyone else) to be thinking about this.)
Thanx, Paul
------------------------------------------------------------------------
Quoted 8 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1587
Quoted 8 lines
kernel/rcu/tree.c +0
-0@@ -1585,6 +1585,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, goto unlock_out; } rnp_root->gp_seq_needed = c;e
Quoted 4 lines
kernel/rcu/tree.c static bool rcu_start_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, · line 1589
Quoted 2 lines
kernel/rcu/tree.c +2
-0+if (rcu_seq_statn (rcu_seq_current (&rnp_root->gp_seq))) +if (rcu_seq_state (rcu_seq_current (&rnp_root->gp_seq)))Right... Make that
rnp->gp_seq. Memory locality and all that...Quoted 6 lines
Quoted 4 lines
Quoted 1 line
Thanx, Paul
Yes, I think this condition would be right to add. I could roll it into my clean up patch.
I already queued it, please see below.
Cool!
Quoted 6 lines
Quoted 3 lines
Also, I think its better if we split the conditions for prestarted into separate if conditions and comment them so its clear, I have started to do that in my tree.
Hmmm... Let's see how this plays out.
Sure.
Quoted 38 lines
Quoted 19 lines
If you don't mind going through the if conditions in the funnel locking loop with me, it would be quite helpful so that I don't mess the code up and would also help me add tracing correctly.
The if condition for prestarted is this:
if (need_future_gp_element(rnp_root, c) || ULONG_CMP_GE(rnp_root->gpnum, c) || (rnp != rnp_root && rnp_root->gpnum != rnp_root->completed)) { trace_rcu_this_gp(rnp_root, rdp, c, TPS("Prestarted")); goto unlock_out; need_future_gp_element(rnp_root, c) = true;As of 16/21, the heart of the loop is the above (excluding the locking bits)
In this what confuses me is the second and the third condition for pre-started.
Quoted 7 lines
The second condition is:
ULONG_CMP_GE(rnp_root->gpnum, c). AIUI the goal of this condition is to check whether the requested grace period has already started. I believe then the above check is insufficient. The reason I think its insufficient is I believe we should also check the state of the grace period to augment this check.
IMO the condition should really be:
(ULONG_CMP_GT(rnp_root->gpnum, c)||The above asks whether the -next- grace period -after- the requested one had started.
Quoted 1 line
(rnp_root->gpnum == c && rnp_root->gpnum != rnp_root->completed))This asks that the requested grace period not have completed.
What about the case where the requested grace period has completed, but the one after has not yet started? If you add that in, I bet you will have something that simplifies to my original.
Quoted 8 lines
Quoted 4 lines
In a later patch you replaced this with
rseq_done(&rnp_root->gp_seq, c)which kind of accounts for the state, except thatrseq_doneusesULONG_CMP_GE, whereas to fix this,rseq_doneIMO should be usingULONG_CMP_GTto be equivalent to the above check. Do you agree?I do not believe that I do. The
ULONG_CMP_GE()allows for the missing case where the requested grace period completed, but the following grace period has not yet started.Ok thanks that clears it up. For some reason I was thinking if
rnp_root->gpnum== c, that could means 'c' has not yet started, unless we also checked the state. Obviously, now I realize gpnum == c can only mean 2 things:
- c has started but not yet completed
- c has completedBoth of these cases should cause a bail out so I agree now with your condition
ULONG_CMP_GE, thanks.Quoted 9 lines
Quoted 8 lines
The third condition for pre-started is:
(rnp != rnp_root && rnp_root->gpnum != rnp_root->completed))This as I followed from your commit message is if an intermediate node thinks RCU is non-idle, then its not necessary to mark the tree and we can bail out since the clean up will scan the whole tree anyway. That makes sense to me but I think I will like to squash the diff in your previous email into this condition as well to handle both conditions together.
Quoted 3 lines
Please keep in mind that it is necessary to actually record the request in the leaf case. Or are you advocating use of ?: or similar to make this happen?
Yes, I realized yesterday you wanted to record it for the leaf that's why you're doing things this way. I'll let you know if I find any other ways of simplifying it once I look at your latest tree.
Btw, I checked your git tree and couldn't see the update that you mentioned you queued above. Could you push those changes?
Quoted 6 lines
Quoted 2 lines
Good point, pushed now. And the patch that I forgot to include in the last email is below.
Cool, thanks. Also one thing I wanted to discuss, I am a bit unclear about the if (
rcu_seq_done..) condition in the loop which decides if the GP requested is pre-started.
Actually, rcu_seq_done() instead determines whether or not the GP has -completed-.
Quoted 4 lines
Say c is 8 (0b1000) - i.e. gp requested is 2.
I drew some tables with some examples, the result column is what the current code will do.
Quoted 8 lines
Say
gp_seqis 12 and its not progress (0b1100),
gp_seqgp_numstate analysis ofgp_seqresult
12 3 0 gp 3 not started pre-started
(gp 2 completed)For this, the "greater than" check in
rcu_seq_donewill work because 2 already completed (The check essentially does 12 >= 8 which implies prestarted).
Agreed.
Quoted 6 lines
Say
gp_seqis 9 and it is in progress (0b1001)gp_seqgp_numstate state ofgp_seqresult
9 2 1 gp 2 in progress pre-started
(gp 1 completed)Here also the "greater than" check is correct (9 >= 8 which implies prestarted).
Yes, ->gp_seq of 9 implies that _snap() of 8 is done and gone.
Quoted 8 lines
However, say
gp_seqis 8gp_seqgp_numstate state ofgp_seqresult
8 2 0 gp 2 not started pre-started
(gp 1 completed)In this case,
rcu_seq_donewill incorrectly say that its pre-started when 2 has not yet started. For this reason, I feel the equal-to check inrcu_seq_donewill incorrectly predict prestarted.
If _snap() said 8, then it meant that when ->gp_seq reached 8, the needed grace periods had elapsed. So ULONG_CMP_GE() really is what we want.
Quoted 11 lines
I think to fix this, the
rseq_donecondition could be replaced with:if (ULONG_CMP_GT(rnp_root->gpseq, c)) { // pre-started }I believe the difference arises because one of the patches during the conversion to use
gp_seqin the tree replacedrcu_seq_donewithULONG_CMP_GE, where as such a replacement doesn't work in thegp_seqregime because of difference in the way a gp's starte/end is accounted (vs the old way).Does it make sense or was I way off about something :D ?
I believe that you need to start with where the value passed via "c" to rcu_start_this_gp() came from. I suggest starting with the call from the rcu_seq_snap() in rcu_nocb_wait_gp(), whose return value is then passed to rcu_start_this_gp(), the reason being that it doesn't drag you through the callback lists.
Thanx, Paul
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
On Sun, May 13, 2018 at 07:22:06PM -0700, Paul E. McKenney wrote:
[..]
Quoted 102 lines
Quoted 77 lines
Quoted 77 lines
Quoted 76 lines
Quoted 38 lines
Quoted 19 lines
If you don't mind going through the if conditions in the funnel locking loop with me, it would be quite helpful so that I don't mess the code up and would also help me add tracing correctly.
The if condition for prestarted is this:
if (need_future_gp_element(rnp_root, c) || ULONG_CMP_GE(rnp_root->gpnum, c) || (rnp != rnp_root && rnp_root->gpnum != rnp_root->completed)) { trace_rcu_this_gp(rnp_root, rdp, c, TPS("Prestarted")); goto unlock_out; need_future_gp_element(rnp_root, c) = true;As of 16/21, the heart of the loop is the above (excluding the locking bits)
In this what confuses me is the second and the third condition for pre-started.
Quoted 7 lines
The second condition is:
ULONG_CMP_GE(rnp_root->gpnum, c). AIUI the goal of this condition is to check whether the requested grace period has already started. I believe then the above check is insufficient. The reason I think its insufficient is I believe we should also check the state of the grace period to augment this check.
IMO the condition should really be:
(ULONG_CMP_GT(rnp_root->gpnum, c)||The above asks whether the -next- grace period -after- the requested one had started.
Quoted 1 line
(rnp_root->gpnum == c && rnp_root->gpnum != rnp_root->completed))This asks that the requested grace period not have completed.
What about the case where the requested grace period has completed, but the one after has not yet started? If you add that in, I bet you will have something that simplifies to my original.
Quoted 8 lines
Quoted 4 lines
In a later patch you replaced this with
rseq_done(&rnp_root->gp_seq, c)which kind of accounts for the state, except thatrseq_doneusesULONG_CMP_GE, whereas to fix this,rseq_doneIMO should be usingULONG_CMP_GTto be equivalent to the above check. Do you agree?I do not believe that I do. The
ULONG_CMP_GE()allows for the missing case where the requested grace period completed, but the following grace period has not yet started.Ok thanks that clears it up. For some reason I was thinking if
rnp_root->gpnum== c, that could means 'c' has not yet started, unless we also checked the state. Obviously, now I realize gpnum == c can only mean 2 things:
- c has started but not yet completed
- c has completedBoth of these cases should cause a bail out so I agree now with your condition
ULONG_CMP_GE, thanks.Quoted 9 lines
Quoted 8 lines
The third condition for pre-started is:
(rnp != rnp_root && rnp_root->gpnum != rnp_root->completed))This as I followed from your commit message is if an intermediate node thinks RCU is non-idle, then its not necessary to mark the tree and we can bail out since the clean up will scan the whole tree anyway. That makes sense to me but I think I will like to squash the diff in your previous email into this condition as well to handle both conditions together.
Quoted 3 lines
Please keep in mind that it is necessary to actually record the request in the leaf case. Or are you advocating use of ?: or similar to make this happen?
Yes, I realized yesterday you wanted to record it for the leaf that's why you're doing things this way. I'll let you know if I find any other ways of simplifying it once I look at your latest tree.
Btw, I checked your git tree and couldn't see the update that you mentioned you queued above. Could you push those changes?
Quoted 6 lines
Quoted 2 lines
Good point, pushed now. And the patch that I forgot to include in the last email is below.
Cool, thanks. Also one thing I wanted to discuss, I am a bit unclear about the if (
rcu_seq_done..) condition in the loop which decides if the GP requested is pre-started.Actually,
rcu_seq_done()instead determines whether or not the GP has -completed-.Quoted 4 lines
Say c is 8 (0b1000) - i.e. gp requested is 2.
I drew some tables with some examples, the result column is what the current code will do.Quoted 8 lines
Say
gp_seqis 12 and its not progress (0b1100),
gp_seqgp_numstate analysis ofgp_seqresult
12 3 0 gp 3 not started pre-started
(gp 2 completed)For this, the "greater than" check in
rcu_seq_donewill work because 2 already completed (The check essentially does 12 >= 8 which implies prestarted).Agreed.
Quoted 8 lines
Quoted 6 lines
Say
gp_seqis 9 and it is in progress (0b1001)gp_seqgp_numstate state ofgp_seqresult
9 2 1 gp 2 in progress pre-started
(gp 1 completed)Here also the "greater than" check is correct (9 >= 8 which implies prestarted).
Yes, ->gp_seq of 9 implies that
_snap()of 8 is done and gone.
According to the above table, I was trying to indicate that gp_seq = 9 implies, gp_num of 2 is in progress, not done. So in my view, whatever the _snap returned is in progress now (state bit is set).
Quoted 11 lines
Quoted 8 lines
However, say
gp_seqis 8gp_seqgp_numstate state ofgp_seqresult
8 2 0 gp 2 not started pre-started
(gp 1 completed)In this case,
rcu_seq_donewill incorrectly say that its pre-started when 2 has not yet started. For this reason, I feel the equal-to check inrcu_seq_donewill incorrectly predict prestarted.If
_snap()said 8, then it meant that when ->gp_seq reached 8, the needed grace periods had elapsed. SoULONG_CMP_GE()really is what we want.
I kind of don't agree still according to the below (but I'm pretty sure I'm missing something so I probably need to go through some more examples, do some more tracing etc.)
Forgetting about _snap for a second, can we not look at gp_seq independently and determine what the grace period is currently doing? In my view, if gp_seq reaches 8 (gp_num is 2) - that means that gp_num of 1 was just done. It doesn't mean 2 completed.. 2 could have either started or not yet started, we can't tell without look at the state bits... this is the part I didn't get.
rcu_seq_start only sets the state bit. rcu_seq_end increments the gp_num value.
I thought when rcu_seq_end sets the value part of gp_seq to gp_num, I thought that means that gp_num - 1 just completed. Is that not true?
Quoted 11 lines
Quoted 11 lines
I think to fix this, the
rseq_donecondition could be replaced with:if (ULONG_CMP_GT(rnp_root->gpseq, c)) { // pre-started }I believe the difference arises because one of the patches during the conversion to use
gp_seqin the tree replacedrcu_seq_donewithULONG_CMP_GE, where as such a replacement doesn't work in thegp_seqregime because of difference in the way a gp's starte/end is accounted (vs the old way).
Quoted 7 lines
Quoted 1 line
Does it make sense or was I way off about something :D ?
I believe that you need to start with where the value passed via "c" to
rcu_start_this_gp()came from. I suggest starting with the call from thercu_seq_snap()inrcu_nocb_wait_gp(), whose return value is then passed torcu_start_this_gp(), the reason being that it doesn't drag you through the callback lists.
Ok I'll try to do some more tracing / analysis and think some more following your suggestions about starting from rcu_nocb_wait_gp. Most likely I am wrong, but I am yet to convince myself about it :-(
thanks so much!
- Joel
Quoted 5 lines · On Sun, 22 Apr 2018 20:02:58 -0700 "Paul E. McKenney"…
Hello!
This series reduces lock contention on the root
rcu_nodestructure, and is also the first precursor to TBD changes to consolidate the three RCU flavors (RCU-bh, RCU-preempt, and RCU-sched) into one.
Hi Paul,
I've been running your rcu/dev branch and haven't noticed any problems yet. The irqsoff latency improvement is a little hard to measure because the scheduler, but I've tried turning balancing parameters right down and I'm yet to see any sign of RCU in traces (down to about 100us on a 176 CPU machine), so that's great.
(Not that RCU was ever the worst contributor to latency as I said, just that I noticed those couple of traces where it showed up.)
Thanks very much for the fast response, sorry I've taken a while to test.
Thanks,
Nick
Re: [tip/core/rcu,16/21] rcu: Add funnel locking to rcu_start_this_gp()
In reply to Joel FernandesQuoted 109 lines · Joel Fernandes
On Sun, May 13, 2018 at 07:22:06PM -0700, Paul E. McKenney wrote:
[..]Quoted 107 lines
Quoted 77 lines
Quoted 77 lines
Quoted 76 lines
Quoted 38 lines
Quoted 19 lines
If you don't mind going through the if conditions in the funnel locking loop with me, it would be quite helpful so that I don't mess the code up and would also help me add tracing correctly.
The if condition for prestarted is this:
if (need_future_gp_element(rnp_root, c) || ULONG_CMP_GE(rnp_root->gpnum, c) || (rnp != rnp_root && rnp_root->gpnum != rnp_root->completed)) { trace_rcu_this_gp(rnp_root, rdp, c, TPS("Prestarted")); goto unlock_out; need_future_gp_element(rnp_root, c) = true;As of 16/21, the heart of the loop is the above (excluding the locking bits)
In this what confuses me is the second and the third condition for pre-started.
Quoted 7 lines
The second condition is:
ULONG_CMP_GE(rnp_root->gpnum, c). AIUI the goal of this condition is to check whether the requested grace period has already started. I believe then the above check is insufficient. The reason I think its insufficient is I believe we should also check the state of the grace period to augment this check.
IMO the condition should really be:
(ULONG_CMP_GT(rnp_root->gpnum, c)||The above asks whether the -next- grace period -after- the requested one had started.
Quoted 1 line
(rnp_root->gpnum == c && rnp_root->gpnum != rnp_root->completed))This asks that the requested grace period not have completed.
What about the case where the requested grace period has completed, but the one after has not yet started? If you add that in, I bet you will have something that simplifies to my original.
Quoted 8 lines
Quoted 4 lines
In a later patch you replaced this with
rseq_done(&rnp_root->gp_seq, c)which kind of accounts for the state, except thatrseq_doneusesULONG_CMP_GE, whereas to fix this,rseq_doneIMO should be usingULONG_CMP_GTto be equivalent to the above check. Do you agree?I do not believe that I do. The
ULONG_CMP_GE()allows for the missing case where the requested grace period completed, but the following grace period has not yet started.Ok thanks that clears it up. For some reason I was thinking if
rnp_root->gpnum== c, that could means 'c' has not yet started, unless we also checked the state. Obviously, now I realize gpnum == c can only mean 2 things:
- c has started but not yet completed
- c has completedBoth of these cases should cause a bail out so I agree now with your condition
ULONG_CMP_GE, thanks.Quoted 9 lines
Quoted 8 lines
The third condition for pre-started is:
(rnp != rnp_root && rnp_root->gpnum != rnp_root->completed))This as I followed from your commit message is if an intermediate node thinks RCU is non-idle, then its not necessary to mark the tree and we can bail out since the clean up will scan the whole tree anyway. That makes sense to me but I think I will like to squash the diff in your previous email into this condition as well to handle both conditions together.
Quoted 3 lines
Please keep in mind that it is necessary to actually record the request in the leaf case. Or are you advocating use of ?: or similar to make this happen?
Yes, I realized yesterday you wanted to record it for the leaf that's why you're doing things this way. I'll let you know if I find any other ways of simplifying it once I look at your latest tree.
Btw, I checked your git tree and couldn't see the update that you mentioned you queued above. Could you push those changes?
Quoted 6 lines
Quoted 2 lines
Good point, pushed now. And the patch that I forgot to include in the last email is below.
Cool, thanks. Also one thing I wanted to discuss, I am a bit unclear about the if (
rcu_seq_done..) condition in the loop which decides if the GP requested is pre-started.Actually,
rcu_seq_done()instead determines whether or not the GP has -completed-.Quoted 4 lines
Say c is 8 (0b1000) - i.e. gp requested is 2.
I drew some tables with some examples, the result column is what the current code will do.Quoted 8 lines
Say
gp_seqis 12 and its not progress (0b1100),
gp_seqgp_numstate analysis ofgp_seqresult
12 3 0 gp 3 not started pre-started
(gp 2 completed)For this, the "greater than" check in
rcu_seq_donewill work because 2 already completed (The check essentially does 12 >= 8 which implies prestarted).Agreed.
Quoted 5 lines
Say
gp_seqis 9 and it is in progress (0b1001)gp_seqgp_numstate state ofgp_seqresult
9 2 1 gp 2 in progress pre-started
(gp 1 completed)
Quoted 7 lines
Quoted 3 lines
Quoted 1 line
Here also the "greater than" check is correct (9 >= 8 which implies prestarted).
Yes, ->gp_seq of 9 implies that
_snap()of 8 is done and gone.According to the above table, I was trying to indicate that
gp_seq= 9 implies,gp_numof 2 is in progress, not done. So in my view, whatever the_snapreturned is in progress now (state bit is set).
Yes, ->gp_seq of 9 implies that a grace period is in progress. But given that rcu_seq_snap() returned 8 some time in the past, the required grace period has in fact completed. Similarly, a ->gp_seq of 3248324301 would also indicate a grace period in progress, but would still indicate that the grace period indicated by a return value of 8 from rcu_seq_snap() had already completed.
Quoted 22 lines
Quoted 11 lines
Quoted 8 lines
However, say
gp_seqis 8gp_seqgp_numstate state ofgp_seqresult
8 2 0 gp 2 not started pre-started
(gp 1 completed)In this case,
rcu_seq_donewill incorrectly say that its pre-started when 2 has not yet started. For this reason, I feel the equal-to check inrcu_seq_donewill incorrectly predict prestarted.If
_snap()said 8, then it meant that when ->gp_seq reached 8, the needed grace periods had elapsed. SoULONG_CMP_GE()really is what we want.I kind of don't agree still according to the below (but I'm pretty sure I'm missing something so I probably need to go through some more examples, do some more tracing etc.)
Forgetting about
_snapfor a second, can we not look atgp_seqindependently and determine what the grace period is currently doing? In my view, ifgp_seqreaches 8 (gp_numis 2) - that means thatgp_numof 1 was just done. It doesn't mean 2 completed.. 2 could have either started or not yet started, we can't tell without look at the state bits... this is the part I didn't get.
Quoted 5 lines
rcu_seq_startonly sets the state bit.rcu_seq_endincrements thegp_numvalue.I thought when
rcu_seq_endsets the value part ofgp_seqtogp_num, I thought that means thatgp_num- 1 just completed. Is that not true?
If we are comparing a ->gp_seq value to a return value from rcu_seq_snap(), it does not make much sense to forget about rcu_seq_snap(). But let me suspend disbelief and instead tell you how I think about the ->gp_seq values.
A value of 8 says that grace period #2 has not yet started. It also says that grace period #1 has completed. In addition, it says that any grace period whose number is larger than 2 has not yet started, and further that any grace period whose number is smaller than 1 has already completed. Given a modular definition accounting for wrap, of course -- which is why ->gpwrap should be consulted when looking at rdp->gp_seq.
A value of 9 says that grace period #2 has started, but it also implies that #1 and earlier have completed (as with 8) and that #3 and later have not yet started.
So rcu_seq_snap() is given a value of ->gp_seq, and must return a later value that will indicate that a full grace period has passed. We can make a table:
->gp_seq rcu_seq_snap() return value 0 4 1 8 4 8 5 12 8 12
And so on. The point of returning 4 when ->gp_seq is zero has nothing to do with grace period #1 having completed and everything to do with grace period #0 having completed.
The values 2 and 3 cannot happen for RCU, though the value of 2 can happen for SRCU. So SRCU is why we have two state bits rather than just one.
As you say, rcu_seq_start() just increments. It also verifies that the state bits of the result are exactly 1, which means that it will complain if invoked with non-zero state bits.
Then rcu_seq_end() rounds up to the next grace period, but with the state bits all zero, indicating that this grace period has not yet started.
All of this allows rcu_seq_done() to simply do a modular comparison of the snapshot from rcu_seq_snap() to the current ->gp_seq.
Make sense?
Quoted 18 lines
Quoted 10 lines
Quoted 10 lines
I think to fix this, the
rseq_donecondition could be replaced with:if (ULONG_CMP_GT(rnp_root->gpseq, c)) { // pre-started }I believe the difference arises because one of the patches during the conversion to use
gp_seqin the tree replacedrcu_seq_donewithULONG_CMP_GE, where as such a replacement doesn't work in thegp_seqregime because of difference in the way a gp's starte/end is accounted (vs the old way).Quoted 7 lines
Quoted 1 line
Does it make sense or was I way off about something :D ?
I believe that you need to start with where the value passed via "c" to
rcu_start_this_gp()came from. I suggest starting with the call from thercu_seq_snap()inrcu_nocb_wait_gp(), whose return value is then passed torcu_start_this_gp(), the reason being that it doesn't drag you through the callback lists.
Quoted 3 lines
Ok I'll try to do some more tracing / analysis and think some more following your suggestions about starting from
rcu_nocb_wait_gp. Most likely I am wrong, but I am yet to convince myself about it :-(
But of course!
Thanx, Paul
Quoted 4 lines
thanks so much!
- Joel
Quoted 9 lines · Nicholas Piggin
Quoted 5 lines · On Sun, 22 Apr 2018 20:02:58 -0700 "Paul E. McKenney"…
Hello!
This series reduces lock contention on the root
rcu_nodestructure, and is also the first precursor to TBD changes to consolidate the three RCU flavors (RCU-bh, RCU-preempt, and RCU-sched) into one.
Quoted 7 lines
Hi Paul,
I've been running your rcu/dev branch and haven't noticed any problems yet. The irqsoff latency improvement is a little hard to measure because the scheduler, but I've tried turning balancing parameters right down and I'm yet to see any sign of RCU in traces (down to about 100us on a 176 CPU machine), so that's great.
Good to hear!!!
Quoted 5 lines
(Not that RCU was ever the worst contributor to latency as I said, just that I noticed those couple of traces where it showed up.)
Thanks very much for the fast response, sorry I've taken a while to test.
Would you be willing to give me a Tested-by on that series of patches?
Thanx, Paul
Nicholas Piggin
Tested-byQuoted 13 lines · On Mon, 14 May 2018 09:09:07 -0700 "Paul E. McKenney"…
Quoted 12 lines · Nicholas Piggin
Quoted 6 lines · On Sun, 22 Apr 2018 20:02:58 -0700 "Paul E. McKenney"…
Hello!
This series reduces lock contention on the root
rcu_nodestructure, and is also the first precursor to TBD changes to consolidate the three RCU flavors (RCU-bh, RCU-preempt, and RCU-sched) into one.Hi Paul,
Quoted 7 lines
Quoted 5 lines
I've been running your rcu/dev branch and haven't noticed any problems yet. The irqsoff latency improvement is a little hard to measure because the scheduler, but I've tried turning balancing parameters right down and I'm yet to see any sign of RCU in traces (down to about 100us on a 176 CPU machine), so that's great.
Good to hear!!!
Yep, as in, various other latencies are down to 100us, and still no sign of RCU, so RCU must be sitting somewhere below that.
Quoted 7 lines
Quoted 5 lines
(Not that RCU was ever the worst contributor to latency as I said, just that I noticed those couple of traces where it showed up.)
Thanks very much for the fast response, sorry I've taken a while to test.
Would you be willing to give me a Tested-by on that series of patches?
Yes of course, for your rcu/dev series
Let me know if you make any other changes you'd like me to test before merge.
Thanks,
Nick
Quoted 22 lines · Nicholas Piggin
Quoted 19 lines · On Mon, 14 May 2018 09:09:07 -0700 "Paul E. McKenney"…
Quoted 17 lines · Nicholas Piggin
Quoted 6 lines · On Sun, 22 Apr 2018 20:02:58 -0700 "Paul E. McKenney"…
Hello!
This series reduces lock contention on the root
rcu_nodestructure, and is also the first precursor to TBD changes to consolidate the three RCU flavors (RCU-bh, RCU-preempt, and RCU-sched) into one.Hi Paul,
I've been running your rcu/dev branch and haven't noticed any problems yet. The irqsoff latency improvement is a little hard to measure because the scheduler, but I've tried turning balancing parameters right down and I'm yet to see any sign of RCU in traces (down to about 100us on a 176 CPU machine), so that's great.
Quoted 4 lines
Quoted 1 line
Good to hear!!!
Yep, as in, various other latencies are down to 100us, and still no sign of RCU, so RCU must be sitting somewhere below that.
Sometimes you get lucky. ;-)
Quoted 11 lines
Quoted 7 lines
Quoted 5 lines
(Not that RCU was ever the worst contributor to latency as I said, just that I noticed those couple of traces where it showed up.)
Thanks very much for the fast response, sorry I've taken a while to test.
Would you be willing to give me a Tested-by on that series of patches?
Yes of course, for your rcu/dev series
Tested-by Nicholas Piggin <[email protected]>
Thank you very much!
The rcu/dev branch has been a bit dynamic of late, so I am going to apply your Tested-by up to the merge point, which is 434533a52e8d ("Merge branches 'exp.2018.04.20a', 'fixes.2018.04.30a', 'lock.2018.04.22a' and 'torture.2018.04.20a' into HEAD"). That will allow me to send my pull request, and we can work out which of the more recent commits I can apply your Tested-by to later. ;-)
Quoted 2 lines
Let me know if you make any other changes you'd like me to test before merge.
Will do, and thank you again!
There are some additional commits that should further reduce RCU's latency, but they won't be going in until the v4.19 merge window, that is, the one after this coming one.
Thanx, Paul