aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/events/core.c
diff options
context:
space:
mode:
authorPeter Zijlstra <peterz@infradead.org>2013-10-07 11:12:48 -0400
committerIngo Molnar <mingo@kernel.org>2013-10-29 09:13:01 -0400
commit5a3126d4fe7c311fe12f98fef0470f6cb582d1ef (patch)
tree597334d1312749439ddf8438ab85d296ef204990 /kernel/events/core.c
parente00b12e64be9a34ef071de7b6052ca9ea29dd460 (diff)
perf: Fix the perf context switch optimization
Currently we only optimize the context switch between two contexts that have the same parent; this forgoes the optimization between parent and child context, even though these contexts could be equivalent too. Signed-off-by: Peter Zijlstra <peterz@infradead.org> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Shishkin, Alexander <alexander.shishkin@intel.com> Link: http://lkml.kernel.org/r/20131007164257.GH3081@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/events/core.c')
-rw-r--r--kernel/events/core.c64
1 files changed, 46 insertions, 18 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 85a8bbde6481..17b3c6cf1606 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -899,6 +899,7 @@ static void unclone_ctx(struct perf_event_context *ctx)
899 put_ctx(ctx->parent_ctx); 899 put_ctx(ctx->parent_ctx);
900 ctx->parent_ctx = NULL; 900 ctx->parent_ctx = NULL;
901 } 901 }
902 ctx->generation++;
902} 903}
903 904
904static u32 perf_event_pid(struct perf_event *event, struct task_struct *p) 905static u32 perf_event_pid(struct perf_event *event, struct task_struct *p)
@@ -1136,6 +1137,8 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx)
1136 ctx->nr_events++; 1137 ctx->nr_events++;
1137 if (event->attr.inherit_stat) 1138 if (event->attr.inherit_stat)
1138 ctx->nr_stat++; 1139 ctx->nr_stat++;
1140
1141 ctx->generation++;
1139} 1142}
1140 1143
1141/* 1144/*
@@ -1313,6 +1316,8 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx)
1313 */ 1316 */
1314 if (event->state > PERF_EVENT_STATE_OFF) 1317 if (event->state > PERF_EVENT_STATE_OFF)
1315 event->state = PERF_EVENT_STATE_OFF; 1318 event->state = PERF_EVENT_STATE_OFF;
1319
1320 ctx->generation++;
1316} 1321}
1317 1322
1318static void perf_group_detach(struct perf_event *event) 1323static void perf_group_detach(struct perf_event *event)
@@ -2149,22 +2154,38 @@ static void ctx_sched_out(struct perf_event_context *ctx,
2149} 2154}
2150 2155
2151/* 2156/*
2152 * Test whether two contexts are equivalent, i.e. whether they 2157 * Test whether two contexts are equivalent, i.e. whether they have both been
2153 * have both been cloned from the same version of the same context 2158 * cloned from the same version of the same context.
2154 * and they both have the same number of enabled events. 2159 *
2155 * If the number of enabled events is the same, then the set 2160 * Equivalence is measured using a generation number in the context that is
2156 * of enabled events should be the same, because these are both 2161 * incremented on each modification to it; see unclone_ctx(), list_add_event()
2157 * inherited contexts, therefore we can't access individual events 2162 * and list_del_event().
2158 * in them directly with an fd; we can only enable/disable all
2159 * events via prctl, or enable/disable all events in a family
2160 * via ioctl, which will have the same effect on both contexts.
2161 */ 2163 */
2162static int context_equiv(struct perf_event_context *ctx1, 2164static int context_equiv(struct perf_event_context *ctx1,
2163 struct perf_event_context *ctx2) 2165 struct perf_event_context *ctx2)
2164{ 2166{
2165 return ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx 2167 /* Pinning disables the swap optimization */
2166 && ctx1->parent_gen == ctx2->parent_gen 2168 if (ctx1->pin_count || ctx2->pin_count)
2167 && !ctx1->pin_count && !ctx2->pin_count; 2169 return 0;
2170
2171 /* If ctx1 is the parent of ctx2 */
2172 if (ctx1 == ctx2->parent_ctx && ctx1->generation == ctx2->parent_gen)
2173 return 1;
2174
2175 /* If ctx2 is the parent of ctx1 */
2176 if (ctx1->parent_ctx == ctx2 && ctx1->parent_gen == ctx2->generation)
2177 return 1;
2178
2179 /*
2180 * If ctx1 and ctx2 have the same parent; we flatten the parent
2181 * hierarchy, see perf_event_init_context().
2182 */
2183 if (ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx &&
2184 ctx1->parent_gen == ctx2->parent_gen)
2185 return 1;
2186
2187 /* Unmatched */
2188 return 0;
2168} 2189}
2169 2190
2170static void __perf_event_sync_stat(struct perf_event *event, 2191static void __perf_event_sync_stat(struct perf_event *event,
@@ -2247,7 +2268,7 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn,
2247{ 2268{
2248 struct perf_event_context *ctx = task->perf_event_ctxp[ctxn]; 2269 struct perf_event_context *ctx = task->perf_event_ctxp[ctxn];
2249 struct perf_event_context *next_ctx; 2270 struct perf_event_context *next_ctx;
2250 struct perf_event_context *parent; 2271 struct perf_event_context *parent, *next_parent;
2251 struct perf_cpu_context *cpuctx; 2272 struct perf_cpu_context *cpuctx;
2252 int do_switch = 1; 2273 int do_switch = 1;
2253 2274
@@ -2259,10 +2280,18 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn,
2259 return; 2280 return;
2260 2281
2261 rcu_read_lock(); 2282 rcu_read_lock();
2262 parent = rcu_dereference(ctx->parent_ctx);
2263 next_ctx = next->perf_event_ctxp[ctxn]; 2283 next_ctx = next->perf_event_ctxp[ctxn];
2264 if (parent && next_ctx && 2284 if (!next_ctx)
2265 rcu_dereference(next_ctx->parent_ctx) == parent) { 2285 goto unlock;
2286
2287 parent = rcu_dereference(ctx->parent_ctx);
2288 next_parent = rcu_dereference(next_ctx->parent_ctx);
2289
2290 /* If neither context have a parent context; they cannot be clones. */
2291 if (!parent && !next_parent)
2292 goto unlock;
2293
2294 if (next_parent == ctx || next_ctx == parent || next_parent == parent) {
2266 /* 2295 /*
2267 * Looks like the two contexts are clones, so we might be 2296 * Looks like the two contexts are clones, so we might be
2268 * able to optimize the context switch. We lock both 2297 * able to optimize the context switch. We lock both
@@ -2290,6 +2319,7 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn,
2290 raw_spin_unlock(&next_ctx->lock); 2319 raw_spin_unlock(&next_ctx->lock);
2291 raw_spin_unlock(&ctx->lock); 2320 raw_spin_unlock(&ctx->lock);
2292 } 2321 }
2322unlock:
2293 rcu_read_unlock(); 2323 rcu_read_unlock();
2294 2324
2295 if (do_switch) { 2325 if (do_switch) {
@@ -7136,7 +7166,6 @@ SYSCALL_DEFINE5(perf_event_open,
7136 } 7166 }
7137 7167
7138 perf_install_in_context(ctx, event, event->cpu); 7168 perf_install_in_context(ctx, event, event->cpu);
7139 ++ctx->generation;
7140 perf_unpin_context(ctx); 7169 perf_unpin_context(ctx);
7141 mutex_unlock(&ctx->mutex); 7170 mutex_unlock(&ctx->mutex);
7142 7171
@@ -7219,7 +7248,6 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
7219 WARN_ON_ONCE(ctx->parent_ctx); 7248 WARN_ON_ONCE(ctx->parent_ctx);
7220 mutex_lock(&ctx->mutex); 7249 mutex_lock(&ctx->mutex);
7221 perf_install_in_context(ctx, event, cpu); 7250 perf_install_in_context(ctx, event, cpu);
7222 ++ctx->generation;
7223 perf_unpin_context(ctx); 7251 perf_unpin_context(ctx);
7224 mutex_unlock(&ctx->mutex); 7252 mutex_unlock(&ctx->mutex);
7225 7253