rcu/nocb updates
Subsystems (3)
Frederic Weisbecker
maintainercover rcu/nocb updates
So this set has grown further than I expected.
This addresses most reviews from Paul and also consolidates the nocb timers code.
Please mind the very first patch that is a stable bugfix.
git://git.kernel.org/pub/scm/linux/kernel/git/frederic/linux-dynticks.git
rcu/dev
HEAD: 75991420c246c26f598602da1a70947b5bdf77b6
Thanks,
Frederic
---
Frederic Weisbecker (16):
rcu/nocb: Fix potential missed nocb_timer rearm
rcu/nocb: Comment the reason behind BH disablement on batch processing rcu/nocb: Forbid NOCB toggling on offline CPUs
rcu/nocb: Only (re-)initialize segcblist when needed on CPU up rcu/nocb: Disable bypass when CPU isn't completely offloaded rcu/nocb: Avoid confusing double write of rdp->nocb_cb_sleep rcu/nocb: Rename nocb_gp_update_state to nocb_gp_update_state_deoffloading rcu/nocb: Move trace_rcu_nocb_wake() calls outside nocb_lock when possible rcu/nocb: Merge nocb_timer to the rdp leader
rcu/nocb: Directly call __wake_nocb_gp() from bypass timer rcu/nocb: Allow de-offloading rdp leader
rcu/nocb: Cancel nocb_timer upon nocb_gp wakeup
rcu/nocb: Delete bypass_timer upon nocb_gp wakeup
rcu/nocb: Only cancel nocb timer if not polling
rcu/nocb: Prepare for finegrained deferred wakeup
rcu/nocb: Unify timers
include/linux/rcu_segcblist.h | 7 +- include/trace/events/rcu.h | 1 + kernel/rcu/tree.c | 12 +- kernel/rcu/tree.h | 9 +- kernel/rcu/tree_plugin.h | 280 ++++++++++++++++++++++-------------------- 5 files changed, 163 insertions(+), 146 deletions(-)
Frederic Weisbecker
maintainerPATCH 1/16 rcu/nocb: Fix potential missed nocb_timer rearm
The "nocb_bypass_timer" ends up calling wake_nocb_gp() which deletes the pending "nocb_timer" (note they are not the same timers) for the given rdp without resetting the matching state stored in nocb_defer wakeup.
As a result, a future call_rcu() on that rdp may be fooled and think the timer is armed when it's not, missing a deferred nocb_gp wakeup.
Fix this with resetting rdp->nocb_defer_wakeup when we disarm the timer.
--- kernel/rcu/tree_plugin.h | 2 ++ 1 file changed, 2 insertions(+)
kernel/rcu/tree_plugin.h +2 -0
@@ -1705,6 +1705,8 @@ static bool wake_nocb_gp(struct rcu_data *rdp, bool force,rcu_nocb_unlock_irqrestore (rdp, flags);return false; } + + rdp->nocb_defer_wakeup =RCU_NOCB_WAKE_NOT ;del_timer (&rdp->nocb_timer);rcu_nocb_unlock_irqrestore (rdp, flags);raw_spin_lock_irqsave (&rdp_gp->nocb_gp_lock, flags);
Signature · 1 line
2.25.1
A message that is not in the archive (sent off-list or not yet received): 20210128184834.GP2743@paulmck-ThinkPad-P72
Frederic Weisbecker
maintainerRe: [PATCH 01/16] rcu/nocb: Fix potential missed nocb_timer rearm
Quoted 23 lines · Paul E. McKenney
Quoted 22 lines · Frederic Weisbecker
The "
nocb_bypass_timer" ends up callingwake_nocb_gp()which deletes the pending "nocb_timer" (note they are not the same timers) for the given rdp without resetting the matching state stored innocb_deferwakeup.As a result, a future
call_rcu()on that rdp may be fooled and think the timer is armed when it's not, missing a deferrednocb_gpwakeup.Fix this with resetting
rdp->nocb_defer_wakeupwhen we disarm the timer.Fixes d1b222c6be1f (rcu/nocb: Add bypass callback queueing)Cc Stable <[email protected]>Cc Josh Triplett <[email protected]>Cc Lai Jiangshan <[email protected]>Cc Joel Fernandes <[email protected]>Cc Neeraj Upadhyay <[email protected]>Cc Boqun Feng <[email protected]>Signed-off-by Frederic Weisbecker <[email protected]>--- kernel/rcu/tree_plugin.h | 2 ++ 1 file changed, 2 insertions(+)
Quoted 14 lines
kernel/rcu/tree_plugin.h static bool wake_nocb_gp(struct rcu_data *rdp, bool force, · line 1709
Quoted 10 lines
kernel/rcu/tree_plugin.h +2
-0@@ -1705,6 +1705,8 @@ static bool wake_nocb_gp(struct rcu_data *rdp, bool force,rcu_nocb_unlock_irqrestore (rdp, flags);return false; } + + rdp->nocb_defer_wakeup =RCU_NOCB_WAKE_NOT ;Given this change, does it make sense to remove the setting of ->nocb_defer_wakeup to
RCU_NOCB_WAKE_NOTfrom thedo_nocb_deferred_wakeup_common()function?
I do it later in "[PATCH 09/16] rcu/nocb: Merge nocb_timer to the rdp leader"
Quoted 4 lines
Does the above assignment need
to beWRITE_ONCE(), in other words, are all reads of ->nocb_defer_wakeup done with either ->nocb_lock or ->nocb_gp_lock held? (I do not believe that this is the case.)
Ah indeed it should probably be done with WRITE_ONCE() because it's read locklessly on many places.
Thanks.
Quoted 9 lines
Thanx, Paul
Quoted 6 lines
kernel/rcu/tree_plugin.h +0
-0del_timer (&rdp->nocb_timer);rcu_nocb_unlock_irqrestore (rdp, flags);raw_spin_lock_irqsave (&rdp_gp->nocb_gp_lock, flags);Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 2/16 rcu/nocb: Comment the reason behind BH disablement on batch processing
In reply to Frederic WeisbeckerExplain why we need to disable softirqs while processing callbacks in an offline fashion. The subtle reason doesn't want to be forgotten.
--- kernel/rcu/tree_plugin.h | 6 ++++++ 1 file changed, 6 insertions(+)
kernel/rcu/tree_plugin.h +6 -0
@@ -2235,6 +2235,12 @@ static void nocb_cb_wait(struct rcu_data *rdp)local_irq_save (flags);rcu_momentary_dyntick_idle ();local_irq_restore (flags); +/* +* While transitioning to/from NOCB mode, a CPU might execute the same +* callback concurrently if it requeues itself and the softirq interrupts +* the offloaded callback processing. Make sure we disable BH to prevent +* from that. +*/ local_bh_disable ();rcu_do_batch (rdp);local_bh_enable ();
Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 3/16 rcu/nocb: Forbid NOCB toggling on offline CPUs
In reply to Frederic WeisbeckerToggling the NOCB state of a CPU when it is offline imply some specific issues to handle, especially making sure that the kthreads have handled all the remaining callbacks and bypass before the corresponding CPU can be set as non-offloaded while it is offline.
To prevent from such complications, simply forbid offline CPUs to perform NOCB-mode toggling. It's a simple rule to observe and after all it doesn't make much sense to switch a non working CPU to/from offloaded state.
--- kernel/rcu/tree.c | 3 +-- kernel/rcu/tree_plugin.h | 57 +++++++++++++++------------------------- 2 files changed, 22 insertions(+), 38 deletions(-)
kernel/rcu/tree.c +1 -2
@@ -4068,8 +4068,7 @@ int rcutree_prepare_cpu(unsigned int cpu)raw_spin_unlock_rcu_node (rnp);/* irqs remain disabled. */ /* * Lock in case the CB/GP kthreads are still around handling -* old callbacks (longer term we should flush all callbacks -* before completing CPU offline) +* old callbacks. */ rcu_nocb_lock (rdp);if (rcu_segcblist_empty (&rdp->cblist))/* No early-boot CBs? */
kernel/rcu/tree_plugin.h +21 -36
108 lines not on this page · raw message
Signature · 1 line
2.25.1
Paul E. McKenney
maintainerRe: [PATCH 03/16] rcu/nocb: Forbid NOCB toggling on offline CPUs
Quoted 10 lines · Frederic Weisbecker
Toggling the NOCB state of a CPU when it is offline imply some specific issues to handle, especially making sure that the kthreads have handled all the remaining callbacks and bypass before the corresponding CPU can be set as non-offloaded while it is offline.
To prevent from such complications, simply forbid offline CPUs to perform NOCB-mode toggling. It's a simple rule to observe and after all it doesn't make much sense to switch a non working CPU to/from offloaded state.
Quoted 7 lines
Reported-by Paul E. McKenney <[email protected]>Cc Josh Triplett <[email protected]>Cc Lai Jiangshan <[email protected]>Cc Joel Fernandes <[email protected]>Cc Neeraj Upadhyay <[email protected]>Cc Boqun Feng <[email protected]>Signed-off-by Frederic Weisbecker <[email protected]>
Very nice, cuts out a world of hurt, thank you! Queued for testing and further review, the usual wordsmithing applied and the usual request for you to check to see if I messed anything up.
Thanx, Paul
------------------------------------------------------------------------
commit 4fbfeec81533e6ea9811e57cc848ee30522c0517
Author: Frederic Weisbecker <[email protected]> Date: Thu Jan 28 18:12:09 2021 +0100
rcu/nocb: Forbid NOCB toggling on offline CPUs
It makes no sense to de-offload an offline CPU because that CPU will never invoke any remaining callbacks. It also makes little sense to offload an offline CPU because any pending RCU callbacks were migrated when that CPU went offline. Yes, it is in theory possible to use a number of tricks to permit offloading and deoffloading offline CPUs in certain cases, but in practice it is far better to have the simple and deterministic rule "Toggling the offload state of an offline CPU is forbidden".
For but one example, consider that an offloaded offline CPU might have millions of callbacks queued. Best to just say "no".
This commit therefore forbids toggling of the offloaded state of offline CPUs.
kernel/rcu/tree.c +1 -2
@@ -4066,8 +4066,7 @@ int rcutree_prepare_cpu(unsigned int cpu)raw_spin_unlock_rcu_node (rnp);/* irqs remain disabled. */ /* * Lock in case the CB/GP kthreads are still around handling -* old callbacks (longer term we should flush all callbacks -* before completing CPU offline) +* old callbacks. */ rcu_nocb_lock (rdp);if (rcu_segcblist_empty (&rdp->cblist))/* No early-boot CBs? */
kernel/rcu/tree_plugin.h +21 -36
108 lines not on this page · raw message
Frederic Weisbecker
maintainerPATCH 4/16 rcu/nocb: Only (re-)initialize segcblist when needed on CPU up
In reply to Frederic WeisbeckerSimply checking if the segcblist is enabled is enough to know if we need to initialize it or not. It's safe to check within hotplug machine.
--- kernel/rcu/tree.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
kernel/rcu/tree.c +4 -5
@@ -4066,14 +4066,13 @@ int rcutree_prepare_cpu(unsigned int cpu) rdp->dynticks_nesting =1 ;/* CPU not up, no tearing. */ rcu_dynticks_eqs_online ();raw_spin_unlock_rcu_node (rnp);/* irqs remain disabled. */ +/* -* Lock in case the CB/GP kthreads are still around handling -* old callbacks. +* Only non-NOCB CPUs that didn't have early-boot callbacks need to be +* (re-)initialized. */ -rcu_nocb_lock (rdp); -if (rcu_segcblist_empty (&rdp->cblist))/* No early-boot CBs? */ +if (!rcu_segcblist_is_enabled (&rdp->cblist))rcu_segcblist_init (&rdp->cblist);/* Re-enable callbacks. */ -rcu_nocb_unlock (rdp);/* * Add CPU to leaf rcu_node pending-online bitmask. Any needed
Signature · 1 line
2.25.1
A message that is not in the archive (sent off-list or not yet received): 20210128191228.GQ2743@paulmck-ThinkPad-P72
Frederic Weisbecker
maintainerRe: [PATCH 04/16] rcu/nocb: Only (re-)initialize segcblist when needed on CPU up
Quoted 25 lines · Paul E. McKenney
Quoted 10 lines · Frederic Weisbecker
Simply checking if the segcblist is enabled is enough to know if we need to initialize it or not. It's safe to check within hotplug machine.
Signed-off-by Frederic Weisbecker <[email protected]>Cc Josh Triplett <[email protected]>Cc Lai Jiangshan <[email protected]>Cc Joel Fernandes <[email protected]>Cc Neeraj Upadhyay <[email protected]>Cc Boqun Feng <[email protected]>Hmmm...
At the start of a CPU-hotplug operation, an incoming CPU's callback list can be in a number of states:
1. Disabled and empty. This is the case when the boot CPU has
not donecall_rcu(), when a non-boot CPU first comes online, and when a non-offloaded CPU comes back online. In this case, it is permissible to initialize ->cblist. Because either the CPU is currently running with interrupts disabled (boot CPU) or is not yet running at all (other CPUs), it is not necessary to acquire ->nocb_lock.
Quoted 4 lines
2. Disabled and non-empty. This is the case when the boot CPU has
donecall_rcu(). It is not permissible to initialize ->cblist because doing so will leak any callbacks posted by early boot invocations ofcall_rcu().
I don't think that's possible. In this case __call_rcu() has called rcu_segcblist_init() and has enabled the segcblist.
Quoted 5 lines
Test for the possibility of leaking by building with
CONFIG_PROVE_RCU=yand booting with rcupdate.rcu_self_test=1.3. Enabled, whether empty or not. This is the case when an
Quoted 8 lines
offloaded CPU comes back online. This is the only case where the ->nocb_lock must be held to modify ->cblist. However, it is not necessarily to modify ->cblist because the rcuoc kthread is on the job.So I believe that it is necessary to check for both disabled and empty. But don't take my word for it! Build with
CONFIG_PROVE_RCU=yand boot with rcupdate.rcu_self_test=1. ;-)
I'm trying that :-)
Paul E. McKenney
maintainerRe: [PATCH 04/16] rcu/nocb: Only (re-)initialize segcblist when needed on CPU up
Quoted 26 lines · Frederic Weisbecker
Quoted 25 lines · Paul E. McKenney
Quoted 10 lines · Frederic Weisbecker
Simply checking if the segcblist is enabled is enough to know if we need to initialize it or not. It's safe to check within hotplug machine.
Signed-off-by Frederic Weisbecker <[email protected]>Cc Josh Triplett <[email protected]>Cc Lai Jiangshan <[email protected]>Cc Joel Fernandes <[email protected]>Cc Neeraj Upadhyay <[email protected]>Cc Boqun Feng <[email protected]>Hmmm...
At the start of a CPU-hotplug operation, an incoming CPU's callback list can be in a number of states:
1. Disabled and empty. This is the case when the boot CPU has
not donecall_rcu(), when a non-boot CPU first comes online, and when a non-offloaded CPU comes back online. In this case, it is permissible to initialize ->cblist. Because either the CPU is currently running with interrupts disabled (boot CPU) or is not yet running at all (other CPUs), it is not necessary to acquire ->nocb_lock.
Quoted 7 lines
Quoted 4 lines
2. Disabled and non-empty. This is the case when the boot CPU has
donecall_rcu(). It is not permissible to initialize ->cblist because doing so will leak any callbacks posted by early boot invocations ofcall_rcu().I don't think that's possible. In this case
__call_rcu()has calledrcu_segcblist_init()and has enabled the segcblist.
You are right, rcu_segcblist_init() would have been called in that case and it has: rcu_segcblist_set_flags(rsclp, SEGCBLIST_ENABLED).
Quoted 9 lines
Quoted 9 lines
Test for the possibility of leaking by building with
CONFIG_PROVE_RCU=yand booting with rcupdate.rcu_self_test=1.3. Enabled, whether empty or not. This is the case when an
offloaded CPU comes back online. This is the only case where the ->nocb_lock must be held to modify ->cblist. However, it is not necessarily to modify ->cblist because the rcuoc kthread is on the job.
Quoted 5 lines
Quoted 3 lines
So I believe that it is necessary to check for both disabled and empty. But don't take my word for it! Build with
CONFIG_PROVE_RCU=yand boot with rcupdate.rcu_self_test=1. ;-)I'm trying that :-)
Even better!
Thanx, Paul
Paul E. McKenney
maintainerRe: [PATCH 04/16] rcu/nocb: Only (re-)initialize segcblist when needed on CPU up
Quoted 47 lines · Paul E. McKenney
Quoted 26 lines · Frederic Weisbecker
Quoted 25 lines · Paul E. McKenney
Quoted 10 lines · Frederic Weisbecker
Simply checking if the segcblist is enabled is enough to know if we need to initialize it or not. It's safe to check within hotplug machine.
Signed-off-by Frederic Weisbecker <[email protected]>Cc Josh Triplett <[email protected]>Cc Lai Jiangshan <[email protected]>Cc Joel Fernandes <[email protected]>Cc Neeraj Upadhyay <[email protected]>Cc Boqun Feng <[email protected]>Hmmm...
At the start of a CPU-hotplug operation, an incoming CPU's callback list can be in a number of states:
1. Disabled and empty. This is the case when the boot CPU has
not donecall_rcu(), when a non-boot CPU first comes online, and when a non-offloaded CPU comes back online. In this case, it is permissible to initialize ->cblist. Because either the CPU is currently running with interrupts disabled (boot CPU) or is not yet running at all (other CPUs), it is not necessary to acquire ->nocb_lock.Quoted 7 lines
Quoted 4 lines
2. Disabled and non-empty. This is the case when the boot CPU has
donecall_rcu(). It is not permissible to initialize ->cblist because doing so will leak any callbacks posted by early boot invocations ofcall_rcu().I don't think that's possible. In this case
__call_rcu()has calledrcu_segcblist_init()and has enabled the segcblist.You are right,
rcu_segcblist_init()would have been called in that case and it has:rcu_segcblist_set_flags(rsclp, SEGCBLIST_ENABLED).Quoted 9 lines
Quoted 9 lines
Test for the possibility of leaking by building with
CONFIG_PROVE_RCU=yand booting with rcupdate.rcu_self_test=1.3. Enabled, whether empty or not. This is the case when an
offloaded CPU comes back online. This is the only case where the ->nocb_lock must be held to modify ->cblist. However, it is not necessarily to modify ->cblist because the rcuoc kthread is on the job.
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
So I believe that it is necessary to check for both disabled and empty. But don't take my word for it! Build with
CONFIG_PROVE_RCU=yand boot with rcupdate.rcu_self_test=1. ;-)I'm trying that :-)
Even better!
I applied this patch (with the usual wordsmithing as shown below), then ran this:
tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration 2 --configs "TREE05" --bootargs "rcu_nocbs=0-7" --trust-make
The resulting console.log file says "Running RCU self tests" and the test runs to completion without complaint. So good show!
Thanx, Paul
------------------------------------------------------------------------
commit 0a43799de756a486e45eba8d9d4286a577e746cd
Author: Frederic Weisbecker <[email protected]> Date: Thu Jan 28 18:12:10 2021 +0100
rcu/nocb: Only (re-)initialize segcblist when needed on CPU up
At the start of a CPU-hotplug operation, the incoming CPU's callback list can be in a number of states:
1. Disabled and empty. This is the case when the boot CPU has
not invoked call_rcu(), when a non-boot CPU first comes online, and when a non-offloaded CPU comes back online. In this case, it is both necessary and permissible to initialize ->cblist. Because either the CPU is currently running with interrupts disabled (boot CPU) or is not yet running at all (other CPUs), it is not necessary to acquire ->nocb_lock.
In this case, initialization is required.
2. Disabled and non-empty. This cannot occur, because early boot
call_rcu() invocations enable the callback list before enqueuing their callback.
3. Enabled, whether empty or not. In this case, the callback
list has already been initialized. This case occurs when the boot CPU has executed an early boot call_rcu() and also when an offloaded CPU comes back online. In both cases, there is no need to initialize the callback list: In the boot-CPU case, the CPU has not (yet) gone offline, and in the offloaded case, the rcuo kthreads are taking care of business.
Because it is not necessary to initialize the callback list, it is also not necessary to acquire ->nocb_lock.
Therefore, checking if the segcblist is enabled suffices. This commit therefore initializes the callback list at rcutree_prepare_cpu() time only if that list is disabled.
kernel/rcu/tree.c +4 -5
@@ -4064,14 +4064,13 @@ int rcutree_prepare_cpu(unsigned int cpu) rdp->dynticks_nesting =1 ;/* CPU not up, no tearing. */ rcu_dynticks_eqs_online ();raw_spin_unlock_rcu_node (rnp);/* irqs remain disabled. */ +/* -* Lock in case the CB/GP kthreads are still around handling -* old callbacks. +* Only non-NOCB CPUs that didn't have early-boot callbacks need to be +* (re-)initialized. */ -rcu_nocb_lock (rdp); -if (rcu_segcblist_empty (&rdp->cblist))/* No early-boot CBs? */ +if (!rcu_segcblist_is_enabled (&rdp->cblist))rcu_segcblist_init (&rdp->cblist);/* Re-enable callbacks. */ -rcu_nocb_unlock (rdp);/* * Add CPU to leaf rcu_node pending-online bitmask. Any needed
Frederic Weisbecker
maintainerPATCH 5/16 rcu/nocb: Disable bypass when CPU isn't completely offloaded
In reply to Frederic WeisbeckerInstead of flushing bypass at the very last moment in the deoffloading process, just disable bypass enqueue at soon as we start the deoffloading process and flush the pending bypass early. It's less fragile and we leave some time to the kthreads and softirqs to process quietly.
Symmetrically, only enable bypass once we safely complete the offloading process.
--- include/linux/rcu_segcblist.h | 7 ++++--- kernel/rcu/tree_plugin.h | 31 +++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 11 deletions(-)
include/linux/rcu_segcblist.h +4 -3
@@ -109,7 +109,7 @@ struct rcu_cblist {* | SEGCBLIST_KTHREAD_GP | * | | * | Kthreads handle callbacks holding nocb_lock, local rcu_core() stops | -* | handling callbacks. | +* | handling callbacks. Allow bypass enqueue. | * ---------------------------------------------------------------------------- */
@@ -125,7 +125,7 @@ struct rcu_cblist {* | SEGCBLIST_KTHREAD_GP | * | | * | CB/GP kthreads handle callbacks holding nocb_lock, local rcu_core() | -* | ignores callbacks. | +* | ignores callbacks. Bypass enqueue is enabled. | * ---------------------------------------------------------------------------- * | * v
@@ -134,7 +134,8 @@ struct rcu_cblist {* | SEGCBLIST_KTHREAD_GP | * | | * | CB/GP kthreads and local rcu_core() handle callbacks concurrently | -* | holding nocb_lock. Wake up CB and GP kthreads if necessary. | +* | holding nocb_lock. Wake up CB and GP kthreads if necessary. Disable | +* | bypass enqueue. | * ---------------------------------------------------------------------------- * | * v
kernel/rcu/tree_plugin.h +23 -8
@@ -1825,11 +1825,22 @@ static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,unsigned long j = jiffies;long ncbs =rcu_cblist_n_cbs (&rdp->nocb_bypass); +lockdep_assert_irqs_disabled (); + +// Pure softirq/rcuc based processing: no bypassing, no +// locking. if (!rcu_rdp_is_offloaded (rdp)) { + *was_alldone = !rcu_segcblist_pend_cbs (&rdp->cblist); +return false; + } + +// In the process of (de-)offloading: no bypassing, but +// locking. +if (!rcu_segcblist_completely_offloaded (&rdp->cblist)) { +rcu_nocb_lock (rdp); *was_alldone = !rcu_segcblist_pend_cbs (&rdp->cblist);return false;/* Not offloaded, no bypassing. */ } -lockdep_assert_irqs_disabled ();// Don't use ->nocb_bypass during early boot. if (rcu_scheduler_active !=RCU_SCHEDULER_RUNNING ) {
@@ -2412,6 +2423,7 @@ static long rcu_nocb_rdp_deoffload(void *arg)rcu_nocb_lock_irqsave (rdp, flags); +WARN_ON_ONCE (!rcu_nocb_flush_bypass (rdp,NULL , jiffies)); ret =rdp_offload_toggle (rdp, false, flags);swait_event_exclusive (rdp->nocb_state_wq, !rcu_segcblist_test_flags(cblist, SEGCBLIST_KTHREAD_CB |
@@ -2422,19 +2434,22 @@ static long rcu_nocb_rdp_deoffload(void *arg)rcu_nocb_unlock_irqrestore (rdp, flags);del_timer_sync (&rdp->nocb_timer); +/* Sanity check */ +WARN_ON_ONCE (rcu_cblist_n_cbs (&rdp->nocb_bypass)); +/* -* Flush bypass. While IRQs are disabled and once we set -* SEGCBLIST_SOFTIRQ_ONLY, no callback is supposed to be -* enqueued on bypass. +* Lock one last time so we see latest updates from kthreads and timer +* so that we can later run callbacks locally without the lock. */ rcu_nocb_lock_irqsave (rdp, flags); -rcu_nocb_flush_bypass (rdp,NULL , jiffies); +/* +* Theoretically we could set SEGCBLIST_SOFTIRQ_ONLY after the nocb +* LOCK/UNLOCK but let's be paranoid. +*/ rcu_segcblist_set_flags (cblist,SEGCBLIST_SOFTIRQ_ONLY );/* * With SEGCBLIST_SOFTIRQ_ONLY, we can't use -* rcu_nocb_unlock_irqrestore() anymore. Theoretically we -* could set SEGCBLIST_SOFTIRQ_ONLY with cb unlocked and IRQs -* disabled now, but let's be paranoid. +* rcu_nocb_unlock_irqrestore() anymore. */ raw_spin_unlock_irqrestore (&rdp->nocb_lock, flags);
Signature · 1 line
2.25.1
Paul E. McKenney
maintainerRe: [PATCH 05/16] rcu/nocb: Disable bypass when CPU isn't completely offloaded
Quoted 8 lines · Frederic Weisbecker
Instead of flushing bypass at the very last moment in the deoffloading process, just disable bypass enqueue at soon as we start the deoffloading process and flush the pending bypass early. It's less fragile and we leave some time to the kthreads and softirqs to process quietly.
Symmetrically, only enable bypass once we safely complete the offloading process.
Quoted 7 lines
Reported-by Paul E. McKenney <[email protected]>Cc Josh Triplett <[email protected]>Cc Lai Jiangshan <[email protected]>Cc Joel Fernandes <[email protected]>Cc Neeraj Upadhyay <[email protected]>Cc Boqun Feng <[email protected]>Signed-off-by Frederic Weisbecker <[email protected]>
Looks plausible, thank you! Some questions and comments below.
Quoted 10 lines
--- include/linux/rcu_segcblist.h | 7 ++++--- kernel/rcu/tree_plugin.h | 31 +++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 11 deletions(-)include/linux/rcu_segcblist.h +0
-0
include/linux/rcu_segcblist.h struct rcu_cblist { · line 112
Quoted 5 lines
include/linux/rcu_segcblist.h +1
-1@@ -109,7 +109,7 @@ struct rcu_cblist {* | SEGCBLIST_KTHREAD_GP | * | | * | Kthreads handle callbacks holding nocb_lock, local rcu_core() stops | -* | handling callbacks. | +* | handling callbacks. Allow bypass enqueue. |
"Allow bypass enqueue" as in bypass was disabled and entering this state causes it to be enabled, correct? If so, "Enable bypass queueing" would be less ambiguous and would match the change below.
Quoted 51 lines
include/linux/rcu_segcblist.h +3
-2* ---------------------------------------------------------------------------- */ @@ -125,7 +125,7 @@ struct rcu_cblist {* | SEGCBLIST_KTHREAD_GP | * | | * | CB/GP kthreads handle callbacks holding nocb_lock, local rcu_core() | -* | ignores callbacks. | +* | ignores callbacks. Bypass enqueue is enabled. | * ---------------------------------------------------------------------------- * | * v @@ -134,7 +134,8 @@ struct rcu_cblist {* | SEGCBLIST_KTHREAD_GP | * | | * | CB/GP kthreads and local rcu_core() handle callbacks concurrently | -* | holding nocb_lock. Wake up CB and GP kthreads if necessary. | +* | holding nocb_lock. Wake up CB and GP kthreads if necessary. Disable | +* | bypass enqueue. | * ---------------------------------------------------------------------------- * | * v kernel/rcu/tree_plugin.h +12
-1@@ -1825,11 +1825,22 @@ static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,unsigned long j = jiffies;long ncbs =rcu_cblist_n_cbs (&rdp->nocb_bypass); +lockdep_assert_irqs_disabled (); + +// Pure softirq/rcuc based processing: no bypassing, no +// locking. if (!rcu_rdp_is_offloaded (rdp)) { + *was_alldone = !rcu_segcblist_pend_cbs (&rdp->cblist); +return false; + } + +// In the process of (de-)offloading: no bypassing, but +// locking. +if (!rcu_segcblist_completely_offloaded (&rdp->cblist)) { +rcu_nocb_lock (rdp); *was_alldone = !rcu_segcblist_pend_cbs (&rdp->cblist);return false;/* Not offloaded, no bypassing. */ } -lockdep_assert_irqs_disabled ();// Don't use ->nocb_bypass during early boot. if (rcu_scheduler_active !=RCU_SCHEDULER_RUNNING ) {
kernel/rcu/tree_plugin.h static long rcu_nocb_rdp_deoffload(void *arg) · line 2426
Quoted 4 lines
kernel/rcu/tree_plugin.h +1
-0@@ -2412,6 +2423,7 @@ static long rcu_nocb_rdp_deoffload(void *arg)rcu_nocb_lock_irqsave (rdp, flags); +WARN_ON_ONCE (!rcu_nocb_flush_bypass (rdp,NULL , jiffies));
This flush suffices because we are running on the target CPU holding ->nocb_lock (thus having interrupts disabled), and because rdp_offload_toggle() invokes rcu_segcblist_offload(), which clears SEGCBLIST_OFFLOADED. Thus future calls to rcu_segcblist_completely_offloaded() will return false, which means that future calls to rcu_nocb_try_bypass() will refuse to put anything into the bypass.
I believe that this deserves a comment, particularly if I am confused about what is really happening here. ;-)
On another topic, since I saw it along the way...
The header comment for rcu_segcblist_offload() says that the structure must be empty, but that isn't really the case, is it?
Quoted 5 lines
kernel/rcu/tree_plugin.h +0
-0ret =rdp_offload_toggle (rdp, false, flags);swait_event_exclusive (rdp->nocb_state_wq, !rcu_segcblist_test_flags(cblist, SEGCBLIST_KTHREAD_CB |@@ -2422,19 +2434,22 @@ static long rcu_nocb_rdp_deoffload(void *arg)rcu_nocb_unlock_irqrestore (rdp, flags);
kernel/rcu/tree_plugin.h static long rcu_nocb_rdp_deoffload(void *arg) · line 2441
Quoted 10 lines
kernel/rcu/tree_plugin.h +4
-3del_timer_sync (&rdp->nocb_timer); +/* Sanity check */ +WARN_ON_ONCE (rcu_cblist_n_cbs (&rdp->nocb_bypass)); +/* -* Flush bypass. While IRQs are disabled and once we set -* SEGCBLIST_SOFTIRQ_ONLY, no callback is supposed to be -* enqueued on bypass. +* Lock one last time so we see latest updates from kthreads and timer
You lost me here. What updates are we seeing from kthreads and timers?
kernel/rcu/tree_plugin.h static long rcu_nocb_rdp_deoffload(void *arg) · line 2449
Quoted 9 lines
kernel/rcu/tree_plugin.h +5
-1+* so that we can later run callbacks locally without the lock. */ rcu_nocb_lock_irqsave (rdp, flags); -rcu_nocb_flush_bypass (rdp,NULL , jiffies); +/* +* Theoretically we could set SEGCBLIST_SOFTIRQ_ONLY after the nocb +* LOCK/UNLOCK but let's be paranoid. +*/ rcu_segcblist_set_flags (cblist,SEGCBLIST_SOFTIRQ_ONLY );
As long as we are being paranoid, should we also check that the bypass remains empty?
Quoted 12 lines
kernel/rcu/tree_plugin.h +1
-3/* * With SEGCBLIST_SOFTIRQ_ONLY, we can't use -* rcu_nocb_unlock_irqrestore() anymore. Theoretically we -* could set SEGCBLIST_SOFTIRQ_ONLY with cb unlocked and IRQs -* disabled now, but let's be paranoid. +* rcu_nocb_unlock_irqrestore() anymore. */ raw_spin_unlock_irqrestore (&rdp->nocb_lock, flags);Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerRe: [PATCH 05/16] rcu/nocb: Disable bypass when CPU isn't completely offloaded
Quoted 6 lines · Paul E. McKenney
Quoted 5 lines · Frederic Weisbecker
--- include/linux/rcu_segcblist.h | 7 ++++--- kernel/rcu/tree_plugin.h | 31 +++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 11 deletions(-)
Quoted 13 lines
include/linux/rcu_segcblist.h struct rcu_cblist { · line 112
Quoted 10 lines
include/linux/rcu_segcblist.h +1
-1@@ -109,7 +109,7 @@ struct rcu_cblist {* | SEGCBLIST_KTHREAD_GP | * | | * | Kthreads handle callbacks holding nocb_lock, local rcu_core() stops | -* | handling callbacks. | +* | handling callbacks. Allow bypass enqueue. | "Allow bypass enqueue" as in bypass was disabled and entering this state causes it to be enabled, correct?
Right.
Quoted 2 lines
If so, "Enable bypass
queueing" would be less ambiguous and would match the change below.
Ok I'll fix that.
Quoted 4 lines
Quoted 4 lines
include/linux/rcu_segcblist.h +0
-0@@ -2412,6 +2423,7 @@ static long rcu_nocb_rdp_deoffload(void *arg)rcu_nocb_lock_irqsave (rdp, flags);
Quoted 9 lines
include/linux/rcu_segcblist.h static long rcu_nocb_rdp_deoffload(void *arg) · line 2426
Quoted 1 line
include/linux/rcu_segcblist.h +1
-0+WARN_ON_ONCE (!rcu_nocb_flush_bypass (rdp,NULL , jiffies));This flush suffices because we are running on the target CPU holding ->nocb_lock (thus having interrupts disabled), and because
rdp_offload_toggle()invokesrcu_segcblist_offload(), which clearsSEGCBLIST_OFFLOADED. Thus future calls torcu_segcblist_completely_offloaded()will return false, which means that future calls torcu_nocb_try_bypass()will refuse to put anything into the bypass.
Exactly!
Quoted 2 lines
I believe that this deserves a comment, particularly if I am confused about what is really happening here. ;-)
Yes indeed I've been greedy there, will comment this :o)
Quoted 5 lines
On another topic, since I saw it along the way...
The header comment for
rcu_segcblist_offload()says that the structure must be empty, but that isn't really the case, is it?
Ah strange indeed, must be a leftover. I'll remove it.
Quoted 8 lines
Quoted 8 lines
include/linux/rcu_segcblist.h +0
-0ret =rdp_offload_toggle (rdp, false, flags);swait_event_exclusive (rdp->nocb_state_wq, !rcu_segcblist_test_flags(cblist, SEGCBLIST_KTHREAD_CB |@@ -2422,19 +2434,22 @@ static long rcu_nocb_rdp_deoffload(void *arg)rcu_nocb_unlock_irqrestore (rdp, flags);del_timer_sync (&rdp->nocb_timer);
Quoted 10 lines
include/linux/rcu_segcblist.h static long rcu_nocb_rdp_deoffload(void *arg) · line 2441
Quoted 8 lines
include/linux/rcu_segcblist.h +4
-3+/* Sanity check */ +WARN_ON_ONCE (rcu_cblist_n_cbs (&rdp->nocb_bypass)); +/* -* Flush bypass. While IRQs are disabled and once we set -* SEGCBLIST_SOFTIRQ_ONLY, no callback is supposed to be -* enqueued on bypass. +* Lock one last time so we see latest updates from kthreads and timer You lost me here. What updates are we seeing from kthreads and timers?
We want to make sure that, whatever change has been made on the segcblist by kthreads such as length or callbacks dequeue, this is visible on the current CPU. The swait_event_exclusive() doesn't guarantee that everything from the kthreads is visible here as the flags are checked lockless and I haven't added specific barriers.
That said right after the swait_event there is a nocb_lock LOCK/UNLOCK cycle to disable the timer, so that's enough for the local CPU to see those updates. What remains is the updates made by pending timers flushed in del_timer_sync(). There is nothing special there to be visible here but out of paranoia...
In fact this matters later in the series as the above timer disablement and flush will disappear and the LOCK/UNLOCK cycle that comes along.
Quoted 13 lines
include/linux/rcu_segcblist.h static long rcu_nocb_rdp_deoffload(void *arg) · line 2449
Quoted 10 lines
include/linux/rcu_segcblist.h +5
-1+* so that we can later run callbacks locally without the lock. */ rcu_nocb_lock_irqsave (rdp, flags); -rcu_nocb_flush_bypass (rdp,NULL , jiffies); +/* +* Theoretically we could set SEGCBLIST_SOFTIRQ_ONLY after the nocb +* LOCK/UNLOCK but let's be paranoid. +*/ rcu_segcblist_set_flags (cblist,SEGCBLIST_SOFTIRQ_ONLY );As long as we are being paranoid, should we also check that the bypass remains empty?
You missed it, check above for sanity check :-)
Thanks.
Quoted 13 lines
Quoted 13 lines
include/linux/rcu_segcblist.h +1
-3/* * With SEGCBLIST_SOFTIRQ_ONLY, we can't use -* rcu_nocb_unlock_irqrestore() anymore. Theoretically we -* could set SEGCBLIST_SOFTIRQ_ONLY with cb unlocked and IRQs -* disabled now, but let's be paranoid. +* rcu_nocb_unlock_irqrestore() anymore. */ raw_spin_unlock_irqrestore (&rdp->nocb_lock, flags);Signature · 1 line
2.25.1
Paul E. McKenney
maintainerRe: [PATCH 05/16] rcu/nocb: Disable bypass when CPU isn't completely offloaded
Quoted 42 lines · Frederic Weisbecker
Quoted 6 lines · Paul E. McKenney
Quoted 5 lines · Frederic Weisbecker
--- include/linux/rcu_segcblist.h | 7 ++++--- kernel/rcu/tree_plugin.h | 31 +++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 11 deletions(-)Quoted 13 lines
include/linux/rcu_segcblist.h struct rcu_cblist { · line 112
Quoted 10 lines
include/linux/rcu_segcblist.h +1
-1@@ -109,7 +109,7 @@ struct rcu_cblist {* | SEGCBLIST_KTHREAD_GP | * | | * | Kthreads handle callbacks holding nocb_lock, local rcu_core() stops | -* | handling callbacks. | +* | handling callbacks. Allow bypass enqueue. | "Allow bypass enqueue" as in bypass was disabled and entering this state causes it to be enabled, correct?
Right.
Quoted 2 lines
If so, "Enable bypass
queueing" would be less ambiguous and would match the change below.Ok I'll fix that.
Quoted 4 lines
Quoted 4 lines
include/linux/rcu_segcblist.h +0
-0@@ -2412,6 +2423,7 @@ static long rcu_nocb_rdp_deoffload(void *arg)rcu_nocb_lock_irqsave (rdp, flags);Quoted 9 lines
include/linux/rcu_segcblist.h static long rcu_nocb_rdp_deoffload(void *arg) · line 2426
Quoted 1 line
include/linux/rcu_segcblist.h +1
-0+WARN_ON_ONCE (!rcu_nocb_flush_bypass (rdp,NULL , jiffies));This flush suffices because we are running on the target CPU holding ->nocb_lock (thus having interrupts disabled), and because
rdp_offload_toggle()invokesrcu_segcblist_offload(), which clearsSEGCBLIST_OFFLOADED. Thus future calls torcu_segcblist_completely_offloaded()will return false, which means that future calls torcu_nocb_try_bypass()will refuse to put anything into the bypass.
Quoted 1 line
Exactly!
Whew! ;-)
Quoted 4 lines
Quoted 2 lines
I believe that this deserves a comment, particularly if I am confused about what is really happening here. ;-)
Yes indeed I've been greedy there, will comment this :o)
Very good!
Quoted 6 lines
Quoted 4 lines
On another topic, since I saw it along the way...
The header comment for
rcu_segcblist_offload()says that the structure must be empty, but that isn't really the case, is it?Ah strange indeed, must be a leftover. I'll remove it.
I should have spotted it earlier, shouldn't I have? ;-)
Quoted 24 lines
Quoted 7 lines
Quoted 7 lines
include/linux/rcu_segcblist.h +0
-0ret =rdp_offload_toggle (rdp, false, flags);swait_event_exclusive (rdp->nocb_state_wq, !rcu_segcblist_test_flags(cblist, SEGCBLIST_KTHREAD_CB |@@ -2422,19 +2434,22 @@ static long rcu_nocb_rdp_deoffload(void *arg)rcu_nocb_unlock_irqrestore (rdp, flags);del_timer_sync (&rdp->nocb_timer);Quoted 10 lines
include/linux/rcu_segcblist.h static long rcu_nocb_rdp_deoffload(void *arg) · line 2441
Quoted 8 lines
include/linux/rcu_segcblist.h +4
-3+/* Sanity check */ +WARN_ON_ONCE (rcu_cblist_n_cbs (&rdp->nocb_bypass)); +/* -* Flush bypass. While IRQs are disabled and once we set -* SEGCBLIST_SOFTIRQ_ONLY, no callback is supposed to be -* enqueued on bypass. +* Lock one last time so we see latest updates from kthreads and timer You lost me here. What updates are we seeing from kthreads and timers?
We want to make sure that, whatever change has been made on the segcblist by kthreads such as length or callbacks dequeue, this is visible on the current CPU. The
swait_event_exclusive()doesn't guarantee that everything from the kthreads is visible here as the flags are checked lockless and I haven't added specific barriers.
Quoted 7 lines
That said right after the
swait_eventthere is anocb_lockLOCK/UNLOCK cycle to disable the timer, so that's enough for the local CPU to see those updates. What remains is the updates made by pending timers flushed indel_timer_sync(). There is nothing special there to be visible here but out of paranoia...In fact this matters later in the series as the above timer disablement and flush will disappear and the LOCK/UNLOCK cycle that comes along.
OK, so the point is that any future manipulations of this callback list will see the desired stable state, correct?
Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
include/linux/rcu_segcblist.h +1
-1+* so that we can later run callbacks locally without the lock. */ rcu_nocb_lock_irqsave (rdp, flags); -rcu_nocb_flush_bypass (rdp,NULL , jiffies);Quoted 8 lines
include/linux/rcu_segcblist.h static long rcu_nocb_rdp_deoffload(void *arg) · line 2449
Quoted 5 lines
include/linux/rcu_segcblist.h +4
-0+/* +* Theoretically we could set SEGCBLIST_SOFTIRQ_ONLY after the nocb +* LOCK/UNLOCK but let's be paranoid. +*/ rcu_segcblist_set_flags (cblist,SEGCBLIST_SOFTIRQ_ONLY );As long as we are being paranoid, should we also check that the bypass remains empty?
Quoted 1 line
You missed it, check above for sanity check :-)
I did see that, but... Why not place it as late as possible, like just before releasing the ->nocb_lock? Or is there some way that a callback can sneak into the bypass list after the sanity check but before ->nocb_lock is acquired?
Thanx, Paul
Quoted 15 lines
Thanks.
Quoted 13 lines
Quoted 13 lines
include/linux/rcu_segcblist.h +1
-3/* * With SEGCBLIST_SOFTIRQ_ONLY, we can't use -* rcu_nocb_unlock_irqrestore() anymore. Theoretically we -* could set SEGCBLIST_SOFTIRQ_ONLY with cb unlocked and IRQs -* disabled now, but let's be paranoid. +* rcu_nocb_unlock_irqrestore() anymore. */ raw_spin_unlock_irqrestore (&rdp->nocb_lock, flags);Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 6/16 rcu/nocb: Avoid confusing double write of rdp->nocb_cb_sleep
In reply to Frederic Weisbeckerrdp->nocb_cb_sleep is first set to true by default after processing the callbacks then set back to false if we still find ready callbacks to invoke.
This is confusing and even unsafe if it ever happens to be read locklessly at some point. So make sure we write it only once per nocb_cb_wait() loop.
--- kernel/rcu/tree_plugin.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
kernel/rcu/tree_plugin.h +4 -3
@@ -2241,6 +2241,7 @@ static void nocb_cb_wait(struct rcu_data *rdp)unsigned long flags;bool needwake_state = false;bool needwake_gp = false; +bool can_sleep = true;struct rcu_node *rnp = rdp->mynode;local_irq_save (flags);
@@ -2264,8 +2265,6 @@ static void nocb_cb_wait(struct rcu_data *rdp)raw_spin_unlock_rcu_node (rnp);/* irqs remain disabled. */ } -WRITE_ONCE (rdp->nocb_cb_sleep, true); -if (rcu_segcblist_test_flags (cblist,SEGCBLIST_OFFLOADED )) {if (!rcu_segcblist_test_flags (cblist,SEGCBLIST_KTHREAD_CB )) {rcu_segcblist_set_flags (cblist,SEGCBLIST_KTHREAD_CB );
@@ -2273,7 +2272,7 @@ static void nocb_cb_wait(struct rcu_data *rdp) needwake_state = true; }if (rcu_segcblist_ready_cbs (cblist)) -WRITE_ONCE (rdp->nocb_cb_sleep, false); + can_sleep = false; }else {/* * De-offloading. Clear our flag and notify the de-offload worker.
@@ -2286,6 +2285,8 @@ static void nocb_cb_wait(struct rcu_data *rdp) needwake_state = true; } +WRITE_ONCE (rdp->nocb_cb_sleep, can_sleep); +if (rdp->nocb_cb_sleep)trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("CBSleep" ));
Signature · 1 line
2.25.1
Paul E. McKenney
maintainerRe: [PATCH 06/16] rcu/nocb: Avoid confusing double write of rdp->nocb_cb_sleep
Quoted 8 lines · Frederic Weisbecker
rdp->nocb_cb_sleepis first set to true by default after processing the callbacks then set back to false if we still find ready callbacks to invoke.This is confusing and even unsafe if it ever happens to be read locklessly at some point. So make sure we write it only once per
nocb_cb_wait()loop.
Quoted 7 lines
Reported-by Paul E. McKenney <[email protected]>Cc Josh Triplett <[email protected]>Cc Lai Jiangshan <[email protected]>Cc Joel Fernandes <[email protected]>Cc Neeraj Upadhyay <[email protected]>Cc Boqun Feng <[email protected]>Signed-off-by Frederic Weisbecker <[email protected]>
Nice, queued, thank you! The usual wordsmithing &c...
Thanx, Paul
------------------------------------------------------------------------
commit cbc3fbfe8424edc90668d5878eb493ae2ff1b888
Author: Frederic Weisbecker <[email protected]> Date: Thu Jan 28 18:12:12 2021 +0100
rcu/nocb: Avoid confusing double write of rdp->nocb_cb_sleep
The nocb_cb_wait() function first sets the rdp->nocb_cb_sleep flag to true by after invoking the callbacks, and then sets it back to false if it finds more callbacks that are ready to invoke.
This is confusing and will become unsafe if this flag is ever read locklessly. This commit therefore writes it only once, based on the state after both callback invocation and checking.
kernel/rcu/tree_plugin.h +4 -3
@@ -2229,6 +2229,7 @@ static void nocb_cb_wait(struct rcu_data *rdp)unsigned long flags;bool needwake_state = false;bool needwake_gp = false; +bool can_sleep = true;struct rcu_node *rnp = rdp->mynode;local_irq_save (flags);
@@ -2252,8 +2253,6 @@ static void nocb_cb_wait(struct rcu_data *rdp)raw_spin_unlock_rcu_node (rnp);/* irqs remain disabled. */ } -WRITE_ONCE (rdp->nocb_cb_sleep, true); -if (rcu_segcblist_test_flags (cblist,SEGCBLIST_OFFLOADED )) {if (!rcu_segcblist_test_flags (cblist,SEGCBLIST_KTHREAD_CB )) {rcu_segcblist_set_flags (cblist,SEGCBLIST_KTHREAD_CB );
@@ -2261,7 +2260,7 @@ static void nocb_cb_wait(struct rcu_data *rdp) needwake_state = true; }if (rcu_segcblist_ready_cbs (cblist)) -WRITE_ONCE (rdp->nocb_cb_sleep, false); + can_sleep = false; }else {/* * De-offloading. Clear our flag and notify the de-offload worker.
@@ -2274,6 +2273,8 @@ static void nocb_cb_wait(struct rcu_data *rdp) needwake_state = true; } +WRITE_ONCE (rdp->nocb_cb_sleep, can_sleep); +if (rdp->nocb_cb_sleep)trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("CBSleep" ));
Frederic Weisbecker
maintainerPATCH 7/16 rcu/nocb: Rename nocb_gp_update_state to nocb_gp_update_state_deoffloading
In reply to Frederic WeisbeckerUnconfuse a bit the name of this function which suggests returning true when the state is updated. It actually returns true when the rdp is in the process of deoffloading and we must ignore it.
--- kernel/rcu/tree_plugin.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
kernel/rcu/tree_plugin.h +5 -4
@@ -2026,7 +2026,8 @@ static inline bool nocb_gp_enabled_cb(struct rcu_data *rdp)return rcu_segcblist_test_flags (&rdp->cblist, flags); } -static inline bool nocb_gp_update_state (struct rcu_data *rdp,bool *needwake_state) +static inline bool nocb_gp_update_state_deoffloading (struct rcu_data *rdp, +bool *needwake_state) {struct rcu_segcblist *cblist = &rdp->cblist;
@@ -2036,7 +2037,7 @@ static inline bool nocb_gp_update_state(struct rcu_data *rdp, bool *needwake_staif (rcu_segcblist_test_flags (cblist,SEGCBLIST_KTHREAD_CB )) *needwake_state = true; } -return true; +return false; }/*
@@ -2047,7 +2048,7 @@ static inline bool nocb_gp_update_state(struct rcu_data *rdp, bool *needwake_starcu_segcblist_clear_flags (cblist,SEGCBLIST_KTHREAD_GP );if (!rcu_segcblist_test_flags (cblist,SEGCBLIST_KTHREAD_CB )) *needwake_state = true; -return false; +return true; }
@@ -2085,7 +2086,7 @@ static void nocb_gp_wait(struct rcu_data *my_rdp)continue ;trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("Check" ));rcu_nocb_lock_irqsave (rdp, flags); -if (!nocb_gp_update_state (rdp, &needwake_state)) { +if (nocb_gp_update_state_deoffloading (rdp, &needwake_state)) {rcu_nocb_unlock_irqrestore (rdp, flags);if (needwake_state)swake_up_one (&rdp->nocb_state_wq);
Signature · 1 line
2.25.1
Paul E. McKenney
maintainerRe: [PATCH 07/16] rcu/nocb: Rename nocb_gp_update_state to nocb_gp_update_state_deoffloading
Quoted 11 lines · Frederic Weisbecker
Unconfuse a bit the name of this function which suggests returning true when the state is updated. It actually returns true when the rdp is in the process of deoffloading and we must ignore it.
Reported-by Paul E. McKenney <[email protected]>Cc Josh Triplett <[email protected]>Cc Lai Jiangshan <[email protected]>Cc Joel Fernandes <[email protected]>Cc Neeraj Upadhyay <[email protected]>Cc Boqun Feng <[email protected]>Signed-off-by Frederic Weisbecker <[email protected]>
Fair point, thank you! I have queued this one for further review and testing, with the usual wordsmithing shown below.
Thanx, Paul
------------------------------------------------------------------------
commit 142d159f544763140e0f498936bca8f71563e0e0
Author: Frederic Weisbecker <[email protected]> Date: Thu Jan 28 18:12:13 2021 +0100
rcu/nocb: Rename nocb_gp_update_state to nocb_gp_update_state_deoffloading
The name nocb_gp_update_state() is unenlightening, so this commit changes it to nocb_gp_update_state_deoffloading(). This function now does what its name says, updates state and returns true if the CPU corresponding to the specified rcu_data structure is in the process of being de-offloaded.
kernel/rcu/tree_plugin.h +5 -4
@@ -2014,7 +2014,8 @@ static inline bool nocb_gp_enabled_cb(struct rcu_data *rdp)return rcu_segcblist_test_flags (&rdp->cblist, flags); } -static inline bool nocb_gp_update_state (struct rcu_data *rdp,bool *needwake_state) +static inline bool nocb_gp_update_state_deoffloading (struct rcu_data *rdp, +bool *needwake_state) {struct rcu_segcblist *cblist = &rdp->cblist;
@@ -2024,7 +2025,7 @@ static inline bool nocb_gp_update_state(struct rcu_data *rdp, bool *needwake_staif (rcu_segcblist_test_flags (cblist,SEGCBLIST_KTHREAD_CB )) *needwake_state = true; } -return true; +return false; }/*
@@ -2035,7 +2036,7 @@ static inline bool nocb_gp_update_state(struct rcu_data *rdp, bool *needwake_starcu_segcblist_clear_flags (cblist,SEGCBLIST_KTHREAD_GP );if (!rcu_segcblist_test_flags (cblist,SEGCBLIST_KTHREAD_CB )) *needwake_state = true; -return false; +return true; }
@@ -2073,7 +2074,7 @@ static void nocb_gp_wait(struct rcu_data *my_rdp)continue ;trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("Check" ));rcu_nocb_lock_irqsave (rdp, flags); -if (!nocb_gp_update_state (rdp, &needwake_state)) { +if (nocb_gp_update_state_deoffloading (rdp, &needwake_state)) {rcu_nocb_unlock_irqrestore (rdp, flags);if (needwake_state)swake_up_one (&rdp->nocb_state_wq);
Frederic Weisbecker
maintainerPATCH 8/16 rcu/nocb: Move trace_rcu_nocb_wake() calls outside nocb_lock when possible
In reply to Frederic WeisbeckerThose tracing calls don't need to be under the nocb lock. Move them outside.
--- kernel/rcu/tree_plugin.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
kernel/rcu/tree_plugin.h +3 -3
@@ -1700,9 +1700,9 @@ static bool wake_nocb_gp(struct rcu_data *rdp, bool force,lockdep_assert_held (&rdp->nocb_lock);if (!READ_ONCE (rdp_gp->nocb_gp_kthread)) { +rcu_nocb_unlock_irqrestore (rdp, flags);trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("AlreadyAwake" )); -rcu_nocb_unlock_irqrestore (rdp, flags);return false; }
@@ -1950,9 +1950,9 @@ static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_alldone,// If we are being polled or there is no kthread, just leave. t =READ_ONCE (rdp->nocb_gp_kthread);if (rcu_nocb_poll || !t) { +rcu_nocb_unlock_irqrestore (rdp, flags);trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("WakeNotPoll" )); -rcu_nocb_unlock_irqrestore (rdp, flags);return ; }// Need to actually to a wakeup.
@@ -1987,8 +1987,8 @@ static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_alldone, TPS("WakeOvfIsDeferred"));rcu_nocb_unlock_irqrestore (rdp, flags); }else { +rcu_nocb_unlock_irqrestore (rdp, flags);trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("WakeNot" )); -rcu_nocb_unlock_irqrestore (rdp, flags); }return ; }
Signature · 1 line
2.25.1
Paul E. McKenney
maintainerRe: [PATCH 08/16] rcu/nocb: Move trace_rcu_nocb_wake() calls outside nocb_lock when possible
Quoted 2 lines · Frederic Weisbecker
Those tracing calls don't need to be under the nocb lock. Move them outside.
This one looks fine (give or take the usual wordsmithing), but does not apply, presumably due to my not having yet taken one of the earlier patches. So deferred for now, and please include it in your next version.
Thanx, Paul
Quoted 49 lines
Signed-off-by Frederic Weisbecker <[email protected]>Cc Josh Triplett <[email protected]>Cc Lai Jiangshan <[email protected]>Cc Joel Fernandes <[email protected]>Cc Neeraj Upadhyay <[email protected]>Cc Boqun Feng <[email protected]>--- kernel/rcu/tree_plugin.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)kernel/rcu/tree_plugin.h +3
-3@@ -1700,9 +1700,9 @@ static bool wake_nocb_gp(struct rcu_data *rdp, bool force,lockdep_assert_held (&rdp->nocb_lock);if (!READ_ONCE (rdp_gp->nocb_gp_kthread)) { +rcu_nocb_unlock_irqrestore (rdp, flags);trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("AlreadyAwake" )); -rcu_nocb_unlock_irqrestore (rdp, flags);return false; }@@ -1950,9 +1950,9 @@ static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_alldone,// If we are being polled or there is no kthread, just leave. t =READ_ONCE (rdp->nocb_gp_kthread);if (rcu_nocb_poll || !t) { +rcu_nocb_unlock_irqrestore (rdp, flags);trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("WakeNotPoll" )); -rcu_nocb_unlock_irqrestore (rdp, flags);return ; }// Need to actually to a wakeup. @@ -1987,8 +1987,8 @@ static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_alldone, TPS("WakeOvfIsDeferred"));rcu_nocb_unlock_irqrestore (rdp, flags); }else { +rcu_nocb_unlock_irqrestore (rdp, flags);trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("WakeNot" )); -rcu_nocb_unlock_irqrestore (rdp, flags); }return ; }Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 9/16 rcu/nocb: Merge nocb_timer to the rdp leader
In reply to Frederic WeisbeckerCurrently each offline rdp has its own nocb_timer armed when the nocb_gp wakeup must be deferred. This layout has many drawbacks, compared to a solution based on a single timer per rdp group:
* It's a lot of timers to maintain.
* The per rdp nocb lock must be held to arm and cancel the timer and it's already quite contended.
* Firing one timer doesn't cancel the other rdp's timers in the same group:
- They may conflict and end up with spurious wake ups
- Each of the rdp that armed a timer need to lock both nocb_lock and then nocb_gp_lock upon exit to idle/user/guest mode.
* We can't cancel all of them if we detect an unflushed bypass in nocb_gp_wait(). In fact currently we only ever cancel the nocb_timer of the leader group.
* The leader group's nocb_timer is cancelled without locking nocb_lock in nocb_gp_wait(). It appears to be safe currently but is very error prone and hairy for reviewers.
* Since the timer takes the nocb_lock, it requires extra care in the NOCB (de-)offloading process, needing it to be either enabled or disabled and flushed.
Use a per rdp leader timer instead. It is based on nocb_gp_lock that is _way_ less contended and stays so after this change. As a matter of fact, the nocb_timer almost never fires and the deferred wakeup is mostly handled on idle/user/guest entry. Now the early check performed at this point in do_nocb_deferred_wakeup() is done on rdp_gp->nocb_defer_wakeup, which is racy of course. It doesn't matter though because we only need the guarantee to see the timer armed if we were the last one to arm it. Any other situation (another rdp has armed it and we either see it or not) is fine.
This solves all the issues listed above.
--- kernel/rcu/tree.h | 1 - kernel/rcu/tree_plugin.h | 119 ++++++++++++++++++++++----------------- 2 files changed, 68 insertions(+), 52 deletions(-)
kernel/rcu/tree.h +0 -1
@@ -257,7 +257,6 @@ struct rcu_data { };/* Values for nocb_defer_wakeup field in struct rcu_data. */ -#define RCU_NOCB_WAKE_OFF -1#define RCU_NOCB_WAKE_NOT 0#define RCU_NOCB_WAKE 1#define RCU_NOCB_WAKE_FORCE 2
kernel/rcu/tree_plugin.h +68 -51
207 lines not on this page · raw message
Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 10/16 rcu/nocb: Directly call __wake_nocb_gp() from bypass timer
In reply to Frederic WeisbeckerThe bypass timer calls __call_rcu_nocb_wake() instead of directly calling __wake_nocb_gp(). The only difference here is that rdp->qlen_last_fqs_check gets overriden. But resetting the deferred force quiescent state base shouldn't be relevant for that timer. In fact the bypass queue in concern can be for any rdp from the group and not necessarily the rdp leader on which the bypass timer is attached.
Therefore we can simply call directly __wake_nocb_gp(). This way we don't even need to lock the nocb_lock.
--- kernel/rcu/tree_plugin.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
kernel/rcu/tree_plugin.h +3 -2
@@ -2018,9 +2018,10 @@ static void do_nocb_bypass_wakeup_timer(struct timer_list *t)struct rcu_data *rdp =from_timer (rdp, t, nocb_bypass_timer);trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("Timer" )); -rcu_nocb_lock_irqsave (rdp, flags); + +raw_spin_lock_irqsave (&rdp->nocb_gp_lock, flags);smp_mb__after_spinlock ();/* Timer expire before wakeup. */ -__call_rcu_nocb_wake (rdp, true, flags); +__wake_nocb_gp (rdp, rdp, false, flags); }/*
Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 11/16 rcu/nocb: Allow de-offloading rdp leader
In reply to Frederic WeisbeckerThe only thing that prevented an rdp leader from being de-offloaded was the nocb_bypass_timer that used to lock the nocb_lock of the rdp leader.
If an rdp gets de-offloaded, it will subtely ignore rcu_nocb_lock() calls and do its job in the timer unsafely. Worse yet: if it gets re-offloaded in the middle of the timer, rcu_nocb_unlock() would try to unlock, leaving it imbalanced.
Now that the nocb_bypass_timer doesn't use the nocb_lock anymore, we can safely allow rdp leader to be de-offloaded.
--- kernel/rcu/tree_plugin.h | 4 ---- 1 file changed, 4 deletions(-)
kernel/rcu/tree_plugin.h +0 -4
@@ -2481,10 +2481,6 @@ int rcu_nocb_cpu_deoffload(int cpu)struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);int ret =0 ; -if (rdp == rdp->nocb_gp_rdp) { -pr_info ("Can't deoffload an rdp GP leader (yet)\n" ); -return -EINVAL ; - }mutex_lock (&rcu_state.barrier_mutex);cpus_read_lock ();if (rcu_rdp_is_offloaded (rdp)) {
Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 12/16 rcu/nocb: Cancel nocb_timer upon nocb_gp wakeup
In reply to Frederic WeisbeckerAs we wake up in nocb_gp_wait(), there is no need to keep the nocb_timer around as we are going to go through the whole rdp list again. Any update performed before the timer was armed will now be visible after the nocb_gp_lock acquire.
--- kernel/rcu/tree_plugin.h | 4 ++++ 1 file changed, 4 insertions(+)
kernel/rcu/tree_plugin.h +4 -0
@@ -2212,6 +2212,10 @@ static void nocb_gp_wait(struct rcu_data *my_rdp)raw_spin_lock_irqsave (&my_rdp->nocb_gp_lock, flags);if (bypass)del_timer (&my_rdp->nocb_bypass_timer); +if (my_rdp->nocb_defer_wakeup >RCU_NOCB_WAKE_NOT ) { +WRITE_ONCE (my_rdp->nocb_defer_wakeup,RCU_NOCB_WAKE_NOT ); +del_timer (&my_rdp->nocb_timer); + }WRITE_ONCE (my_rdp->nocb_gp_sleep, true);raw_spin_unlock_irqrestore (&my_rdp->nocb_gp_lock, flags); }
Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 13/16 rcu/nocb: Delete bypass_timer upon nocb_gp wakeup
In reply to Frederic WeisbeckerA NOCB-gp wake up can safely delete the nocb_bypass_timer. nocb_gp_wait() is going to check again the bypass state and rearm the bypass timer if necessary.
--- kernel/rcu/tree_plugin.h | 1 + 1 file changed, 1 insertion(+)
kernel/rcu/tree_plugin.h +1 -0
@@ -1703,6 +1703,7 @@ static bool __wake_nocb_gp(struct rcu_data *rdp_gp, rdp_gp->nocb_defer_wakeup =RCU_NOCB_WAKE_NOT ;del_timer (&rdp_gp->nocb_timer); +del_timer (&rdp_gp->nocb_bypass_timer);if (force ||READ_ONCE (rdp_gp->nocb_gp_sleep)) {WRITE_ONCE (rdp_gp->nocb_gp_sleep, false);
Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 14/16 rcu/nocb: Only cancel nocb timer if not polling
In reply to Frederic WeisbeckerNo need to disarm the nocb_timer if rcu_nocb is polling because it shouldn't be armed either.
--- kernel/rcu/tree_plugin.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
kernel/rcu/tree_plugin.h +5 -5
@@ -2178,16 +2178,16 @@ static void nocb_gp_wait(struct rcu_data *my_rdp) my_rdp->nocb_gp_gp = needwait_gp; my_rdp->nocb_gp_seq = needwait_gp ? wait_gp_seq :0 ;if (bypass) { -raw_spin_lock_irqsave (&my_rdp->nocb_gp_lock, flags); -// Avoid race with first bypass CB. -WRITE_ONCE (my_rdp->nocb_defer_wakeup,RCU_NOCB_WAKE_NOT ); -del_timer (&my_rdp->nocb_timer);if (!rcu_nocb_poll) { +raw_spin_lock_irqsave (&my_rdp->nocb_gp_lock, flags); +// Avoid race with first bypass CB. +WRITE_ONCE (my_rdp->nocb_defer_wakeup,RCU_NOCB_WAKE_NOT ); +del_timer (&my_rdp->nocb_timer);// At least one child with non-empty ->nocb_bypass, so set // timer in order to avoid stranding its callbacks. mod_timer (&my_rdp->nocb_bypass_timer, j +2 ); +raw_spin_unlock_irqrestore (&my_rdp->nocb_gp_lock, flags); } -raw_spin_unlock_irqrestore (&my_rdp->nocb_gp_lock, flags); }if (rcu_nocb_poll) {/* Polling, so trace if first poll in the series. */
Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 15/16 rcu/nocb: Prepare for finegrained deferred wakeup
In reply to Frederic WeisbeckerProvide a way to tune the deferred wakeup level we want to perform from a safe wakeup point. Currently those sites are:
* nocb_timer
* user/idle/guest entry
* CPU down
* softirq/rcuc
All of these sites perform the wake up for both RCU_NOCB_WAKE and RCU_NOCB_WAKE_FORCE.
In order to merge nocb_timer and nocb_bypass_timer together, we plan to add a new RCU_NOCB_WAKE_BYPASS that really want to be deferred until a timer fires so that we don't wake up the NOCB-gp kthread too early.
To prepare for that, integrate a wake up level/limit that a callsite is deemed to perform.
--- kernel/rcu/tree.c | 2 +- kernel/rcu/tree.h | 2 +- kernel/rcu/tree_plugin.h | 15 ++++++++------- 3 files changed, 10 insertions(+), 9 deletions(-)
kernel/rcu/tree.c +1 -1
@@ -3813,7 +3813,7 @@ static int rcu_pending(int user)check_cpu_stall (rdp);/* Does this CPU need a deferred NOCB wakeup? */ -if (rcu_nocb_need_deferred_wakeup (rdp)) +if (rcu_nocb_need_deferred_wakeup (rdp,RCU_NOCB_WAKE ))return 1 ;/* Is this a nohz_full CPU in userspace or idle? (Ignore RCU if so.) */
kernel/rcu/tree.h +1 -1
@@ -433,7 +433,7 @@ static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp, bool *was_alldone, unsigned long flags);static void __call_rcu_nocb_wake (struct rcu_data *rdp,bool was_empty,unsigned long flags); -static int rcu_nocb_need_deferred_wakeup (struct rcu_data *rdp); +static int rcu_nocb_need_deferred_wakeup (struct rcu_data *rdp,int level);static bool do_nocb_deferred_wakeup (struct rcu_data *rdp);static void rcu_boot_init_nocb_percpu_data (struct rcu_data *rdp);static void rcu_spawn_cpu_nocb_kthread (int cpu);
kernel/rcu/tree_plugin.h +8 -7
@@ -2354,13 +2354,14 @@ static int rcu_nocb_cb_kthread(void *arg) }/* Is a deferred wakeup of rcu_nocb_kthread() required? */ -static int rcu_nocb_need_deferred_wakeup (struct rcu_data *rdp) +static int rcu_nocb_need_deferred_wakeup (struct rcu_data *rdp,int level) { -return READ_ONCE (rdp->nocb_defer_wakeup) >RCU_NOCB_WAKE_NOT ; +return READ_ONCE (rdp->nocb_defer_wakeup) >= level; }/* Do a deferred wakeup of rcu_nocb_kthread(). */ -static bool do_nocb_deferred_wakeup_common (struct rcu_data *rdp) +static bool do_nocb_deferred_wakeup_common (struct rcu_data *rdp, +int level) {unsigned long flags;int ndw;
@@ -2369,7 +2370,7 @@ static bool do_nocb_deferred_wakeup_common(struct rcu_data *rdp)raw_spin_lock_irqsave (&rdp_gp->nocb_gp_lock, flags); -if (!rcu_nocb_need_deferred_wakeup (rdp_gp)) { +if (!rcu_nocb_need_deferred_wakeup (rdp_gp, level)) {raw_spin_unlock_irqrestore (&rdp_gp->nocb_gp_lock, flags);;return false; }
@@ -2385,7 +2386,7 @@ static void do_nocb_deferred_wakeup_timer(struct timer_list *t) {struct rcu_data *rdp =from_timer (rdp, t, nocb_timer); -do_nocb_deferred_wakeup_common (rdp); +do_nocb_deferred_wakeup_common (rdp,RCU_NOCB_WAKE ); }/*
@@ -2398,8 +2399,8 @@ static bool do_nocb_deferred_wakeup(struct rcu_data *rdp)if (!rdp->nocb_gp_rdp)return false; -if (rcu_nocb_need_deferred_wakeup (rdp->nocb_gp_rdp)) -return do_nocb_deferred_wakeup_common (rdp); +if (rcu_nocb_need_deferred_wakeup (rdp->nocb_gp_rdp,RCU_NOCB_WAKE )) +return do_nocb_deferred_wakeup_common (rdp,RCU_NOCB_WAKE );return false; }
Signature · 1 line
2.25.1
Frederic Weisbecker
maintainerPATCH 16/16 rcu/nocb: Unify timers
In reply to Frederic WeisbeckerNow that nocb_timer and nocb_bypass_timer have become very similar, merge them together. A new RCU_NOCB_WAKE_BYPASS wake level is introduced. As a result, timers perform all kinds of deferred wake ups but other deferred wakeup callsites only handle non-bypass wakeups in order not to wake up rcuo too early.
The timer also performs the full barrier all the time to order timer_pending() and callback enqueue although the path performing RCU_NOCB_WAKE_FORCE that makes use of it is debatable. It should also test against the rdp leader instead of the current rdp.
The permanent full barrier shouldn't bring visible overhead since the timers almost never fire.
--- include/trace/events/rcu.h | 1 + kernel/rcu/tree.h | 6 +-- kernel/rcu/tree_plugin.h | 88 ++++++++++++++++---------------------- 3 files changed, 42 insertions(+), 53 deletions(-)
include/trace/events/rcu.h +1 -0
@@ -278,6 +278,7 @@ TRACE_EVENT_RCU(rcu_exp_funnel_lock,* "WakeNot": Don't wake rcuo kthread. * "WakeNotPoll": Don't wake rcuo kthread because it is polling. * "WakeOvfIsDeferred": Wake rcuo kthread later, CB list is huge. +* "WakeBypassIsDeferred": Wake rcuo kthread later, bypass list is contended. * "WokeEmpty": rcuo CB kthread woke to find empty list. */ TRACE_EVENT_RCU(rcu_nocb_wake,
kernel/rcu/tree.h +3 -3
@@ -218,7 +218,6 @@ struct rcu_data {/* The following fields are used by GP kthread, hence own cacheline. */ raw_spinlock_t nocb_gp_lock____cacheline_internodealigned_in_smp ; -struct timer_list nocb_bypass_timer;/* Force nocb_bypass flush. */ u8 nocb_gp_sleep;/* Is the nocb GP thread asleep? */ u8 nocb_gp_bypass;/* Found a bypass on last scan? */ u8 nocb_gp_gp;/* GP to wait for on last scan? */
@@ -258,8 +257,9 @@ struct rcu_data {/* Values for nocb_defer_wakeup field in struct rcu_data. */ #define RCU_NOCB_WAKE_NOT 0 -#define RCU_NOCB_WAKE 1 -#define RCU_NOCB_WAKE_FORCE 2 +#define RCU_NOCB_WAKE_BYPASS 1 +#define RCU_NOCB_WAKE 2 +#define RCU_NOCB_WAKE_FORCE 3#define RCU_JIFFIES_TILL_FORCE_QS (1 + (HZ > 250) + (HZ > 500))/* For jiffies_till_first_fqs and */
kernel/rcu/tree_plugin.h +38 -50
185 lines not on this page · raw message
Signature · 1 line
2.25.1