Hi folks,
this is v2 of:
https://lore.kernel.org/lkml/20210721115118.729943-1-valentin.schneider@arm.com/
respun from Frederic and Paul's helpful feedback. Tested atop v5.14-rc4-rt6 with
the v1 patches reverted. There, commit
d76e0926d835 ("rcu/nocb: Use the rcuog CPU's ->nocb_timer")
prevents the NOCB offload warning from firing if there are no NOCB CPUs (which
is sensible). Adding a single NOCB CPU brings the warning back, which patch 3
fixes.
Note that patches 2 & 4 are already in v5.14-rc4-rt6, but still apply against
mainline.
Revisions
=========
v1 -> v2
++++++++
o Rebased and tested against v5.14-rc4-rt6
o Picked rcutorture patch from
https://lore.kernel.org/lkml/20210803225437.3612591-2-valentin.schneider@arm.com/
o Added a local_lock to protect NOCB offload state under PREEMPT_RT (Frederic,
Paul)
Valentin Schneider (4):
rcutorture: Don't disable softirqs with preemption disabled when
PREEMPT_RT
sched: Introduce is_pcpu_safe()
rcu/nocb: Protect NOCB state via local_lock() under PREEMPT_RT
arm64: mm: Make arch_faults_on_old_pte() check for migratability
arch/arm64/include/asm/pgtable.h | 2 +-
include/linux/sched.h | 10 ++++
kernel/rcu/rcutorture.c | 2 +
kernel/rcu/tree.c | 4 ++
kernel/rcu/tree.h | 4 ++
kernel/rcu/tree_plugin.h | 82 ++++++++++++++++++++++++++++----
6 files changed, 94 insertions(+), 10 deletions(-)
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Some areas use preempt_disable() + preempt_enable() to safely access
per-CPU data. The PREEMPT_RT folks have shown this can also be done by
keeping preemption enabled and instead disabling migration (and acquiring a
sleepable lock, if relevant).
Introduce a helper which checks whether the current task can safely access
per-CPU data, IOW if the task's context guarantees the accesses will target
a single CPU. This accounts for preemption, CPU affinity, and migrate
disable - note that the CPU affinity check also mandates the presence of
PF_NO_SETAFFINITY, as otherwise userspace could concurrently render the
upcoming per-CPU access(es) unsafe.
Signed-off-by: Valentin Schneider <redacted>
---
include/linux/sched.h | 10 ++++++++++
1 file changed, 10 insertions(+)
@@ -1715,6 +1715,16 @@ static inline bool is_percpu_thread(void)#endif}+/* Is the current task guaranteed not to be migrated elsewhere? */+staticinlineboolis_pcpu_safe(void)+{+#ifdef CONFIG_SMP+return!preemptible()||is_percpu_thread()||current->migration_disabled;+#else+returntrue;+#endif+}+/* Per-process atomic flags. */#define PFA_NO_NEW_PRIVS 0 /* May not gain new privileges. */#define PFA_SPREAD_PAGE 1 /* Spread page cache over cpuset */
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Warning
=======
Running v5.13-rt1 on my arm64 Juno board triggers:
[ 0.156302] =============================
[ 0.160416] WARNING: suspicious RCU usage
[ 0.164529] 5.13.0-rt1 #20 Not tainted
[ 0.168300] -----------------------------
[ 0.172409] kernel/rcu/tree_plugin.h:69 Unsafe read of RCU_NOCB offloaded state!
[ 0.179920]
[ 0.179920] other info that might help us debug this:
[ 0.179920]
[ 0.188037]
[ 0.188037] rcu_scheduler_active = 1, debug_locks = 1
[ 0.194677] 3 locks held by rcuc/0/11:
[ 0.198448] #0: ffff00097ef10cf8 ((softirq_ctrl.lock).lock){+.+.}-{2:2}, at: __local_bh_disable_ip (./include/linux/rcupdate.h:662 kernel/softirq.c:171)
[ 0.208709] #1: ffff80001205e5f0 (rcu_read_lock){....}-{1:2}, at: rt_spin_lock (kernel/locking/spinlock_rt.c:43 (discriminator 4))
[ 0.217134] #2: ffff80001205e5f0 (rcu_read_lock){....}-{1:2}, at: __local_bh_disable_ip (kernel/softirq.c:169)
[ 0.226428]
[ 0.226428] stack backtrace:
[ 0.230889] CPU: 0 PID: 11 Comm: rcuc/0 Not tainted 5.13.0-rt1 #20
[ 0.237100] Hardware name: ARM Juno development board (r0) (DT)
[ 0.243041] Call trace:
[ 0.245497] dump_backtrace (arch/arm64/kernel/stacktrace.c:163)
[ 0.249185] show_stack (arch/arm64/kernel/stacktrace.c:219)
[ 0.252522] dump_stack (lib/dump_stack.c:122)
[ 0.255947] lockdep_rcu_suspicious (kernel/locking/lockdep.c:6439)
[ 0.260328] rcu_rdp_is_offloaded (kernel/rcu/tree_plugin.h:69 kernel/rcu/tree_plugin.h:58)
[ 0.264537] rcu_core (kernel/rcu/tree.c:2332 kernel/rcu/tree.c:2398 kernel/rcu/tree.c:2777)
[ 0.267786] rcu_cpu_kthread (./include/linux/bottom_half.h:32 kernel/rcu/tree.c:2876)
[ 0.271644] smpboot_thread_fn (kernel/smpboot.c:165 (discriminator 3))
[ 0.275767] kthread (kernel/kthread.c:321)
[ 0.279013] ret_from_fork (arch/arm64/kernel/entry.S:1005)
In this case, this is the RCU core kthread accessing the local CPU's
rdp. Before that, rcu_cpu_kthread() invokes local_bh_disable().
Under !CONFIG_PREEMPT_RT (and rcutree.use_softirq=0), this ends up
incrementing the preempt_count, which satisfies the "local non-preemptible
read" of rcu_rdp_is_offloaded().
Under CONFIG_PREEMPT_RT however, this becomes
local_lock(&softirq_ctrl.lock)
which, under the same config, is migrate_disable() + rt_spin_lock(). As
pointed out by Frederic, this is not sufficient to safely access an rdp's
offload state, as the RCU core kthread can be preempted by a kworker
executing rcu_nocb_rdp_offload() [1].
Introduce a local_lock to serialize an rdp's offload state while the rdp's
associated core kthread is executing rcu_core().
rcu_core() preemptability considerations
========================================
As pointed out by Paul [2], keeping rcu_check_quiescent_state() preemptible
(which is the case under CONFIG_PREEMPT_RT) requires some consideration.
note_gp_changes() itself runs with irqs off, and enters
__note_gp_changes() with rnp->lock held (raw_spinlock), thus is safe vs
preemption.
rdp->core_needs_qs *could* change after being read by the RCU core
kthread if it then gets preempted. Consider, with
CONFIG_RCU_STRICT_GRACE_PERIOD:
rcuc/x task_foo
rcu_check_quiescent_state()
`\
rdp->core_needs_qs == true
<PREEMPT>
rcu_read_unlock()
`\
rcu_preempt_deferred_qs_irqrestore()
`\
rcu_report_qs_rdp()
`\
rdp->core_needs_qs := false;
This would let rcuc/x's rcu_check_quiescent_state() proceed further down to
rcu_report_qs_rdp(), but if task_foo's earlier rcu_report_qs_rdp()
invocation would have cleared the rdp grpmask from the rnp mask, so
rcuc/x's invocation would simply bail.
Since rcu_report_qs_rdp() can be safely invoked, even if rdp->core_needs_qs
changed, it appears safe to keep rcu_check_quiescent_state() preemptible.
[1]: http://lore.kernel.org/r/20210727230814.GC283787@lothringen
[2]: http://lore.kernel.org/r/20210729010445.GO4397@paulmck-ThinkPad-P17-Gen-1
Signed-off-by: Valentin Schneider <redacted>
---
kernel/rcu/tree.c | 4 ++
kernel/rcu/tree.h | 4 ++
kernel/rcu/tree_plugin.h | 82 +++++++++++++++++++++++++++++++++++-----
3 files changed, 81 insertions(+), 9 deletions(-)
@@ -210,6 +210,8 @@ struct rcu_data {structtimer_listnocb_timer;/* Enforce finite deferral. */unsignedlongnocb_gp_adv_time;/* Last call_rcu() CB adv (jiffies). */+local_lock_tnocb_local_lock;+/* The following fields are used by call_rcu, hence own cacheline. */raw_spinlock_tnocb_bypass_lock____cacheline_internodealigned_in_smp;structrcu_cblistnocb_bypass;/* Lock-contention-bypass CB list. */
@@ -21,6 +21,17 @@ static inline int rcu_lockdep_is_held_nocb(struct rcu_data *rdp)returnlockdep_is_held(&rdp->nocb_lock);}+staticinlineintrcu_lockdep_is_held_nocb_local(structrcu_data*rdp)+{+returnlockdep_is_held(+#ifdef CONFIG_PREEMPT_RT+&rdp->nocb_local_lock.lock+#else+&rdp->nocb_local_lock+#endif+);+}+staticinlineboolrcu_current_is_nocb_kthread(structrcu_data*rdp){/* Race on early boot between thread creation and assignment */
@@ -38,7 +49,10 @@ static inline int rcu_lockdep_is_held_nocb(struct rcu_data *rdp){return0;}-+staticinlineintrcu_lockdep_is_held_nocb_local(structrcu_data*rdp)+{+return0;+}staticinlineboolrcu_current_is_nocb_kthread(structrcu_data*rdp){returnfalse;
@@ -46,23 +60,44 @@ static inline bool rcu_current_is_nocb_kthread(struct rcu_data *rdp)#endif /* #ifdef CONFIG_RCU_NOCB_CPU */+/*+*Isalocalreadoftherdp'soffloadedstatesafeandstable?+*Seercu_nocb_local_lock()&family.+*/+staticinlineboolrcu_local_offload_access_safe(structrcu_data*rdp)+{+if(!preemptible())+returntrue;++if(is_pcpu_safe()){+if(!IS_ENABLED(CONFIG_RCU_NOCB))+returntrue;++returnrcu_lockdep_is_held_nocb_local(rdp);+}++returnfalse;+}+staticboolrcu_rdp_is_offloaded(structrcu_data*rdp){/*-*Inordertoreadtheoffloadedstateofanrdpisasafe-*andstablewayandpreventfromitsvaluetobechanged-*underus,wemusteitherholdthebarriermutex,thecpu-*hotpluglock(readorwrite)orthenocblock.Local-*non-preemptiblereadsarealsosafe.NOCBkthreadsand-*timershavetheirownmeansofsynchronizationagainstthe-*offloadedstateupdaters.+*Inordertoreadtheoffloadedstateofanrdpisasafeandstable+*wayandpreventfromitsvaluetobechangedunderus,wemusteither...*/RCU_LOCKDEP_WARN(+// ...hold the barrier mutex...!(lockdep_is_held(&rcu_state.barrier_mutex)||+// ... the cpu hotplug lock (read or write)...(IS_ENABLED(CONFIG_HOTPLUG_CPU)&&lockdep_is_cpus_held())||+// ... or the NOCB lock.rcu_lockdep_is_held_nocb(rdp)||+// Local reads still require the local state to remain stable+// (preemption disabled / local lock held)(rdp==this_cpu_ptr(&rcu_data)&&-!(IS_ENABLED(CONFIG_PREEMPT_COUNT)&&preemptible()))||+rcu_local_offload_access_safe(rdp))||+// NOCB kthreads and timers have their own means of synchronization+// against the offloaded state updaters.rcu_current_is_nocb_kthread(rdp)),"Unsafe read of RCU_NOCB offloaded state");
@@ -1629,6 +1664,22 @@ static void rcu_nocb_unlock_irqrestore(struct rcu_data *rdp,}}+/*+*Theinvocationofrcu_core()withintheRCUcorekthreadsremainspreemptible+*underPREEMPT_RT,thustheoffloadstateofaCPUcouldchangewhile+*saidkthreadsarepreempted.Preventthisfromhappeningbyprotectingthe+*offloadstatewithalocal_lock().+*/+staticvoidrcu_nocb_local_lock(structrcu_data*rdp)+{+local_lock(&rcu_data.nocb_local_lock);+}++staticvoidrcu_nocb_local_unlock(structrcu_data*rdp)+{+local_unlock(&rcu_data.nocb_local_lock);+}+/* Lockdep check that ->cblist may be safely accessed. */staticvoidrcu_lockdep_assert_cblist_protected(structrcu_data*rdp){
@@ -2396,6 +2447,7 @@ static int rdp_offload_toggle(struct rcu_data *rdp,if(rdp->nocb_cb_sleep)rdp->nocb_cb_sleep=false;rcu_nocb_unlock_irqrestore(rdp,flags);+rcu_nocb_local_unlock(rdp);/**Ignoreformervalueofnocb_cb_sleepandforcewakeupasitcould
@@ -2427,6 +2479,7 @@ static long rcu_nocb_rdp_deoffload(void *arg)pr_info("De-offloading %d\n",rdp->cpu);+rcu_nocb_local_lock(rdp);rcu_nocb_lock_irqsave(rdp,flags);/**Flushonceandforallnow.Thissufficesbecauseweare
@@ -2509,6 +2562,7 @@ static long rcu_nocb_rdp_offload(void *arg)*Can'tusercu_nocb_lock_irqsave()whilewearein*SEGCBLIST_SOFTIRQ_ONLYmode.*/+rcu_nocb_local_lock(rdp);raw_spin_lock_irqsave(&rdp->nocb_lock,flags);/*
@@ -2868,6 +2922,16 @@ static void rcu_nocb_unlock_irqrestore(struct rcu_data *rdp,local_irq_restore(flags);}+/* No ->nocb_local_lock to acquire. */+staticvoidrcu_nocb_local_lock(structrcu_data*rdp)+{+}++/* No ->nocb_local_lock to release. */+staticvoidrcu_nocb_local_unlock(structrcu_data*rdp)+{+}+/* Lockdep check that ->cblist may be safely accessed. */staticvoidrcu_lockdep_assert_cblist_protected(structrcu_data*rdp){
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: Mike Galbraith <hidden> Date: 2021-08-07 01:43:45
On Sat, 2021-08-07 at 01:58 +0100, Valentin Schneider wrote:
+static inline bool is_pcpu_safe(void)
Nit: seems odd to avoid spelling it out to save two characters, percpu
is word like, rolls off the ole tongue better than p-c-p-u.
-Mike
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Sat, 2021-08-07 at 01:58 +0100, Valentin Schneider wrote:
quoted
+static inline bool is_pcpu_safe(void)
Nit: seems odd to avoid spelling it out to save two characters, percpu
is word like, rolls off the ole tongue better than p-c-p-u.
-Mike
True. A quick grep says both versions are used, though "percpu" wins by
about a factor of 2. I'll tweak that for a v3.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Hi,
On Sat, Aug 07, 2021 at 01:58:05AM +0100, Valentin Schneider wrote:
quoted hunk
Some areas use preempt_disable() + preempt_enable() to safely access
per-CPU data. The PREEMPT_RT folks have shown this can also be done by
keeping preemption enabled and instead disabling migration (and acquiring a
sleepable lock, if relevant).
Introduce a helper which checks whether the current task can safely access
per-CPU data, IOW if the task's context guarantees the accesses will target
a single CPU. This accounts for preemption, CPU affinity, and migrate
disable - note that the CPU affinity check also mandates the presence of
PF_NO_SETAFFINITY, as otherwise userspace could concurrently render the
upcoming per-CPU access(es) unsafe.
Signed-off-by: Valentin Schneider <redacted>
---
include/linux/sched.h | 10 ++++++++++
1 file changed, 10 insertions(+)
@@ -1715,6 +1715,16 @@ static inline bool is_percpu_thread(void)#endif}+/* Is the current task guaranteed not to be migrated elsewhere? */+staticinlineboolis_pcpu_safe(void)+{+#ifdef CONFIG_SMP+return!preemptible()||is_percpu_thread()||current->migration_disabled;+#else+returntrue;+#endif+}
I wonder whether the following can happen, say thread A is a worker
thread for CPU 1, so it has the flag PF_NO_SETAFFINITY set.
{ percpu variable X on CPU 2 is initially 0 }
thread A
========
<preemption enabled>
if (is_pcpu_safe()) { // nr_cpus_allowed == 1, so return true.
<preempted>
<hot unplug CPU 1>
unbinder_workers(1); // A->cpus_mask becomes cpu_possible_mask
<back to run on CPU 2>
__this_cpu_inc(X);
tmp = X; // tmp == 0
<preempted>
<in thread B>
this_cpu_inc(X); // X becomes 1
<back to run A on CPU 2>
X = tmp + 1; // race!
}
if so, then is_percpu_thread() doesn't indicate is_pcpu_safe()?
Regards,
Boqun
+
/* Per-process atomic flags. */
#define PFA_NO_NEW_PRIVS 0 /* May not gain new privileges. */
#define PFA_SPREAD_PAGE 1 /* Spread page cache over cpuset */
--
2.25.1
Hi,
On Sat, Aug 07, 2021 at 01:58:05AM +0100, Valentin Schneider wrote:
quoted
Some areas use preempt_disable() + preempt_enable() to safely access
per-CPU data. The PREEMPT_RT folks have shown this can also be done by
keeping preemption enabled and instead disabling migration (and acquiring a
sleepable lock, if relevant).
Introduce a helper which checks whether the current task can safely access
per-CPU data, IOW if the task's context guarantees the accesses will target
a single CPU. This accounts for preemption, CPU affinity, and migrate
disable - note that the CPU affinity check also mandates the presence of
PF_NO_SETAFFINITY, as otherwise userspace could concurrently render the
upcoming per-CPU access(es) unsafe.
Signed-off-by: Valentin Schneider <redacted>
---
include/linux/sched.h | 10 ++++++++++
1 file changed, 10 insertions(+)
@@ -1715,6 +1715,16 @@ static inline bool is_percpu_thread(void)#endif}+/* Is the current task guaranteed not to be migrated elsewhere? */+staticinlineboolis_pcpu_safe(void)+{+#ifdef CONFIG_SMP+return!preemptible()||is_percpu_thread()||current->migration_disabled;+#else+returntrue;+#endif+}
I wonder whether the following can happen, say thread A is a worker
thread for CPU 1, so it has the flag PF_NO_SETAFFINITY set.
{ percpu variable X on CPU 2 is initially 0 }
thread A
========
<preemption enabled>
if (is_pcpu_safe()) { // nr_cpus_allowed == 1, so return true.
<preempted>
<hot unplug CPU 1>
unbinder_workers(1); // A->cpus_mask becomes cpu_possible_mask
<back to run on CPU 2>
__this_cpu_inc(X);
tmp = X; // tmp == 0
<preempted>
<in thread B>
this_cpu_inc(X); // X becomes 1
<back to run A on CPU 2>
X = tmp + 1; // race!
}
if so, then is_percpu_thread() doesn't indicate is_pcpu_safe()?
You're absolutely right.
migrate_disable() protects the thread against being migrated due to
hotplug, but pure CPU affinity doesn't at all. kthread_is_per_cpu() doesn't
work either, because parking is not the only approach to hotplug for those
(e.g. per-CPU workqueue threads unbind themselves on hotplug, as in your
example).
One could hold cpus_read_lock(), but I don't see much point here. So that
has to be
return !preemptible() || current->migration_disabled;
Thanks!
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Sun, Aug 08, 2021 at 05:15:20PM +0100, Valentin Schneider wrote:
On 07/08/21 03:42, Mike Galbraith wrote:
quoted
On Sat, 2021-08-07 at 01:58 +0100, Valentin Schneider wrote:
quoted
+static inline bool is_pcpu_safe(void)
Nit: seems odd to avoid spelling it out to save two characters, percpu
is word like, rolls off the ole tongue better than p-c-p-u.
-Mike
True. A quick grep says both versions are used, though "percpu" wins by
about a factor of 2. I'll tweak that for a v3.
I wonder why is_percpu_safe() is the correct name. The safety of
accesses to percpu variables means two things to me:
a) The thread cannot migrate to other CPU in the middle of
accessing a percpu variable, in other words, the following
cannot happen:
{ percpu variable X is 0 on CPU 0 and 2 on CPU 1
CPU 0 CPU 1
======== =========
<in thread A>
__this_cpu_inc(X);
tmp = X; // tmp is 0
<preempted>
<migrate to CPU 1>
// continue __this_cpu_inc(X);
X = tmp + 1; // CPU 0 miss this
// increment (this
// may be OK), and
// CPU 1's X got
// corrupted.
b) The accesses to a percpu variable are exclusive, i.e. no
interrupt or preemption can happen in the middle of accessing,
in other words, the following cannot happen:
{ percpu variable X is 0 on CPU 0 }
CPU 0
========
<in thread A>
__this_cpu_inc(X);
tmp = X; // tmp is 0
<preempted>
<in other thread>
this_cpu_inc(X); // X is 1 afterwards.
<back to thread A>
X = tmp + 1; // X is 1, and we have a race condition.
And the is_p{er}cpu_safe() only detects the first, and it doesn't mean
totally safe for percpu accesses.
Maybe we can implement a migratable()? Although not sure it's a English
word.
Regards,
Boqun
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Sun, Aug 08, 2021 at 05:15:20PM +0100, Valentin Schneider wrote:
quoted
On 07/08/21 03:42, Mike Galbraith wrote:
quoted
On Sat, 2021-08-07 at 01:58 +0100, Valentin Schneider wrote:
quoted
+static inline bool is_pcpu_safe(void)
Nit: seems odd to avoid spelling it out to save two characters, percpu
is word like, rolls off the ole tongue better than p-c-p-u.
-Mike
True. A quick grep says both versions are used, though "percpu" wins by
about a factor of 2. I'll tweak that for a v3.
I wonder why is_percpu_safe() is the correct name. The safety of
accesses to percpu variables means two things to me:
a) The thread cannot migrate to other CPU in the middle of
accessing a percpu variable, in other words, the following
cannot happen:
{ percpu variable X is 0 on CPU 0 and 2 on CPU 1
CPU 0 CPU 1
======== =========
<in thread A>
__this_cpu_inc(X);
tmp = X; // tmp is 0
<preempted>
<migrate to CPU 1>
// continue __this_cpu_inc(X);
X = tmp + 1; // CPU 0 miss this
// increment (this
// may be OK), and
// CPU 1's X got
// corrupted.
b) The accesses to a percpu variable are exclusive, i.e. no
interrupt or preemption can happen in the middle of accessing,
in other words, the following cannot happen:
{ percpu variable X is 0 on CPU 0 }
CPU 0
========
<in thread A>
__this_cpu_inc(X);
tmp = X; // tmp is 0
<preempted>
<in other thread>
this_cpu_inc(X); // X is 1 afterwards.
<back to thread A>
X = tmp + 1; // X is 1, and we have a race condition.
And the is_p{er}cpu_safe() only detects the first, and it doesn't mean
totally safe for percpu accesses.
Right. I do briefly point this out in the changelog (the bit about
"acquiring a sleepable lock if relevant"), but that doesn't do much to
clarify the helper name itself.
Maybe we can implement a migratable()? Although not sure it's a English
word.
Funnily enough that is exactly how I named the thing in my initial draft,
but then I somehow convinced myself that tailoring the name to per-CPU
accesses would make its intent clearer.
I think you're right that "migratable()" is less confusing at the end of
the day. Oh well, so much for overthinking the naming problem :-)