rcu: Apply RCU-bh QSes to RCU-sched and RCU-preempt when safe
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Thu, 28 Jun 2018 21:45:25 +0000 (14:45 -0700)
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Thu, 30 Aug 2018 23:02:38 +0000 (16:02 -0700)
One necessary step towards consolidating the three flavors of RCU is to
make sure that the resulting consolidated "one flavor to rule them all"
correctly handles networking denial-of-service attacks.  One thing that
allows RCU-bh to do so is that __do_softirq() invokes rcu_bh_qs() every
so often, and so something similar has to happen for consolidated RCU.

This must be done carefully.  For example, if a preemption-disabled
region of code takes an interrupt which does softirq processing before
returning, consolidated RCU must ignore the resulting rcu_bh_qs()
invocations -- preemption is still disabled, and that means an RCU
reader for the consolidated flavor.

This commit therefore creates a new rcu_softirq_qs() that is called only
from the ksoftirqd task, thus avoiding the interrupted-a-preempted-region
problem.  This new rcu_softirq_qs() function invokes rcu_sched_qs(),
rcu_preempt_qs(), and rcu_preempt_deferred_qs().  The latter call handles
any deferred quiescent states.

Note that __do_softirq() still invokes rcu_bh_qs().  It will continue to
do so until a later stage of cleanup when the RCU-bh flavor is removed.

Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
[ paulmck: Fix !SMP issue located by kbuild test robot. ]

include/linux/rcutiny.h
include/linux/rcutree.h
kernel/rcu/tree.c
kernel/rcu/tree.h
kernel/rcu/tree_plugin.h
kernel/softirq.c

index f617ab19bb517ea5fbaf5f65696a2de6c98bdd6b..bcfbc40a7239bdbf98745459c30bc94b9079a68e 100644 (file)
@@ -90,6 +90,11 @@ static inline void kfree_call_rcu(struct rcu_head *head,
        call_rcu(head, func);
 }
 
+static inline void rcu_softirq_qs(void)
+{
+       rcu_sched_qs();
+}
+
 #define rcu_note_context_switch(preempt) \
        do { \
                rcu_sched_qs(); \
index 914655848ef6a940bd343799e9f350fc7b9a8639..664b580695d647d6b79fb7828dbabb0d82b52ee9 100644 (file)
@@ -30,6 +30,7 @@
 #ifndef __LINUX_RCUTREE_H
 #define __LINUX_RCUTREE_H
 
+void rcu_softirq_qs(void);
 void rcu_note_context_switch(bool preempt);
 int rcu_needs_cpu(u64 basem, u64 *nextevt);
 void rcu_cpu_stall_reset(void);
index 0b42249e2e407c851aec529cd2ebced04245e1e5..cb35a417d94766eeb9218e780358ebb17be214a4 100644 (file)
@@ -255,6 +255,13 @@ void rcu_bh_qs(void)
        }
 }
 
+void rcu_softirq_qs(void)
+{
+       rcu_sched_qs();
+       rcu_preempt_qs();
+       rcu_preempt_deferred_qs(current);
+}
+
 /*
  * Steal a bit from the bottom of ->dynticks for idle entry/exit
  * control.  Initially this is for TLB flushing.
index 025bd2e5592bf903e61b052e8b5d689057d94240..e02c882861eb36850980896d35cf63e4b39dfd39 100644 (file)
@@ -433,6 +433,7 @@ DECLARE_PER_CPU(char, rcu_cpu_has_work);
 
 /* Forward declarations for rcutree_plugin.h */
 static void rcu_bootup_announce(void);
+static void rcu_preempt_qs(void);
 static void rcu_preempt_note_context_switch(bool preempt);
 static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp);
 #ifdef CONFIG_HOTPLUG_CPU
index 527a52792dce67165b65e9e0f499d38b38a50434..c686bf63bba53a81610e2dbedd1d3767f93c6619 100644 (file)
@@ -974,6 +974,11 @@ static void __init rcu_bootup_announce(void)
        rcu_bootup_announce_oddness();
 }
 
+/* Because preemptible RCU does not exist, we can ignore its QSes. */
+static void rcu_preempt_qs(void)
+{
+}
+
 /*
  * Because preemptible RCU does not exist, we never have to check for
  * CPUs being in quiescent states.
index 6f584861d329bfb0382a5691320938cf3db7009b..ebd69694144a9327bbea5b7ac688b94f7b969f7b 100644 (file)
@@ -302,6 +302,8 @@ restart:
        }
 
        rcu_bh_qs();
+       if (__this_cpu_read(ksoftirqd) == current)
+               rcu_softirq_qs();
        local_irq_disable();
 
        pending = local_softirq_pending();