rcu: Protect rcu_node accesses during CPU stall warnings
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / kernel / rcutree.c
index f280e542e3e9f531df83b03d1e1fce6cf8ebaaa2..2cf8eb3e2d435828afb1e149606aa8277efee947 100644 (file)
@@ -305,7 +305,9 @@ cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp)
 static int
 cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp)
 {
-       return *rdp->nxttail[RCU_DONE_TAIL] && !rcu_gp_in_progress(rsp);
+       return *rdp->nxttail[RCU_DONE_TAIL +
+                            ACCESS_ONCE(rsp->completed) != rdp->completed] &&
+              !rcu_gp_in_progress(rsp);
 }
 
 /*
@@ -316,35 +318,6 @@ static struct rcu_node *rcu_get_root(struct rcu_state *rsp)
        return &rsp->node[0];
 }
 
-/*
- * If the specified CPU is offline, tell the caller that it is in
- * a quiescent state.  Otherwise, whack it with a reschedule IPI.
- * Grace periods can end up waiting on an offline CPU when that
- * CPU is in the process of coming online -- it will be added to the
- * rcu_node bitmasks before it actually makes it online.  The same thing
- * can happen while a CPU is in the process of coming online.  Because this
- * race is quite rare, we check for it after detecting that the grace
- * period has been delayed rather than checking each and every CPU
- * each and every time we start a new grace period.
- */
-static int rcu_implicit_offline_qs(struct rcu_data *rdp)
-{
-       /*
-        * If the CPU is offline for more than a jiffy, it is in a quiescent
-        * state.  We can trust its state not to change because interrupts
-        * are disabled.  The reason for the jiffy's worth of slack is to
-        * handle CPUs initializing on the way up and finding their way
-        * to the idle loop on the way down.
-        */
-       if (cpu_is_offline(rdp->cpu) &&
-           ULONG_CMP_LT(rdp->rsp->gp_start + 2, jiffies)) {
-               trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, "ofl");
-               rdp->offline_fqs++;
-               return 1;
-       }
-       return 0;
-}
-
 /*
  * rcu_idle_enter_common - inform RCU that current CPU is moving towards idle
  *
@@ -673,7 +646,7 @@ static int dyntick_save_progress_counter(struct rcu_data *rdp)
  * Return true if the specified CPU has passed through a quiescent
  * state by virtue of being in or having passed through an dynticks
  * idle state since the last call to dyntick_save_progress_counter()
- * for this same CPU.
+ * for this same CPU, or by virtue of having been offline.
  */
 static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
 {
@@ -697,8 +670,26 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
                return 1;
        }
 
-       /* Go check for the CPU being offline. */
-       return rcu_implicit_offline_qs(rdp);
+       /*
+        * Check for the CPU being offline, but only if the grace period
+        * is old enough.  We don't need to worry about the CPU changing
+        * state: If we see it offline even once, it has been through a
+        * quiescent state.
+        *
+        * The reason for insisting that the grace period be at least
+        * one jiffy old is that CPUs that are not quite online and that
+        * have just gone offline can still execute RCU read-side critical
+        * sections.
+        */
+       if (ULONG_CMP_GE(rdp->rsp->gp_start + 2, jiffies))
+               return 0;  /* Grace period is not old enough. */
+       barrier();
+       if (cpu_is_offline(rdp->cpu)) {
+               trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, "ofl");
+               rdp->offline_fqs++;
+               return 1;
+       }
+       return 0;
 }
 
 static int jiffies_till_stall_check(void)
@@ -755,14 +746,15 @@ static void print_other_cpu_stall(struct rcu_state *rsp)
        rcu_for_each_leaf_node(rsp, rnp) {
                raw_spin_lock_irqsave(&rnp->lock, flags);
                ndetected += rcu_print_task_stall(rnp);
+               if (rnp->qsmask != 0) {
+                       for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++)
+                               if (rnp->qsmask & (1UL << cpu)) {
+                                       print_cpu_stall_info(rsp,
+                                                            rnp->grplo + cpu);
+                                       ndetected++;
+                               }
+               }
                raw_spin_unlock_irqrestore(&rnp->lock, flags);
-               if (rnp->qsmask == 0)
-                       continue;
-               for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++)
-                       if (rnp->qsmask & (1UL << cpu)) {
-                               print_cpu_stall_info(rsp, rnp->grplo + cpu);
-                               ndetected++;
-                       }
        }
 
        /*