diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index 7f6877a3505147a3b1094e50f816f3239b189621..debe453c96230e169804183df8ddf03bd9541ba7 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -274,6 +274,42 @@ TRACE_EVENT(rcu_dyntick, __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 @@ -482,6 +518,7 @@ TRACE_EVENT(rcu_torture_read, #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) diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index b70ca8cc52e17fc3d22940f546f7afb66debe394..6467f5669ab71c2b68996490ab0a2a2926cf7466 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -2031,10 +2031,13 @@ static void rcu_prepare_for_idle(int cpu) /* 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) { @@ -2044,9 +2047,11 @@ static void rcu_prepare_for_idle(int cpu) /* 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; } @@ -2073,9 +2078,15 @@ static void rcu_prepare_for_idle(int cpu) 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"); } /* @@ -2085,6 +2096,7 @@ static void rcu_prepare_for_idle(int cpu) */ static void rcu_wake_cpu(void *unused) { + trace_rcu_prep_idle("CPU awakened at GP end"); invoke_rcu_core(); }