rcu: Break more call_rcu() deadlock involving scheduler and perf
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Tue, 29 Jul 2014 21:50:47 +0000 (14:50 -0700)
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Sun, 7 Sep 2014 23:18:17 +0000 (16:18 -0700)
Commit 96d3fd0d315a9 (rcu: Break call_rcu() deadlock involving scheduler
and perf) covered the case where __call_rcu_nocb_enqueue() needs to wake
the rcuo kthread due to the queue being initially empty, but did not
do anything for the case where the queue was overflowing.  This commit
therefore also defers wakeup for the overflow case.

Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
include/trace/events/rcu.h
kernel/rcu/tree.h
kernel/rcu/tree_plugin.h

index aca382266411620f6bd7b052311cee6d6b04e8e7..9b56f37148cfd91d57b849f88c8b1279220aa41a 100644 (file)
@@ -180,9 +180,12 @@ TRACE_EVENT(rcu_grace_period_init,
  * argument is a string as follows:
  *
  *     "WakeEmpty": Wake rcuo kthread, first CB to empty list.
+ *     "WakeEmptyIsDeferred": Wake rcuo kthread later, first CB to empty list.
  *     "WakeOvf": Wake rcuo kthread, CB list is huge.
+ *     "WakeOvfIsDeferred": Wake rcuo kthread later, CB list is huge.
  *     "WakeNot": Don't wake rcuo kthread.
  *     "WakeNotPoll": Don't wake rcuo kthread because it is polling.
+ *     "DeferredWake": Carried out the "IsDeferred" wakeup.
  *     "Poll": Start of new polling cycle for rcu_nocb_poll.
  *     "Sleep": Sleep waiting for CBs for !rcu_nocb_poll.
  *     "WokeEmpty": rcuo kthread woke to find empty list.
index 6a86eb7bac45e7b540f1ab9ead7d412cf6ef60fa..e33562f2a655cab0d50bda67564de65c03bd615f 100644 (file)
@@ -350,7 +350,7 @@ struct rcu_data {
        int nocb_p_count_lazy;          /*  (approximate). */
        wait_queue_head_t nocb_wq;      /* For nocb kthreads to sleep on. */
        struct task_struct *nocb_kthread;
-       bool nocb_defer_wakeup;         /* Defer wakeup of nocb_kthread. */
+       int nocb_defer_wakeup;          /* Defer wakeup of nocb_kthread. */
 
        /* The following fields are used by the leader, hence own cacheline. */
        struct rcu_head *nocb_gp_head ____cacheline_internodealigned_in_smp;
@@ -383,6 +383,11 @@ struct rcu_data {
 #define RCU_FORCE_QS           3       /* Need to force quiescent state. */
 #define RCU_SIGNAL_INIT                RCU_SAVE_DYNTICK
 
+/* Values for nocb_defer_wakeup field in struct rcu_data. */
+#define RCU_NOGP_WAKE_NOT      0
+#define RCU_NOGP_WAKE          1
+#define RCU_NOGP_WAKE_FORCE    2
+
 #define RCU_JIFFIES_TILL_FORCE_QS (1 + (HZ > 250) + (HZ > 500))
                                        /* For jiffies_till_first_fqs and */
                                        /*  and jiffies_till_next_fqs. */
@@ -589,7 +594,7 @@ static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp,
 static bool rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp,
                                      struct rcu_data *rdp,
                                      unsigned long flags);
-static bool rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp);
+static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp);
 static void do_nocb_deferred_wakeup(struct rcu_data *rdp);
 static void rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp);
 static void rcu_spawn_nocb_kthreads(struct rcu_state *rsp);
index bb564560aeb8657d9aaa38025523a0ba8cc0e166..d67cc5c375c581179ebdfaa14208190975c87b8f 100644 (file)
@@ -2121,16 +2121,23 @@ static void __call_rcu_nocb_enqueue(struct rcu_data *rdp,
                        trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
                                            TPS("WakeEmpty"));
                } else {
-                       rdp->nocb_defer_wakeup = true;
+                       rdp->nocb_defer_wakeup = RCU_NOGP_WAKE;
                        trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
                                            TPS("WakeEmptyIsDeferred"));
                }
                rdp->qlen_last_fqs_check = 0;
        } else if (len > rdp->qlen_last_fqs_check + qhimark) {
                /* ... or if many callbacks queued. */
-               wake_nocb_leader(rdp, true);
+               if (!irqs_disabled_flags(flags)) {
+                       wake_nocb_leader(rdp, true);
+                       trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
+                                           TPS("WakeOvf"));
+               } else {
+                       rdp->nocb_defer_wakeup = RCU_NOGP_WAKE_FORCE;
+                       trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
+                                           TPS("WakeOvfIsDeferred"));
+               }
                rdp->qlen_last_fqs_check = LONG_MAX / 2;
-               trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeOvf"));
        } else {
                trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeNot"));
        }
@@ -2438,7 +2445,7 @@ static int rcu_nocb_kthread(void *arg)
 }
 
 /* Is a deferred wakeup of rcu_nocb_kthread() required? */
-static bool rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)
+static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)
 {
        return ACCESS_ONCE(rdp->nocb_defer_wakeup);
 }
@@ -2446,11 +2453,14 @@ static bool rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)
 /* Do a deferred wakeup of rcu_nocb_kthread(). */
 static void do_nocb_deferred_wakeup(struct rcu_data *rdp)
 {
+       int ndw;
+
        if (!rcu_nocb_need_deferred_wakeup(rdp))
                return;
-       ACCESS_ONCE(rdp->nocb_defer_wakeup) = false;
-       wake_nocb_leader(rdp, false);
-       trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("DeferredWakeEmpty"));
+       ndw = ACCESS_ONCE(rdp->nocb_defer_wakeup);
+       ACCESS_ONCE(rdp->nocb_defer_wakeup) = RCU_NOGP_WAKE_NOT;
+       wake_nocb_leader(rdp, ndw == RCU_NOGP_WAKE_FORCE);
+       trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("DeferredWake"));
 }
 
 /* Initialize per-rcu_data variables for no-CBs CPUs. */
@@ -2557,7 +2567,7 @@ static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp)
 {
 }
 
-static bool rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)
+static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)
 {
        return false;
 }