__entry->oldnesting, __entry->newnesting)
 );
 
+/*
+ * Tracepoint for RCU preparation for idle, the goal being to get RCU
+ * processing done so that the current CPU can shut off its scheduling
+ * clock and enter dyntick-idle mode.  One way to accomplish this is
+ * to drain all RCU callbacks from this CPU, and the other is to have
+ * done everything RCU requires for the current grace period.  In this
+ * latter case, the CPU will be awakened at the end of the current grace
+ * period in order to process the remainder of its callbacks.
+ *
+ * These tracepoints take a string as argument:
+ *
+ *     "No callbacks": Nothing to do, no callbacks on this CPU.
+ *     "In holdoff": Nothing to do, holding off after unsuccessful attempt.
+ *     "Dyntick with callbacks": Callbacks remain, but RCU doesn't need CPU.
+ *     "Begin holdoff": Attempt failed, don't retry until next jiffy.
+ *     "More callbacks": Still more callbacks, try again to clear them out.
+ *     "Callbacks drained": All callbacks processed, off to dyntick idle!
+ *     "CPU awakened at GP end":
+ */
+TRACE_EVENT(rcu_prep_idle,
+
+       TP_PROTO(char *reason),
+
+       TP_ARGS(reason),
+
+       TP_STRUCT__entry(
+               __field(char *, reason)
+       ),
+
+       TP_fast_assign(
+               __entry->reason = reason;
+       ),
+
+       TP_printk("%s", __entry->reason)
+);
+
 /*
  * Tracepoint for the registration of a single RCU callback function.
  * The first argument is the type of RCU, the second argument is
 #define trace_rcu_quiescent_state_report(rcuname, gpnum, mask, qsmask, level, grplo, grphi, gp_tasks) do { } while (0)
 #define trace_rcu_fqs(rcuname, gpnum, cpu, qsevent) do { } while (0)
 #define trace_rcu_dyntick(polarity, oldnesting, newnesting) do { } while (0)
+#define trace_rcu_prep_idle(reason) do { } while (0)
 #define trace_rcu_callback(rcuname, rhp, qlen) do { } while (0)
 #define trace_rcu_kfree_callback(rcuname, rhp, offset, qlen) do { } while (0)
 #define trace_rcu_batch_start(rcuname, qlen, blimit) do { } while (0)
 
        /* If no callbacks or in the holdoff period, enter dyntick-idle. */
        if (!rcu_cpu_has_callbacks(cpu)) {
                per_cpu(rcu_dyntick_holdoff, cpu) = jiffies - 1;
+               trace_rcu_prep_idle("No callbacks");
                return;
        }
-       if (per_cpu(rcu_dyntick_holdoff, cpu) == jiffies)
+       if (per_cpu(rcu_dyntick_holdoff, cpu) == jiffies) {
+               trace_rcu_prep_idle("In holdoff");
                return;
+       }
 
        /* Check and update the rcu_dyntick_drain sequencing. */
        if (per_cpu(rcu_dyntick_drain, cpu) <= 0) {
                /* We have hit the limit, so time to give up. */
                per_cpu(rcu_dyntick_holdoff, cpu) = jiffies;
                if (!rcu_pending(cpu)) {
+                       trace_rcu_prep_idle("Dyntick with callbacks");
                        per_cpu(rcu_awake_at_gp_end, cpu) = 1;
                        return;  /* Nothing to do immediately. */
                }
+               trace_rcu_prep_idle("Begin holdoff");
                invoke_rcu_core();  /* Force the CPU out of dyntick-idle. */
                return;
        }
                c = c || per_cpu(rcu_bh_data, cpu).nxtlist;
        }
 
-       /* If RCU callbacks are still pending, RCU still needs this CPU. */
-       if (c)
+       /*
+        * If RCU callbacks are still pending, RCU still needs this CPU.
+        * So try forcing the callbacks through the grace period.
+        */
+       if (c) {
+               trace_rcu_prep_idle("More callbacks");
                invoke_rcu_core();
+       } else
+               trace_rcu_prep_idle("Callbacks drained");
 }
 
 /*
  */
 static void rcu_wake_cpu(void *unused)
 {
+       trace_rcu_prep_idle("CPU awakened at GP end");
        invoke_rcu_core();
 }