From: Sebastian Andrzej Siewior Date: Wed, 13 Sep 2017 14:43:41 +0200 Subject: [PATCH 161/353] srcu: use cpu_online() instead custom check Origin: https://git.kernel.org/cgit/linux/kernel/git/rt/linux-stable-rt.git/commit?id=1cf84fbe102004dc42ac21f8f35479ed4c8fcb82 The current check via srcu_online is slightly racy because after looking at srcu_online there could be an interrupt that interrupted us long enough until the CPU we checked against went offline. An alternative would be to hold the hotplug rwsem (so the CPUs don't change their state) and then check based on cpu_online() if we queue it on a specific CPU or not. queue_work_on() itself can handle if something is enqueued on an offline CPU but a timer which is enqueued on an offline CPU won't fire until the CPU is back online. Signed-off-by: Sebastian Andrzej Siewior --- kernel/rcu/srcutree.c | 22 ++++------------------ kernel/rcu/tree.c | 4 ---- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c index 1ff17e297f0c..df0375453ba1 100644 --- a/kernel/rcu/srcutree.c +++ b/kernel/rcu/srcutree.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "rcu.h" #include "rcu_segcblist.h" @@ -460,21 +461,6 @@ static void srcu_gp_start(struct srcu_struct *sp) WARN_ON_ONCE(state != SRCU_STATE_SCAN1); } -/* - * Track online CPUs to guide callback workqueue placement. - */ -DEFINE_PER_CPU(bool, srcu_online); - -void srcu_online_cpu(unsigned int cpu) -{ - WRITE_ONCE(per_cpu(srcu_online, cpu), true); -} - -void srcu_offline_cpu(unsigned int cpu) -{ - WRITE_ONCE(per_cpu(srcu_online, cpu), false); -} - /* * Place the workqueue handler on the specified CPU if online, otherwise * just run it whereever. This is useful for placing workqueue handlers @@ -486,12 +472,12 @@ static bool srcu_queue_delayed_work_on(int cpu, struct workqueue_struct *wq, { bool ret; - preempt_disable(); - if (READ_ONCE(per_cpu(srcu_online, cpu))) + cpus_read_lock(); + if (cpu_online(cpu)) ret = queue_delayed_work_on(cpu, wq, dwork, delay); else ret = queue_delayed_work(wq, dwork, delay); - preempt_enable(); + cpus_read_unlock(); return ret; } diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index ae716ca783bc..f162a4f54b05 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -3784,8 +3784,6 @@ int rcutree_online_cpu(unsigned int cpu) rnp->ffmask |= rdp->grpmask; raw_spin_unlock_irqrestore_rcu_node(rnp, flags); } - if (IS_ENABLED(CONFIG_TREE_SRCU)) - srcu_online_cpu(cpu); if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE) return 0; /* Too early in boot for scheduler work. */ sync_sched_exp_online_cleanup(cpu); @@ -3813,8 +3811,6 @@ int rcutree_offline_cpu(unsigned int cpu) } rcutree_affinity_setting(cpu, cpu); - if (IS_ENABLED(CONFIG_TREE_SRCU)) - srcu_offline_cpu(cpu); return 0; }