diff options
author | Peter Zijlstra <peterz@infradead.org> | 2013-10-07 11:12:48 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-10-29 09:13:01 -0400 |
commit | 5a3126d4fe7c311fe12f98fef0470f6cb582d1ef (patch) | |
tree | 597334d1312749439ddf8438ab85d296ef204990 /kernel/events/core.c | |
parent | e00b12e64be9a34ef071de7b6052ca9ea29dd460 (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.c | 64 |
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 | ||
904 | static u32 perf_event_pid(struct perf_event *event, struct task_struct *p) | 905 | static 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 | ||
1318 | static void perf_group_detach(struct perf_event *event) | 1323 | static 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 | */ |
2162 | static int context_equiv(struct perf_event_context *ctx1, | 2164 | static 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 | ||
2170 | static void __perf_event_sync_stat(struct perf_event *event, | 2191 | static 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 | } |
2322 | unlock: | ||
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 | ||