]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/rcu/tree_plugin.h
Merge branch 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / kernel / rcu / tree_plugin.h
index 506a7a97a2e2c37f9c670b842a062182b23a8db8..6e2ef4b2b920bc3db08ececbc37d74cf83b8dc14 100644 (file)
@@ -363,10 +363,14 @@ void rcu_read_unlock_special(struct task_struct *t)
        special = t->rcu_read_unlock_special;
        if (special & RCU_READ_UNLOCK_NEED_QS) {
                rcu_preempt_qs(smp_processor_id());
+               if (!t->rcu_read_unlock_special) {
+                       local_irq_restore(flags);
+                       return;
+               }
        }
 
-       /* Hardware IRQ handlers cannot block. */
-       if (in_irq() || in_serving_softirq()) {
+       /* Hardware IRQ handlers cannot block, complain if they get here. */
+       if (WARN_ON_ONCE(in_irq() || in_serving_softirq())) {
                local_irq_restore(flags);
                return;
        }
@@ -785,8 +789,10 @@ static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp,
                }
                if (rnp->parent == NULL) {
                        raw_spin_unlock_irqrestore(&rnp->lock, flags);
-                       if (wake)
+                       if (wake) {
+                               smp_mb(); /* EGP done before wake_up(). */
                                wake_up(&sync_rcu_preempt_exp_wq);
+                       }
                        break;
                }
                mask = rnp->grpmask;
@@ -1864,6 +1870,7 @@ static int rcu_oom_notify(struct notifier_block *self,
 
        /* Wait for callbacks from earlier instance to complete. */
        wait_event(oom_callback_wq, atomic_read(&oom_callback_count) == 0);
+       smp_mb(); /* Ensure callback reuse happens after callback invocation. */
 
        /*
         * Prevent premature wakeup: ensure that all increments happen
@@ -2113,7 +2120,8 @@ bool rcu_is_nocb_cpu(int cpu)
 static void __call_rcu_nocb_enqueue(struct rcu_data *rdp,
                                    struct rcu_head *rhp,
                                    struct rcu_head **rhtp,
-                                   int rhcount, int rhcount_lazy)
+                                   int rhcount, int rhcount_lazy,
+                                   unsigned long flags)
 {
        int len;
        struct rcu_head **old_rhpp;
@@ -2134,9 +2142,16 @@ static void __call_rcu_nocb_enqueue(struct rcu_data *rdp,
        }
        len = atomic_long_read(&rdp->nocb_q_count);
        if (old_rhpp == &rdp->nocb_head) {
-               wake_up(&rdp->nocb_wq); /* ... only if queue was empty ... */
+               if (!irqs_disabled_flags(flags)) {
+                       wake_up(&rdp->nocb_wq); /* ... if queue was empty ... */
+                       trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
+                                           TPS("WakeEmpty"));
+               } else {
+                       rdp->nocb_defer_wakeup = true;
+                       trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
+                                           TPS("WakeEmptyIsDeferred"));
+               }
                rdp->qlen_last_fqs_check = 0;
-               trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeEmpty"));
        } else if (len > rdp->qlen_last_fqs_check + qhimark) {
                wake_up_process(t); /* ... or if many callbacks queued. */
                rdp->qlen_last_fqs_check = LONG_MAX / 2;
@@ -2157,12 +2172,12 @@ static void __call_rcu_nocb_enqueue(struct rcu_data *rdp,
  * "rcuo" kthread can find it.
  */
 static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp,
-                           bool lazy)
+                           bool lazy, unsigned long flags)
 {
 
        if (!rcu_is_nocb_cpu(rdp->cpu))
                return 0;
-       __call_rcu_nocb_enqueue(rdp, rhp, &rhp->next, 1, lazy);
+       __call_rcu_nocb_enqueue(rdp, rhp, &rhp->next, 1, lazy, flags);
        if (__is_kfree_rcu_offset((unsigned long)rhp->func))
                trace_rcu_kfree_callback(rdp->rsp->name, rhp,
                                         (unsigned long)rhp->func,
@@ -2180,7 +2195,8 @@ static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp,
  * not a no-CBs CPU.
  */
 static bool __maybe_unused rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp,
-                                                    struct rcu_data *rdp)
+                                                    struct rcu_data *rdp,
+                                                    unsigned long flags)
 {
        long ql = rsp->qlen;
        long qll = rsp->qlen_lazy;
@@ -2194,14 +2210,14 @@ static bool __maybe_unused rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp,
        /* First, enqueue the donelist, if any.  This preserves CB ordering. */
        if (rsp->orphan_donelist != NULL) {
                __call_rcu_nocb_enqueue(rdp, rsp->orphan_donelist,
-                                       rsp->orphan_donetail, ql, qll);
+                                       rsp->orphan_donetail, ql, qll, flags);
                ql = qll = 0;
                rsp->orphan_donelist = NULL;
                rsp->orphan_donetail = &rsp->orphan_donelist;
        }
        if (rsp->orphan_nxtlist != NULL) {
                __call_rcu_nocb_enqueue(rdp, rsp->orphan_nxtlist,
-                                       rsp->orphan_nxttail, ql, qll);
+                                       rsp->orphan_nxttail, ql, qll, flags);
                ql = qll = 0;
                rsp->orphan_nxtlist = NULL;
                rsp->orphan_nxttail = &rsp->orphan_nxtlist;
@@ -2263,6 +2279,7 @@ static int rcu_nocb_kthread(void *arg)
                        trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
                                            TPS("Sleep"));
                        wait_event_interruptible(rdp->nocb_wq, rdp->nocb_head);
+                       /* Memory barrier provide by xchg() below. */
                } else if (firsttime) {
                        firsttime = 0;
                        trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
@@ -2323,6 +2340,22 @@ static int rcu_nocb_kthread(void *arg)
        return 0;
 }
 
+/* Is a deferred wakeup of rcu_nocb_kthread() required? */
+static bool rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)
+{
+       return ACCESS_ONCE(rdp->nocb_defer_wakeup);
+}
+
+/* Do a deferred wakeup of rcu_nocb_kthread(). */
+static void do_nocb_deferred_wakeup(struct rcu_data *rdp)
+{
+       if (!rcu_nocb_need_deferred_wakeup(rdp))
+               return;
+       ACCESS_ONCE(rdp->nocb_defer_wakeup) = false;
+       wake_up(&rdp->nocb_wq);
+       trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("DeferredWakeEmpty"));
+}
+
 /* Initialize per-rcu_data variables for no-CBs CPUs. */
 static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp)
 {
@@ -2378,13 +2411,14 @@ static void rcu_init_one_nocb(struct rcu_node *rnp)
 }
 
 static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp,
-                           bool lazy)
+                           bool lazy, unsigned long flags)
 {
        return 0;
 }
 
 static bool __maybe_unused rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp,
-                                                    struct rcu_data *rdp)
+                                                    struct rcu_data *rdp,
+                                                    unsigned long flags)
 {
        return 0;
 }
@@ -2393,6 +2427,15 @@ static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp)
 {
 }
 
+static bool rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)
+{
+       return false;
+}
+
+static void do_nocb_deferred_wakeup(struct rcu_data *rdp)
+{
+}
+
 static void __init rcu_spawn_nocb_kthreads(struct rcu_state *rsp)
 {
 }
@@ -2842,3 +2885,23 @@ static void rcu_sysidle_init_percpu_data(struct rcu_dynticks *rdtp)
 }
 
 #endif /* #else #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */
+
+/*
+ * Is this CPU a NO_HZ_FULL CPU that should ignore RCU so that the
+ * grace-period kthread will do force_quiescent_state() processing?
+ * The idea is to avoid waking up RCU core processing on such a
+ * CPU unless the grace period has extended for too long.
+ *
+ * This code relies on the fact that all NO_HZ_FULL CPUs are also
+ * CONFIG_RCU_NOCB_CPUs.
+ */
+static bool rcu_nohz_full_cpu(struct rcu_state *rsp)
+{
+#ifdef CONFIG_NO_HZ_FULL
+       if (tick_nohz_full_cpu(smp_processor_id()) &&
+           (!rcu_gp_in_progress(rsp) ||
+            ULONG_CMP_LT(jiffies, ACCESS_ONCE(rsp->gp_start) + HZ)))
+               return 1;
+#endif /* #ifdef CONFIG_NO_HZ_FULL */
+       return 0;
+}