Callback tagging for 3.9
cover Callback tagging for 3.9
Hello!
This series contains callback-tagging patches. These were sent earlier in the idle/no-CBs series, but have been isolated as the more trustworthy part of that series.
1. Tag callback lists with the grace-period number that they are
waiting for. This change enables a number of optimizations for RCU_FAST_NO_HZ, and though it add a bit of code, it greatly simplifies RCU's callback handling.
2. Trace callback acceleration (which is when RCU notices that a
group of callbacks doesn't actually need to wait as long as it previously thought).
Thanx, Paul
b/include/trace/events/rcu.h | 6 - b/kernel/rcutree.c | 201 +++++++++++++++++++++++++++++++++++++------ b/kernel/rcutree.h | 2 3 files changed, 179 insertions(+), 30 deletions(-)
PATCH 1/2 rcu: Tag callback lists with corresponding grace-period number
From: "Paul E. McKenney" <[email protected]>
Currently, callbacks are advanced each time the corresponding CPU notices a change in its leaf rcu_node structure's ->completed value (this value counts grace-period completions). This approach has worked quite well, but with the advent of RCU_FAST_NO_HZ, we cannot count on a given CPU seeing all the grace-period completions. When a CPU misses a grace-period completion that occurs while it is in dyntick-idle mode, this will delay invocation of its callbacks.
In addition, acceleration of callbacks (when RCU realizes that a given callback need only wait until the end of the next grace period, rather than having to wait for a partial grace period followed by a full grace period) must be carried out extremely carefully. Insufficient acceleration will result in unnecessarily long grace-period latencies, while excessive acceleration will result in premature callback invocation. Changes that involve this tradeoff are therefore among the most nerve-wracking changes to RCU.
This commit therefore explicitly tags groups of callbacks with the number of the grace period that they are waiting for. This means that callback-advancement and callback-acceleration functions are idempotent, so that excessive acceleration will merely waste a few CPU cycles. This also allows a CPU to take full advantage of any grace periods that have elapsed while it has been in dyntick-idle mode. It should also enable simulataneous simplifications to and optimizations of RCU_FAST_NO_HZ.
--- kernel/rcutree.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++-------- kernel/rcutree.h | 2 + 2 files changed, 169 insertions(+), 28 deletions(-)
kernel/rcutree.c +167 -28
@@ -305,17 +305,27 @@ cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp) }/* -* Does the current CPU require a yet-as-unscheduled grace period? +* 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 int cpu_needs_another_gp (struct rcu_state *rsp,struct rcu_data *rdp) { -struct rcu_head **ntp; +int i; - ntp = rdp->nxttail[RCU_DONE_TAIL + - (ACCESS_ONCE (rsp->completed) != rdp->completed)]; -return rdp->nxttail[RCU_DONE_TAIL ] && ntp && *ntp && - !rcu_gp_in_progress (rsp); +if (rcu_gp_in_progress (rsp)) +return 0 ;/* No, a grace period is already in progress. */ +if (!rdp->nxttail[RCU_NEXT_TAIL ]) +return 0 ;/* No, this is a no-CBs (or offline) CPU. */ +if (*rdp->nxttail[RCU_NEXT_READY_TAIL ]) +return 1 ;/* Yes, this CPU has newly registered callbacks. */ +for (i =RCU_WAIT_TAIL ; i <RCU_NEXT_TAIL ; i++) +if (rdp->nxttail[i -1 ] != rdp->nxttail[i] && +ULONG_CMP_LT (ACCESS_ONCE (rsp->completed), + rdp->nxtcompleted[i])) +return 1 ;/* Yes, CBs for future grace period. */ +return 0 ;/* No grace period needed. */ }/*
@@ -1071,6 +1081,139 @@ static void init_callback_list(struct rcu_data *rdp) }/* +* Determine the value that ->completed will have at the end of the +* next subsequent grace period. This is used to tag callbacks so that +* a CPU can invoke callbacks in a timely fashion even if that CPU has +* been dyntick-idle for an extended period with callbacks under the +* influence of RCU_FAST_NO_HZ. +* +* The caller must hold rnp->lock with interrupts disabled. +*/ +static unsigned long rcu_cbs_completed (struct rcu_state *rsp, +struct rcu_node *rnp) +{ +/* +* If RCU is idle, we just wait for the next grace period. +* But we can only be sure that RCU is idle if we are looking +* at the root rcu_node structure -- otherwise, a new grace +* period might have started, but just not yet gotten around +* to initializing the current non-root rcu_node structure. +*/ +if (rcu_get_root (rsp) == rnp && rnp->gpnum == rnp->completed) +return rnp->completed +1 ; + +/* +* Otherwise, wait for a possible partial grace period and +* then the subsequent full grace period. +*/ +return rnp->completed +2 ; +} + +/* +* If there is room, assign a ->completed number to any callbacks on +* this CPU that have not already been assigned. Also accelerate any +* callbacks that were previously assigned a ->completed number that has +* since proven to be too conservative, which can happen if callbacks get +* assigned a ->completed number while RCU is idle, but with reference to +* a non-root rcu_node structure. This function is idempotent, so it does +* not hurt to call it repeatedly. +* +* The caller must hold rnp->lock with interrupts disabled. +*/ +static void rcu_accelerate_cbs (struct rcu_state *rsp,struct rcu_node *rnp, +struct rcu_data *rdp) +{ +unsigned long c; +int i; + +/* If the CPU has no callbacks, nothing to do. */ +if (!rdp->nxttail[RCU_NEXT_TAIL ] || !*rdp->nxttail[RCU_DONE_TAIL ]) +return ; + +/* +* Starting from the sublist containing the callbacks most +* recently assigned a ->completed number and working down, find the +* first sublist that is not assignable to an upcoming grace period. +* Such a sublist has something in it (first two tests) and has +* a ->completed number assigned that will complete sooner than +* the ->completed number for newly arrived callbacks (last test). +* +* The key point is that any later sublist can be assigned the +* same ->completed number as the newly arrived callbacks, which +* means that the callbacks in any of these later sublist can be +* grouped into a single sublist, whether or not they have already +* been assigned a ->completed number. +*/ + c =rcu_cbs_completed (rsp, rnp); +for (i =RCU_NEXT_TAIL -1 ; i >RCU_DONE_TAIL ; i--) +if (rdp->nxttail[i] != rdp->nxttail[i -1 ] && + !ULONG_CMP_GE (rdp->nxtcompleted[i], c)) +break ; + +/* +* If there are no sublist for unassigned callbacks, leave. +* At the same time, advance "i" one sublist, so that "i" will +* index into the sublist where all the remaining callbacks should +* be grouped into. +*/ +if (++i >=RCU_NEXT_TAIL ) +return ; + +/* +* Assign all subsequent callbacks' ->completed number to the next +* full grace period and group them all in the sublist initially +* indexed by "i". +*/ +for (; i <=RCU_NEXT_TAIL ; i++) { + rdp->nxttail[i] = rdp->nxttail[RCU_NEXT_TAIL ]; + rdp->nxtcompleted[i] = c; + } +} + +/* +* Move any callbacks whose grace period has completed to the +* RCU_DONE_TAIL sublist, then compact the remaining sublists and +* assign ->completed numbers to any callbacks in the RCU_NEXT_TAIL +* sublist. This function is idempotent, so it does not hurt to +* invoke it repeatedly. As long as it is not invoked -too- often... +* +* The caller must hold rnp->lock with interrupts disabled. +*/ +static void rcu_advance_cbs (struct rcu_state *rsp,struct rcu_node *rnp, +struct rcu_data *rdp) +{ +int i, j; + +/* If the CPU has no callbacks, nothing to do. */ +if (!rdp->nxttail[RCU_NEXT_TAIL ] || !*rdp->nxttail[RCU_DONE_TAIL ]) +return ; + +/* +* Find all callbacks whose ->completed numbers indicate that they +* are ready to invoke, and put them into the RCU_DONE_TAIL sublist. +*/ +for (i =RCU_WAIT_TAIL ; i <RCU_NEXT_TAIL ; i++) { +if (ULONG_CMP_LT (rnp->completed, rdp->nxtcompleted[i])) +break ; + rdp->nxttail[RCU_DONE_TAIL ] = rdp->nxttail[i]; + } +/* Clean up any sublist tail pointers that were misordered above. */ +for (j =RCU_WAIT_TAIL ; j < i; j++) + rdp->nxttail[j] = rdp->nxttail[RCU_DONE_TAIL ]; + +/* Copy down callbacks to fill in empty sublists. */ +for (j =RCU_WAIT_TAIL ; i <RCU_NEXT_TAIL ; i++, j++) { +if (rdp->nxttail[j] == rdp->nxttail[RCU_NEXT_TAIL ]) +break ; + rdp->nxttail[j] = rdp->nxttail[i]; + rdp->nxtcompleted[j] = rdp->nxtcompleted[i]; + } + +/* Classify any remaining callbacks. */ +rcu_accelerate_cbs (rsp, rnp, rdp); +} + +/* * Advance this CPU's callbacks, but only if the current grace period * has ended. This may be called only from the CPU to whom the rdp * belongs. In addition, the corresponding leaf rcu_node structure's
@@ -1080,12 +1223,15 @@ static void __rcu_process_gp_end(struct rcu_state *rsp, struct rcu_node *rnp, struct rcu_data *rdp) { /* Did another grace period end? */ - if (rdp->completed != rnp->completed) { + if (rdp->completed == rnp->completed) { + +/* No, so just accelerate recent callbacks. */ +rcu_accelerate_cbs (rsp, rnp, rdp); -/* Advance callbacks. No harm if list empty. */ - rdp->nxttail[RCU_DONE_TAIL] = rdp->nxttail[RCU_WAIT_TAIL]; - rdp->nxttail[RCU_WAIT_TAIL ] = rdp->nxttail[RCU_NEXT_READY_TAIL ]; - rdp->nxttail[RCU_NEXT_READY_TAIL ] = rdp->nxttail[RCU_NEXT_TAIL ]; + } else { + +/* Advance callbacks. */ +rcu_advance_cbs (rsp, rnp, rdp);/* Remember that we saw this grace-period completion. */ rdp->completed = rnp->completed;
@@ -1392,17 +1538,10 @@ rcu_start_gp(struct rcu_state *rsp, unsigned long flags)/* * Because 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. So promote all callbacks to be -* handled after the end of the next grace period. If the -* CPU is not yet aware of the end of the previous grace period, -* we need to allow for the callback advancement that will -* occur when it does become aware. Deadlock prevents us from -* making it aware at this point: We cannot acquire a leaf -* rcu_node ->lock while holding the root rcu_node ->lock. +* by the next grace period. So this is a good place to +* assign a grace period number to recently posted callbacks. */ - rdp->nxttail[RCU_NEXT_READY_TAIL ] = rdp->nxttail[RCU_NEXT_TAIL ]; -if (rdp->completed == rsp->completed) - rdp->nxttail[RCU_WAIT_TAIL ] = rdp->nxttail[RCU_NEXT_TAIL ]; +rcu_accelerate_cbs (rsp, rnp, rdp); rsp->gp_flags =RCU_GP_FLAG_INIT ;raw_spin_unlock (&rnp->lock);/* Interrupts remain disabled. */
@@ -1527,7 +1666,7 @@ rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp)* This GP can't end until cpu checks in, so all of our * callbacks can be processed during the next GP. */ - rdp->nxttail[RCU_NEXT_READY_TAIL ] = rdp->nxttail[RCU_NEXT_TAIL ]; +rcu_accelerate_cbs (rsp, rnp, rdp);rcu_report_qs_rnp (mask, rsp, rnp, flags);/* rlses rnp->lock */ }
@@ -1779,7 +1918,7 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp)long bl, count, count_lazy;int i; -/* If no callbacks are ready, just return.*/ +/* If no callbacks are ready, just return. */ if (!cpu_has_callbacks_ready_to_invoke (rdp)) {trace_rcu_batch_start (rsp->name, rdp->qlen_lazy, rdp->qlen,0 ); trace_rcu_batch_end(rsp->name, 0, !!ACCESS_ONCE(rdp->nxtlist),
@@ -2008,19 +2147,19 @@ __rcu_process_callbacks(struct rcu_state *rsp)WARN_ON_ONCE (rdp->beenonline ==0 ); -/* -* Advance callbacks in response to end of earlier grace -* period that some other CPU ended. -*/ +/* Handle the end of a grace period that some other CPU ended. */ rcu_process_gp_end (rsp, rdp);/* 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_irqsave (&rcu_get_root (rsp)->lock, flags); +raw_spin_lock (&rcu_get_root (rsp)->lock);/* irqs disabled. */ rcu_start_gp (rsp, flags);/* releases above lock */ + }else { +local_irq_restore (flags); }/* If there are callbacks ready, invoke them. */
kernel/rcutree.h +2 -0
@@ -282,6 +282,8 @@ struct rcu_data {*/ struct rcu_head *nxtlist;struct rcu_head **nxttail[RCU_NEXT_SIZE ]; +unsigned long nxtcompleted[RCU_NEXT_SIZE ]; +/* grace periods for sublists. */ long qlen_lazy;/* # of lazy queued callbacks */ long qlen;/* # of queued callbacks, incl lazy */ long qlen_last_fqs_check;
Signature · 1 line
1.7.8
PATCH 2/2 rcu: Trace callback acceleration
From: "Paul E. McKenney" <[email protected]>
This commit adds event tracing for callback acceleration to allow better tracking of callbacks through the system.
--- include/trace/events/rcu.h | 6 ++++-- kernel/rcutree.c | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-)
include/trace/events/rcu.h +4 -2
@@ -44,8 +44,10 @@ TRACE_EVENT(rcu_utilization,* of a new grace period or the end of an old grace period ("cpustart" * and "cpuend", respectively), a CPU passing through a quiescent * state ("cpuqs"), a CPU coming online or going offline ("cpuonl" -* and "cpuofl", respectively), and a CPU being kicked for being too -* long in dyntick-idle mode ("kick"). +* and "cpuofl", respectively), a CPU being kicked for being too +* long in dyntick-idle mode ("kick"), a CPU accelerating its new +* callbacks to RCU_NEXT_READY_TAIL ("AccReadyCB"), and a CPU +* accelerating its new callbacks to RCU_WAIT_TAIL ("AccWaitCB"). */ TRACE_EVENT(rcu_grace_period,
kernel/rcutree.c +6 -0
@@ -1168,6 +1168,12 @@ static void rcu_accelerate_cbs(struct rcu_state *rsp, struct rcu_node *rnp, rdp->nxttail[i] = rdp->nxttail[RCU_NEXT_TAIL ]; rdp->nxtcompleted[i] = c; } + +/* Trace depending on how much we were able to accelerate. */ +if (!*rdp->nxttail[RCU_WAIT_TAIL ]) +trace_rcu_grace_period (rsp->name, rdp->gpnum,"AccWaitCB" ); +else +trace_rcu_grace_period (rsp->name, rdp->gpnum,"AccReadyCB" ); }/*
Signature · 1 line
1.7.8
Re: [PATCH tip/core/rcu 2/2] rcu: Trace callback acceleration
You probably don't want to use --chain-reply-to; that makes patch N a reply to patch N-1 rather than to the cover letter, which creates much deeper and harder to follow threads.
- Josh Triplett